Interface subtypes are only compatible with another interface subtype

I've been scratching my head over the problem for a long time, and I still don't know what would be the best solution. Since the application domain is very technical, I will illustrate my problem with a simple example.

Let's assume I have the following interfaces:

public interface Animal {
    public void feed(AnimalFood food);
}

public interface AnimalFood {
    // some methods
}

      

And the next two classes that implement the interfaces:

public class DogFood implements AnimalFood {
    // some methods
}

public class CatFood implements AnimalFood {
    // some methods
}

public class Dog implements Animal {
    public void feed(AnimalFood food){
        // can only eat dog food
    }
}

public class Cat implements Animal {
    public void feed(AnimalFood food){
        // can only eat cat food
    }
}

      

This means that every time I pass in an instance of Dog

or Cat

, I will need to check if the instance received was DogFood

or CatFood

, and throws an exception if it is the wrong kind of food.

It smells bad to me, and I am sure that I will violate the Liskov substitution principle!

Is there a design pattern or what would be an elegant way to handle this situation?

+3


source to share


3 answers


You seem to be using Java. If so, you can use generics to constrain the type of the parameter infeed



public interface Animal<T extends AnimalFood> {
      void feed(T food);
}



public class Dog implements Animal<DogFood> {
    public void feed(DogFood food){
        // can only eat dog food
    }
}

public class Cat implements Animal<CatFood> {
    public void feed(CatFood food){
        // can only eat cat food
    }
}

      

+4


source


Since the question seems to be language agnostic, I'll answer in C #, but the ideas will apply to Java and the like.

We could declare IAnimalFood

as a generic interface, but that would give a compile-time error, not the runtime exception you asked for if you try to feed a cat to a dog:

    void Main()
    {
        new Cat().Feed(new UserQuery.CatFood());
        new Dog().Feed(new UserQuery.CatFood()); // won't compile
    }

    public interface IAnimalFood { }

    public class DogFood : IAnimalFood { }

    public class CatFood : IAnimalFood { }

    public interface IAnimal<TFood> where TFood : IAnimalFood
    {
        void Feed(TFood food);
    }

    public class Dog : IAnimal<DogFood> {
        public void Feed(DogFood food){
            // can only eat dog food
        }
    }

    public class Cat : IAnimal<CatFood> {
        public void Feed(CatFood food){
            // can only eat cat food
        }
    }

      

There seems to be no sensible alternative to the runtime of checking the type of food supplied, other than the declarative pattern I have used before: add a property to the food type that defines what the food is:

void Main()
{
    new Dog().Feed(new UserQuery.CatFood());
}

public enum FoodType
{
   CatFood,
   DogFood,
   BirdFeed
}

public interface IAnimalFood {
   FoodType TypeOfFood { get; }
}

public class DogFood : IAnimalFood {
   public FoodType TypeOfFood { get { return FoodType.DogFood; } }
}

public class CatFood : IAnimalFood
{
   public FoodType TypeOfFood { get { return FoodType.CatFood; } }
}

public interface IAnimal
{
    void Feed(IAnimalFood food);
}

public class Dog : IAnimal {
    public void Feed(IAnimalFood food){
        // can only eat dog food
        if (food.TypeOfFood != FoodType.DogFood)
         throw new Exception("You must be joking pal! I don't eat that!");
    }
}

      

Result:



An exception

You must be joking! I don't eat this!

Alternatively, AnimalFood

implements a flag enumeration (bit-field) that indicates the types of animals that can eat them.

Anyway (type of food or animals that can eat me):

  • You don't need to type

  • No compile time limits

  • The type of food or animals that can eat it, well encapsulated in the AnimalFood class

This can be combined with the factory idea suggested elsewhere.

+1


source


To compliment the accepted answer with Generics, it would be helpful if all Animal instances could create their own product type using the Factory Method design pattern.

Here's the GoF template:

Factory Method pattern (GoF)

Here's how to apply it to your case:

Factory Method pattern applied (GoF)

0


source







All Articles