javainheritancedesign-patternsextensibility

Hide non mutual methods in superclass facade


Is there a way we can hide the non mutual methods for types which are not qualified for the specific methods?

Lets say we have an abstract superclass with methods we don't want to expose to the objects themselves. We create a facade with the methods we want to allow for the objects. (We dont want to be able to set a cats age to 32 out of the blue.)

I end up in a scenario where i expose methods for a specific subclass that was actually meant for another subclass. (Even if we in the method can controll that the type is correct)

Scenario:

public abstract class Animal {

    //setters and code we want to protect
}

public class Cat extends Animal{

    private boolean giveBirth;

    public void giveBirth(){giveBirth = true;}

    //setters etc


}

public class Bird extends Animal{

    private boolean layEgg;

    public void layEgg(){layEgg = true;}

    //setters etc

}

public class FacadeAnimal {

    Animal animal;

    public FacadeAnimal(Animal a){
        animal = a;
    }

    public void layEgg(){
        if(animal instanceof Bird){
            ((Bird) animal).layEgg();
        }
    }

    public void giveBirth(){
        if(animal instanceof Cat){
            ((Cat) animal).giveBirth();
        }
    }

}

So in this case we can control inside the method that the type needs to be Bird if we want to layEgg. But it would also be okay for us to let a Cat layEgg. Even though the logic is taken care of, it's still not very intuitional to give Cat the option to lay eggs.

I was thinking it's possible to create an "facade inheritance structure", where we for every subclass also create a facade for that specific subclass. But in the terms of extensibility, that means we will force future developers to not only create a subclass, but also an implementation of it's facade.

Is this the way to go or can we change the way we wrap this around?

Cheers!

EDIT: Maybe the animalscenario was not very clear.

It could in the same manner be two different cars, where one has turbo and one has not, if the "activateTurbo" method exists in the facade, i would be able to call the activateTurbo method on a car that does not actually have a turbo.


Solution

  • Method names such as giveBirth() and layEgg() are too specific - consider something more common such as:

    public abstract class Animal {
        public void reproduce();
    }
    

    Then each subclass can implement as needed. For examle:

    public class Cat extends Animal {
        public void reproduce() {
           liveBirth();
       }
    
       private void liveBirth() {
           // ...
       }
    }
    

    and

    public class Bird extends Animal {
        public void reproduce() {
           layEgg();
       }
    
       private void layEgg() {
           // ...
       }
    }
    

    This approach will likely lead to at least some duplicate code in the private methods. As @Lini said, combine with the strategy pattern. A little refactoring, and it changes from inheritance to composition.