1

Suppose that you have an abstract class representing a generic animal (called Animal) that provides a method called absorbedFat that taking a generic Food (class Food) computes how many grams of fat an animal absorb:

interface Animal
{
   int absorbedFat( Food food );
}

interface Food
{
   string getName();
}

Which approach you would follow to design and implement concrete classes: Dog, Cat and Meat, Bread?

P.S.: As pointed out in the comment, I have tried to use polymorphism with Animal class, but then I had trouble with managing of the Food parameter (since the language that I am using doesn't provide double dispatch), so I tried Visitor pattern, but I don't like it so much (due to overloading and changing of existing classes every time that I add a new type of animal)

6
  • Sharing your research helps everyone. Tell us what you've tried and why it didn't meet your needs. This demonstrates that you've taken the time to try to help yourself, it saves us from reiterating obvious answers, and most of all it helps you get a more specific and relevant answer. Also see How to Ask
    – gnat
    Aug 22 at 19:36
  • I have tried to use polymorphism with Animal class, but then I had trouble with managing of the Food parameter (since the language that I am using doesn't provide double dispatch), so I tried Visitor pattern, but I don't like it so much (due to overloading and changing of existing classes every time that I add a new type of animal), so I was asking how other people would manage this simple scenario.
    – Kill KRT
    Aug 22 at 19:45
  • What language do you use ?
    – Christophe
    Aug 22 at 22:02
  • I am using Rust, but I was looking for an agnostic approach
    – Kill KRT
    Aug 23 at 10:16
  • 1
    Top rule for modeling such tasks: Clarify the requirements first!. In this case, it is unclear (a) where absorbedFat gets its results from (is it just a table from a book, or some mathematical formula which requires certain parameters from Animal and Food? (b) who is responsible for extending the list of animals and foods? Is it the same person who will be responsible to extend the "absorbedFat" table? Or do you need a super-extensible library solution, with a default list of animals / foods / absorbedFat entries, ...
    – Doc Brown
    Sep 5 at 6:56
1

This is a classic example of the use of the Visitor pattern and if it is for some sort of classroom exercise or programming quiz, then that is what you should use for the solution. As for alternative solutions, I expect it is very language specific.

Here is an alternative solution in Swift. This solution mimics a double-dispatch situation. Keep in mind that no matter what you do (even with double-dispatch), every time you add a sub-type on either class, you will have to write a number of functions equal to the number of sub-types for the other interface.

enum Animal {
    case dog
    case cat
}

enum Food {
    case meat
    case bread
}

func absorbedFat(animal: Animal, food: Food) -> Int {
    switch (animal, food) {
    case (.dog, .meat):
        return 3
    case (.cat, .meat):
        return 5
    case (.dog, .bread):
        return 7
    case (.cat, .bread):
        return 11
    }
}

I would use the above, mainly because I have lots of opportunities for reuse. Whenever a new case is added to either enum, the compiler will remind me to implement the new combinations needed in this method so I am assured that nothing is missing.

1

You have here a computation that depends on a combination of two runtime types:

  • You may code every possible combination, but that would be very redundant and painful that will get impossible to maintain.
  • You may find a few additional properties in each class, that allows you to find a simple formula.
  • You use double-dispatch. If your language doesn't support it, you can perhaps emulate it using parameter overload combined with a polymorphic call (see the C++ example in the wikipedia article).
  • You simulate double-dispatch by using a dispatch table that maps a pair of types (e.g. Bear, Butter) to a function dynamically. It's a tricky table to maintain, because of the inheritance mechanisms. Maybe even, you just use a data table that maps a combination to a value (aka database approach). You can use your language's type information if it allows some minimalistic run-time introspection; otherwhise, you'd be forced to add a typename function in every class.
  • You can use the visitor.

In any case, unless you can use the simple formula, whenever you add new types, you'll have additional maintenance work; It's not just the visitor: even Daniel's answer will require you to updated the switch bloc. It's inevitable because the design is based on combinations.

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.