Reputation: 10136
I have 2 near-identical classes that ideally should share the same parent class but don't (because they come from separate libraries whose source code I cannot change).
To illustrate in an example, I have two classes like:
public class Cat {
public void speak() {
System.out.println("Meow");
}
public CatFood findFood() {
return new CatFood();
}
public void eat(CatFood food) {
System.out.println("[Cat] Yum yum");
}
}
public class Dog {
public void speak() {
System.out.println("Woof");
}
public DogFood findFood() {
return new DogFood();
}
public void eat(DogFood food) {
System.out.println("[Dog] Yum yum");
}
}
Now ideally I want to do something like:
Animal[] animals = {new Cat(), new Dog()};
for (Animal animal : animals) {
animal.speak();
animal.eat(animal.findFood());
}
but Cat
and Dog
don't inherit from Animal
and I can't change their source code. I would also like to avoid relying on instanceof
as a crutch:
for (Object animal : animals) {
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.speak();
dog.eat(dog.findFood());
} else if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.speak();
cat.eat(cat.findFood());
} else if (animal instanceof Rabbit) { // etc etc
}
The code just gets duplicated a zillion times, so if I make a small change to the logic I have to copy and paste a zillion times also.
So how can I use these classes with minimal code duplication?
Upvotes: 1
Views: 62
Reputation: 3605
You can use the Adapter pattern, but you need to implement a concrete Adapter for every type of animal:
interface Animal {
void speak();
void eat();
}
class DogAdapter implements Animal {
private Dog dog;
public DogAdapter(Dog dog) {
this.dog = dog;
}
public void speak() {
dog.speak();
}
public void eat() {
dog.eat(dog.findFood());
}
}
class CatAdapter implements Animal {
private Cat cat;
public CatAdapter(Cat cat) {
this.cat = cat;
}
public void speak() {
cat.speak();
}
public void eat() {
cat.eat(cat.findFood());
}
}
And using a factory might encapsulate the concrete creation:
class AnimalFactory {
public static Animal createAdapter(Dog dog) {
return new DogAdapter(dog);
}
public static Animal createAdapter(Cat cat) {
return new CatAdapter(cat);
}
}
Then you can use the Adapter and run in a loop:
Animal[] animals = {AnimalFactory.createAdapter(cat), AnimalFactory.createAdapter(dog)};
for (Animal animal : animals) {
animal.speak();
animal.eat();
}
One painpoint is the method eat()
because the DogFood, CatFood, ... also has no common super type.
Upvotes: 3
Reputation: 563
If you can't modify these classes and you want to keep the code at its minimum you can use reflection to call these methods. Something like:
Method method = obj.getClass().getMethod("speak");
method.invoke(obj);
But consider using reflection as a last resort only
Upvotes: 0