Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking `instanceof` while iterating through a loop

I have class structure where

  • public abstract class AbstractBuilding implements some non-relevant interfaces for this question.
  • public abstract class AbstractAnimalBuilding extends AbstractBuiling
    And small number of classes following this structure:
  • public class AnimalBuildingA extends AbstractAnimalBuilding
  • public class AnimalBuildingB extends AbstractAnimalBuilding
  • public class AnimalBuildingC extends AbstractAnimalBuilding
  • public class AnimalBuildingD extends AbstractAnimalBuilding

In a totally separate class I have the following method:

@FXML
private Button btnAnimalBuildingA;
@FXML
private Button btnAnimalBuildingB;
@FXML
private Button btnAnimalBuildingC;
@FXML
private Button btnAnimalBuildingD;

for (AbstractAnimalBuilding animalBuilding: animalBuildings){
    if (animalBuilding instanceof AnimalBuildingA) {
        changeButtonDisplay(btnAnimalBuildingA)
    } else if (animalBuilding instanceof AnimalBuildingB){
        changeButtonDisplay(btnAnimalBuildingB)
    } else if (animalBuilding instanceof AnimalBuildingC) {
        changeButtonDisplay(btnAnimalBuildingC)
    } else if (animalBuilding instanceof AnimalBuildingD){
        changeButtonDisplay(btnAnimalBuildingD)
        //Do something specific here
    }
}

private void changeButtonDisplay(Button buttonToChange){
    button.setVisible(true);
}

Where animalBuildings is a Set<AbstractAnimalBuilding> containing any combination of AnimalBuildingX's.

Assuming the structure at the top needs to be kept (eg, AnimalBuildingX HAS to extend AbstractAnimalBuilding), what would be a better approach than the multiple if-then-else statements in determining what kind of building animalBuilding is?

Would it feasible to simply create a new Interface as outlined in this question and have each AnimalBuildingX implement it while still extending AbstractAnimalBuilding or is there a way I can do it using the structure I currently have.

like image 967
Dan Avatar asked Jan 07 '23 07:01

Dan


2 Answers

This is difficult to answer in general without more context.

One possibility is to create an abstract method in AbstractBuilding and implement it differently in the subclasses.

Another possibility is to use the visitor pattern.

like image 186
Henry Avatar answered Jan 14 '23 22:01

Henry


It depends on the action you want to take on behalf of the derived class type. If an action has to be taken which can be perfomed without the need, that the calling class knows the concrete implementation of AnimalBuilding the interface method is appropriate. This usually is the case if you can find a common method description which is implemented differently for each concrete class (e.g. getName()).

If you need to do specific actions dependent on the concrete class (e.g. AnimalBuildingA differs from AnimalBuldingB), you can implement the visitor pattern:

public abstract class AbstractAnimalBuilding {
    ...
    public abstract void accept(AnimalBuildingVisitor v);
}

public interface class AnimalBuildingVisitor<T> {

    public T visit(AnimalBuildingA a);
    public T visit(AnimalBuildingB b);
    ...
}

The implementation of the accept-method usually is the one liner

return v.visit(this);

Then you create an implementation of the Abstract visitor which does the work you want to perform in the loop. The loop then looks like this

ConcreteAnimalBuildingVisitor v;
for (AbstractAnimalBuilding animalBuilding: animalBuildings)
    animalBuilding.accept(v);

This way, the concrete class "identifies" itself to the concrete visior which then can perform the appropriate action.

like image 26
flo Avatar answered Jan 14 '23 23:01

flo