Avoiding use of 'instanceof'

Go To StackoverFlow.com

10

I'm struggling with how I might avoid using instanceof() in some of my code. This contrived example somewhat captures the problem.

Class Meat extends Food;

Class Plant extends Food;

Class Animal;

Class Herbivore extends Animal
{
    void eat( Plant food);
}

Class Carnivore extends Animal
{
    void eat( Meat food);
}

Class Omnivore extends Animal
{
    void eat(Food food);
}

Class Zoo
{
    List<Animals> animals;

    void receiveFood( Food food)
    {
        // only feed Plants to Herbivores and Meat to Carnivores
        // feed either to Omnivores
    }
}

Herbivores are only interested in Plants, Carnivores only in Meat and Omnivores both. When the Zoo receives Food it only makes sense to try to feed food to animals that eat that type of food.

I've thought of a few solutions, but all seem to depend on the use of instanceof() somewhere and my various refactorings just seem to move it around.

(1) I could implement eat( Food food) in Animal and each subclass could choose to ignore food that it doesn't eat, but that is inefficient and would require that each Animal subclass use instanceof() to test the type of food.

(2) I could keep three collections of animals in the Zoo based on the type of food they eat, but would still have to use instanceOf() to test the type of food to see which collection to feed it to. At least this would be more efficient as I wouldn't be feeding food to Animals that won't eat it.

I've thought of some other approaches, but again, they just seem to pass the instanceof() buck.

Any suggestions? Or would this (2, at least) be an acceptable use of instanceof()?

2012-04-04 19:11
by HolySamosa
Perhaps you should look into generic methods? That way, you could specify that an Animal has a method eat(T extends Food) and Herbivore would be typed as Plant? Something along those lines - Tom G 2012-04-04 19:13
possible duplicate of Is This Use of the "instanceof" Operator Considered Bad Design?Tomasz Nurkiewicz 2012-04-04 19:14
Perfect candidate for the visitor patter - bluesman 2012-04-04 19:26
Hmmm... Wouldn't a visitor pattern solution have a similar problem as (1) above? When food is received animal would have to be visited even if it didn't eat the type of food? A visitor example would be awesome-- I'm never sure if I fully get that pattern in some contexts.. - HolySamosa 2012-04-04 19:56
Your concern about the inefficiency is probably not very well founded. Every animal has to get fed, so the cost of iterating is going to be linear. In the worst case (where every animal is an herbavore) you will be feeding N times but asking permission 2*N times. This is very likely a reasonable inefficiency. If you are feeding VERY often, and the configurations of animals RARELY changes, it may be worth pre-processing them into lists of herbavores, omnivores, and carnivores - idbentley 2012-04-04 20:29
What is the interface of the super type Animal? Does it deal with ? If so, would the whole abstraction of Animals and Food not be a violation of the Liskov Substitution Principle? Maybe OOP is not always the best abstraction for taxonomies of nature.. - nansen 2012-04-04 20:36
I'd probably say that this is an abuse of inheritence. @nansen is right, this violates one of the SOLID principles. Violating LSP will cause you no end of grief, unfortunately you often don't feel most of it until you're well into your implemenation - Andy 2012-04-05 01:39
THis looks very similar to this http://stackoverflow.com/questions/9806785/best-practice-for-enforcing-type-safety-in-polymorphic-inheritance-hierarchies/9807170#980717 - MikeSW 2012-04-05 10:01


12

The visitor pattern solves your problem. Here's the code:

public abstract class Animal {
  public abstract void accept(AnimalVisitor visitor);
}

public interface AnimalVisitor {
  public void visit(Omnivore omnivore);
  public void visit(Herbivore herbivore);
  public void visit(Carnivore carnivore);
}

public class Carnivore extends Animal {
  @Override
  public void accept(AnimalVisitor visitor) {
    visitor.visit(this);
  }

  public void eat(Meat meat) {
    System.out.println("Carnivore eating Meat...");
  }
}

public class Herbivore extends Animal {
  @Override
  public void accept(AnimalVisitor visitor) {
    visitor.visit(this);
  }

  public void eat(Plant plant) {
    System.out.println("Herbivore eating Plant...");
  }
}

public class Omnivore extends Animal {
  @Override
  public void accept(AnimalVisitor visitor) {
    visitor.visit(this);
  }

  public void eat(Food food) {
    System.out.println("Omnivore eating " + food.getClass().getSimpleName() + "...");
  }
}

public abstract class Food implements AnimalVisitor {
  public void visit(Omnivore omnivore) {
    omnivore.eat(this);
  }
}

public class Meat extends Food {
  @Override
  public void visit(Carnivore carnivore) {
    carnivore.eat(this);
  }

   @Override
  public void visit(Herbivore herbivore) {
    // do nothing
  }
}

public class Plant extends Food {
   @Override
  public void visit(Carnivore carnivore) {
    // do nothing
  }

   @Override
  public void visit(Herbivore herbivore) {
    herbivore.eat(this);
  }
}

public class Zoo {
  private List<Animal> animals = new ArrayList<Animal>();

  public void addAnimal(Animal animal) {
    animals.add(animal);
  }

  public void receiveFood(Food food) {
    for (Animal animal : animals) {
      animal.accept(food);
    }
  }

  public static void main(String[] args) {
    Zoo zoo = new Zoo();
    zoo.addAnimal(new Herbivore());
    zoo.addAnimal(new Carnivore());
    zoo.addAnimal(new Omnivore());

    zoo.receiveFood(new Plant());
    zoo.receiveFood(new Meat());
  }
}

Running the Zoo demo prints

Herbivore eating Plant...
Omnivore eating Plant...
Carnivore eating Meat...
Omnivore eating Meat...
2012-04-04 20:14
by chris
Refactored code by extracting interface AnimalVistor from class Foo - chris 2012-04-04 21:33
Although the nerd in me loves to see code for the Visitor pattern in action (bravo!), it was designed for adding new Visitors without breaking the Visited classes, not for avoiding instanceof (which is what the question is about). The property solutions (isMeat() etc) are less complex and humbler - Fuhrmanator 2012-04-09 05:16


5

In your case, if the consumer of the object must know certain things about that object (e.g. is it meat), include a property in your base class isMeat() and have concrete subclasses override the implementation of the base class method to return an appropriate value.

Leave that knowledge in the class itself, rather than in consumers of the class.

2012-04-04 19:13
by Eric J.
So the case for putting an isMeat() method in the base class being better than using instanceof is that in the case of the latter knowledge about the (possible) implementation is being transferred to the client class? I guess then it would come down to weighing this knowledge transfer to the client with cluttering of the base object with isXXX methods. I can see different cases perhaps tipping the scale in either direction - HolySamosa 2012-04-11 16:51


4

A simple solution is when using multiple custom classes interacting with one another, just create isFood(), isAnimal(), isCarnivore(), etc. methods that return a boolean depending on which class they're in. It's not the prettiest but it gets the job done 100% of the time.

2012-04-04 19:14
by Chris
This requires a cast - chris 2012-04-04 20:30
See comment above. You guys gave basically the same answer at the same time - HolySamosa 2012-04-11 16:52


1

Expanding on my comment, I would attempt to use generics to help me here:

interface Animal<T extends Food> {
    void eat(T food);
}

class Herbivore extends Animal<Plant> {
    void eat(Plant food) { foo(); }
}

class Carnivore extends Animal<Meat> {
    void eat(Meat food) { bar(); }
}

Note that this still doesn't solve the problem of iterating through a list of Food and Animal and only sending appropriate food to each animal -- I don't see a way to do that without explicit instanceof style checks. But, it does allow you to be more specific with what your subclasses accept.

2012-04-04 19:24
by Tom G
See http://stackoverflow.com/questions/9806785/best-practice-for-enforcing-type-safety-in-polymorphic-inheritance-hierarchies/9807170#9807170

This question was mentioned above, but it does discuss some of the potential pitfalls of just such a use of generics. For the record, though, I had previously been considering just such a solution - HolySamosa 2012-04-05 16:09



0

Another solution is to maintain 2 lists: one for Herbivores and one for Carnivores.

2012-04-04 20:01
by Puce
That's one thing I thought of, but it seems that I would still need to use instanceof() to test for the type of food to decide which list to pass the food to - HolySamosa 2012-04-04 20:27
Well, you would also need to channels for getting food: one only providing plants the other only providing meat - Puce 2012-04-05 08:32
Ads