I have been looking for design ideas to solve this problem in Java.
I am using a library (I cannot change it) that for this example, I'll just call "Animals". It contains an Animal interface, and a bunch of implementations; and I need to call different methods according to the implementation of the Animal I get:
List<Animal> animals = Service.getAnimals();
for(Animal a : animals) {
process(a);
}
private void process(Animal animal) {
if (animal instanceOf Cat) {
processCat(animal);
} else if (animal instanceOf Dog) {
processDog(animal);
} else {
System.out.println("unsopported animal");
}
}
I am currently solving this through reflection, with a class that holds all "Processors" and calling them using
String methodName = "process" + Animal getClass().getSimpleName(); //ugh
I am using Java 8 and I am pretty sure there must be better design to address this issue.
Any help is appreciated!
No, you cannot instantiate an interface.
If you define a reference variable whose type is an interface, any object you assign to it must be an instance of a class that implements the interface. By casting object1 to a Relatable type, it can invoke the isLargerThan method.
The implements keyword is used to implement an interface . The interface keyword is used to declare a special type of class that only contains abstract methods. To access the interface methods, the interface must be "implemented" (kinda like inherited) by another class with the implements keyword (instead of extends ).
Your class can implement more than one interface, so the implements keyword is followed by a comma-separated list of the interfaces implemented by the class. By convention, the implements clause follows the extends clause, if there is one.
If Animal's is a sealed class, that is, it isn't dynamically extendable, and has a limited, known number of sub-classes, Then the if-instanceof pattern you have stumbled across in the example is classic "pattern matching".
If Animal was a class you could control, then you could use the Visitor Pattern to create a visit method directly on Animal.
However you state that Animal is from an external library, which limits the approach you can take.
You can still use the Visitor pattern, keeping all code responsible for interacting with Animals in a single class, using method overloading to resolve the type at runtime (assuming you don't have any issues with generics).
But really this is just as inflexible as if-instanceof method, it just makes OO people feel better.
So, the approach to take comes down to code organization, and what makes sense for your code base.
Honestly, the if-instanceof is what I would go for, unless the amount of methods / behaviors starts getting too complex.
In which case, I'd create a type of registry, that registers a processor for each of the animal types.
Then, you can create a simple class that gets the required processor from the registry for the type of Animal.
This registry pattern I've seen used a lot in Minecraft, but I'm not sure if it's documented elsewhere.
Essentially it's use would look something like this.
void onApplicationStart(){
Registry registry = new Registry();
registry.register(Cat.class, cat -> catProcessor.process(cat));
registry.register(Dog.class, dog -> dogProcessor.process(dog));
registry.registerFallback(Animal.class, ani -> animalProcessor.process(ani));
}
Then later, you could fetch the registry, and the method that does the processing.
void whenNeeded(Animal animal){
Registry registry = fetchRegistrySomehow();
registry.for(animal.getClass()).apply(animal);
}
Edit: The implementation of registry is deliberately missing, as exact behavior differs depending on how you want to do the lookup, treat class hierarchies, when and if the registry should be sealed after some application started event.
I think you can hide you approach into beautiful candy wrapper, e.g. using Enum
or Class hierarchy
in case of many overroden methods.
E.g. this is two Animal
implementation with different methods name to get animal's name:
class Dog implements Animal {
public String getDogName() {
return "dog";
}
}
class Cat implements Animal {
public String getCatName() {
return "cat";
}
}
Then you can define an Enum
, with Functions
for each implementation:
enum AnimalRegistry {
DOG(Dog.class, animal -> ((Dog)animal).getDogName()),
CAT(Cat.class, animal -> ((Cat)animal).getCatName());
private final Class<? extends Animal> cls;
private final Function<Animal, String> getName;
AnimalRegistry(Class<? extends Animal> cls, Function<Animal, String> getName) {
this.cls = cls;
this.getName = getName;
}
public final String getName(Animal animal) {
return getName.apply(animal);
}
public static AnimalRegistry parseClass(Animal animal) {
for (AnimalRegistry registry : values())
if (registry.cls == animal.getClass())
return registry;
throw new RuntimeException("Unknown Animal implementation: " + animal.getClass().getSimpleName());
}
}
Finally, your client code could look like:
Animal animal = ...;
String animalName = AnimalRegistry.parseClass(animal).getName(animal);
Repeat, that if you have e.g. more that 2 methods to implement, Enum
becomes not so comfortable to use; then you can switch into class hierarhy and do exactly like in enum (do not forget, the in JVM each constant in Enum
is a different implementatino of Enum
interface).
P.S. Your approach is not so bad, it is pretty usefule in many cases:
private void process(Animal animal) {
if (animal instanceof Cat)
process((Cat)animal);
else if (animal instanceof Dog)
process((Dog)animal);
else
System.out.println("unsopported animal");
}
private void process(Cat cat) {
cat.getCatName();
}
private void process(Dog dog) {
dog.getDogName();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With