25

I very often get myself in a situation where I need a different behavior of a component which depends on the concrete type of a different Interface.

To illustrate my question, I have written a small code piece which will show what I mean. In this Example I'm trying to instantiate an food object from an animal object.

interface Animal {
    void growl();
    String getAnimalType(); //"DOG" or "FISH"
}

abstract class Food {}
Meat extends Food {}
Bread extends Food {}

class FoodFactory {
    Food createFoodForAnimal(Animal animal) {
        switch (animal.getAnimalType()) {
            case "DOG":
                Food food = new Meat();
                break;
            case "FISH":
                Food food = new Bread();
                break;
            default:
                return;
        }
    }
}


Animal dog = new Dog();
Animal fish = new Fish();
FoodFactory factory = new FoodFactory();
Food food = factory.createFoodForAnimal(dog)
Food food = factory.createFoodForAnimal(fish)

To me this feels somewhat dirty, because is looks like a cast in disguise.

  • 3
    You can either do that your way, or many modern and popular object oriented languages offer you to find out which type an object is, so you can use that instead of having a flag (e.g. C# has the keyword is). After all, a concrete implementation is already a flag by itself, I think that storing an attribute just to know what the implementation is, unless you need it for anything else, is redundant. – Andy May 18 '16 at 17:07
  • 4
    What's wrong with having the interface specify a Food createFood();? – Erik Eidt May 18 '16 at 17:12
  • 12
    @ErikEidt When was the last time you saw an animal create food? I am yet to witness a Lion create an Antilope. – Andy May 18 '16 at 17:13
  • 14
  • 3
    Eric lippert has a great series of blog posts on these types of design problems: ericlippert.com/2015/04/27/wizards-and-warriors-part-one – Ben Aaronson May 19 '16 at 8:58
21

Tell, don't ask might help you out here.

    switch (animal.getAnimalType()) {
        case "DOG":
            Food food = new Meat();
            break;
        case "FISH":
            Food food = new Bread();
            break;
        default:
            return;
    }

Notice what this code fragment is - a switch on behavior. That's a "code smell" in an object oriented solution. Usually, it indicates that there is an underlying object that you haven't discovered. Here, it might be more accurate to say that you haven't delegated the responsibility to the objects you have already discovered.

interface Animal {
    void growl();
    void orderDinner(DinnerOrder order);
}

class Dog implements Animal {
    //...

    void orderDinner(DinnerOrder order) {
        order.add(new Meat());
    }
}

class Fish implements Animal {
    //...

    void orderDinner(DinnerOrder order) {
        order.add(new Bread());
    }
} 

Actually, it probably doesn't quite make sense to specify the instance of the food that the animal wants - it probably doesn't care about a specific Bread entity, so much as Bread as a type. So maybe that gets deferred to the DinnerOrder object.

So you might implement DinnerOrder with methods that allow an animal to specify the kind of food that it wants, and free the Animal from knowing the details (encapsulation).

class Fish implements Animal {
    //...

    void orderDinner(DinnerOrder order) {
        order.addBread();
    }
}

Hard wiring in a specific command isn't readily generalized. So it might be that you want to use arguments to specify the food after all. But as noted above, we probably don't care which Bread entity is dinner tonight; in particular, we might want to give the Animals a special treat at Christmas; so the animals specify what they want, and then the provider does its best.

class Fish implements Animal {
    //...

    void orderDinner(DinnerOrder order) {
        order.add(BREAD);
    }
}

Of course, if you think preferredFood is a trait that a number of Animals have, then you might look to treat the food, not as a static constant, but as a property of an instance.

class Fish implements Animal {
    //...

    void orderDinner(DinnerOrder order) {
        order.add(this.preferredFood);
    }
}

This is a very common OO pattern; a method that passes a copy of its state to an argument for further processing. For instance, this is how DomainServices typically work in a domain model -- the service is passed to an aggregate, which supplies its own state back to the service for work.

Specifications are sometimes used to make this even more general. Instead of passing a property for the DinnerOrder to interpret, you can pass a predicate that identifies what foods have that property, and then let the receiver investigate the alternatives available.

Specification<Food> FISH_FOOD = new Specification {
    boolean isSatifiedBy(Food food) {
        return food.isA(BREAD); 
    }
}

class Fish implements Animal {
    //...

    void orderDinner(DinnerOrder order) {
        order.order(FISH_FOOD);
    }
}

Used in this way, the Specification is an example of the Strategy pattern; which might be a better fit if you wanted to give the Animals a way to rank/choose which of the available options they would prefer....

  • This fixes the flaw in amon's answer. The food types need to be abstracted. It makes even more sense when we realize that a meat eater knows how to eat meat. We don't need a zookeeper to "feed" the animal, just toss the "dinner" in the cage so to speak. Now, when the zoo gets new animals, the (useless) interface does not have to be modified and ALL the implementers do not have to be modified. So the answer to the OP question is "No" – radarbob May 19 '16 at 3:52
  • 1
    I would advise against embedding the decision making within the consuming entity (i.e. the Animal). It seems like the common case would be that a ZooKeeper or some other entity decides on the meal, and how would you expand to eating more than one type of food? – Cryo May 19 '16 at 6:51
  • 2
    @Cryo well, the last version can be extended to accept multiple types of food - you just have to modify the Specifiacation-predicate. The decision between the visitor approach and this one depends on whether it is more likely that new behaviors have to be added (which is easier in visitor case) or new types of animals (which is easier in this approach) – Hulk May 19 '16 at 7:31
29

There are two possible approaches here.

The simplest way to solve this if every Animal knows itself what they are supposed to be fed. So:

interface Animal {
  Food wantedFood();
}

This might completely solve your problem, and if so, is a good solution.

But while this is simple, this is often unsatisfactory: your Animal interface will grow and grow with marginally related methods. Or maybe we don't want each animal to feed itself, and rather want a separate ZooKeeper to handle this. A ZooKeeper knows how to handle() each Animal. How does the ZooKeeper know which animal they are handling? There's a technique for this called double dispatch, where the Animal is responsible for asking the ZooKeeper for correct handling. This looks very much like the above example where the Animal itself knows what food we want, but once we have a generic ZooKeeper we can have different ZooKeepers with different behaviours:

interface ZooKeeper {
  handleDog(Dog dog);
  handleFish(Fish fish);
}

interface Animal {
  accept(ZooKeeper keeper);
}

class Dog implements Animal {
  accept(ZooKeeper keeper) { keeper.handleDog(this); }
}

class Fish implements Animal {
  accept(ZooKeeper keeper) { keeper.handleFish(this); }
}

class FeedingZooKeeper implements ZooKeeper {
  handeDog(Dog dog) { /* the dog gets meat */ }
  handeFish(Fish fish) { /* the fish gets bread */ }
}

Animal someAnimal = ...;
FeedingZooKeeper keeper = new FeedingZooKeeper();
someAnimal.accept(keeper); // either calls handleDog() or handleFish()

We can now add other ZooKeepers without having to change the Animals.

This is generally known as the visitor pattern, and allows us to implement a function differently for different types, even if we don't know which kind of type we'll get at run time. However, there is a large trade off here: if the zoo gets a new Animal, we have to update the ZooKeeper interface, and all ZooKeeper implementations with it. This is bad, because this makes it hard to extend, but also good because it makes it more difficult to forget to handle a case.

  • 2
    To anyone reading this answer, remember you can also inherit interfaces, so in a case when one zookeper only knows how to handle dogs, it is better extracting the handledog method and let him implement that one, rather than throwing exceptions on handling animals a specific zookeper does not support. – Andy May 18 '16 at 18:10
  • 3
    handleFish(Fish fish); Huh? why not Handle(Fish fish) and Handle(Dog dog)? – radarbob May 19 '16 at 3:13
  • if you ask me handleDog(), handleFish() interface violates the Interface Segregation Principle. Might as well not have an interface and have these methods implemented in an abstract class. As it is, every time a new animal type is added - another handlexxxx(), ALL the code breaks. I guess I'm saying the same thing as @DavidPacker. – radarbob May 19 '16 at 3:38
  • 5
    @radarbob handleFish(Fish) vs handle(Fish) is a matter of taste if the language has static typing + overloaded methods, but I've found mentioning the type in the name to be much easier to debug and explain. Regarding the ISP, I understand your reservations about this approach. The visitor pattern is not without its drawbacks. However, it's fundamentally not possible to hide the dispatch in a base class or to use separate interfaces for each type. Please prove me wrong and show me some example code if I'm mistaken here… – amon May 19 '16 at 5:12
  • @amon you can have each handler dispatch separately. The downside is that you don't leverage the type system enough to do the dispatch for you. The upside is that you can now train dogs and feed fish despite fish not knowing how to train themselves and training handlers not knowing how to train fish. – John Dvorak May 19 '16 at 6:03
3

Note: If your actual code resembles the Animal/Food example, please go with the suggestions in the other answers that cover the double dispatch/visitor pattern technique.


The answer to more general question, "Is this ever appropriate?", is a qualified yes. One of the times when these sort of methods are appropriate is when implementing algebraic data types in a language that doesn't provide pattern matching syntax. While algebraic data types are more of a functional programming technique, they do crop up from time to time in object oriented code.

The basic idea is pretty simple, algebraic data types are composite types. The key that makes returning an enum to identify them a reasonable idea is that there is a fixed number of implementations of the interface, which by the nature of the type will never be expanded.

A good example is if you need a type that would allow a method to accept or return one of two types. We'll call this type Or. A very minimal Java implementation might look like this:

public enum OrSubType {
  LEFT,RIGHT
}

interface Or<L,R> {
  OrSubType type();
  L left();
  R right(); 
}

final class Left<L,R> extends Or<L,R> {
  final private L value;

  public Left(L value) {
    this.value = value;
  }
  public OrSubType type() { return LEFT; }
  public L left() { return value; }
  public R right() { 
    throw new IllegalStateException(
      "Attempted to get a Right from a Left(" + value + ")"
    );
  }
}

final class Right<L,R> extends Or<L,R> {
  final private R value;

  public Right(R value) {
    this.value = value;
  }
  public OrSubType type() { return RIGHT; }
  public R right() { return value; }
  public L left() { 
    throw new IllegalStateException(
      "Attempted to get a Left from a Right(" + value + ")"
    );
  }
}

That's quite a lot of extra code, but it will allow you to do stuff like this:

Or<Value,ErrorCode> result = doOperationThatCanFail();
switch(result.type()) {
  case LEFT: 
    doSomethingWithResult(result.left());
    break;
  case RIGHT:
    displayErrorMessageForCode(result.right());
    break;
}

If the language in question has pattern matching as first class syntax, there's often help provided to make this easier to use. In Scala the above example class would be simply this:

sealed trait Or[L,R] {
  def left: L
  def right: R
}

object Or {
  case class Left[L](var left: L) extends Or[L,Nothing] {
    def right: Nothing = throw new IllegalStateException(
      s"Attempted to get a Right from a Left($left)"
    )
  }

  case class Right[R](var right: R) extends Or[Nothing,R] {
    def left: Nothing = throw new IllegalStateException(
      s"Attempted to get a Left from a Right($left)"
    )
  }
}

The sample usage is also simplified:

val result: Or[Value,ErrorCode] = doOperationThatCanFail()
result match {
  case Or.Left(value) => doSomethingWithResult(value);
  case Or.Right(errorCode) => displayErrorMessageForCode(errorCode);
}
  • +1 for shoe-horning the Either Monad into Java. If I could I'd give you another +1 for not using the terms Either and Monad in doing so. – Jared Smith May 19 '16 at 10:16
  • 1
    This is usually called over-engineering :) – Silviu Burcea May 19 '16 at 14:38
  • @Silviu Burcea You should see the amount of extra junk needed for higher-order functions, if the language doesn't support it natively. Java before 8 is a good example of how awkward it can get :) – Morgen May 19 '16 at 14:54
  • @JaredSmith - while this is an implementation of Either, it isn't an implementation of the Either Monad, as it doesn't contain a bind function. Which would look like public <T> Or<L,T> bind(Function<R,Or<L,T>> f) { return this; } for Left and public <T> Or<L,T> bind(Function<R,Or<L,T>> f) { return f.apply(value); } for Right. Bonus points for implementing fmap. :) – Jules Jun 13 '16 at 15:44
1

This strikes me as very similar to dependency injection, the only difference being that a dependency is only set once and an animal may be fed multiple times. My preferences (in order) would be...

  1. Let the animals create their own food (only possible if food is easy to create).
  2. Create a type-specific food factory (e.g. a MeatFactory for a Dog) and pass that into the animal when you create it, using dependency injection if necessary. Then there can be just an eat() method where the animal grabs the food from the factory and chows down.
  3. If not, and there are some common resources/ingredients necessary to create food, then pass those into the feed() method instead.
  4. If there is no real commonality between the foods, then create a bucket of foods (a refrigerator, or DoI container if you prefer), and give that to the animals to take out what food they want to eat.

Thinking on it a bit more I suppose it would depend on the animal's primary purpose. If the primary purpose is not really food related (e.g. entertaining people, breeding, etc.) and food is just something it needs to survive/do its job then the above options are what I would go with.

However, if the animal's primary purpose is to destroy food (or process it, e.g. creating fertilizer) then the above may be a good fit but there may be other patterns.

You may have a router of sorts, routing foods to the correct animals. The thing that leaps to mind is the URL routing mechanism used by technologies like JAX-RS. They have a registry of services which map to different URLs. Such a thing might be implemented by a Set of Animal where each animal has a canEat() method and you just cycle through each animal in your menagerie until you find one that can eat the food you need eaten. Or you might do that with a HashMap. These will be pretty similar to your original design but slightly more extensible (since there is no switch statement to update).

0

If the set of constants is somewhat small without much potential to grow exponentially larger, I consider it acceptable. I would strongly recommend using an enum return type, rather than a string, to allow the compiler to catch errors before runtime.

Your initial approach may seem less than desirable in part because you are introducing an unnecessary coupling between the FoodFactory and Animal classes. Consider the below alternative, where the createFoodForAnimal parameter has changed from Animal to AnimalType. Taking this approach offers a couple of benefits:

1) The FoodFactory can now be tested without also needing to instantiate an Animal.

2) The code clarity has improved slightly

public enum AnimalType {
  FISH, LIGER
}

class FoodFactory {
    Food createFoodForAnimal(AnimalType animalType) {
        Food food;
        switch (animalType) {
            case LIGER:
                food = new UnicornMeat();
                break;
            case FISH:
                food = new Bread();
                break;
            default:
                food = new Spam();
        }
        return food;
    }
}

Having some of the other responses, I would also add that keeping the decision logic separate from the Animal or ZooKeeper class can greatly improve the flexibility of your system. For instance, consider needing to make a change where all animals eat bread at a particular time of the day. You can make a much less obtrusive modification to the FoodFactory than you can to all of the Animal classes.

  • 1
    +1 BUT: The downside of this approach compared to the other ones is that when adding a new AnimalType you have to remember to update all of the factories that use it - without any compile time check. (The best you get is a runtime check if you throw in the default case instead of handing out spam to all unknown animals). – Hulk May 19 '16 at 7:54
  • @Hulk If that were ever a major concern, we could forgo embedding the "default" behavior within the switch statement and enable a compiler warning/error to check for enum values that are not covered by a switch statement. gcc, for instance, will raise a compile error when treating warnings as errors (e.g. -Wall -Werror or -Wswitch-enum). Eclipse also has an option to raise a compile error for enums not handled by a switch statement. I've never considered the enabling of these features as optional. – Cryo May 19 '16 at 8:25
0

You can think in terms of composable behaviours using DI. You already have all the exising pieces in hand, just tweak it a little bit:

// Your FoodFactory interface
public Food createFood();

// Factories, each of them returns a specific kind of food
meat = new MeatFactory();
fish = new FishFactory();
poop = new PoopFactory();

// Inject the wanted factory to the animal to have the desired behaviour
cat = new Cat(fish);
dog = new Dog(poop);
tiger = new Tiger(meat);
lion = new Lion(meat);
  • It's closed for modification, as opposed to your first example.
  • Several animals can use the same food factory, without extra code
  • You "wire" your objects as you like, building the behaviour you want, in a relative flexible way.
  • The animal asks the factory for the food without knowing what it is
  • The food factory doesn't know about animals.

Extra: you can make your food factories smarter if needed:

cat = new Cat(
    new RotatingFood(fish, meat)
);

dog = new Dog(
    new RandomFood([meat, poop, fish, shoe])
);

// RandomFood & RotatingFood both implement FoodFactory and use food factories

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.