Best practice for enforcing type safety in polymorphic inheritance hierarchies [closed]

大憨熊 提交于 2019-12-12 15:49:32

问题


I seem to run into this situation quite a lot and have yet to find a solution that I find acceptable.

Quite often I will have parallel inheritance hierarchies where a method in one hierarchy gets passed the matching class from the other hierarchy as a parameter.

Here is an example that probably explains this better.

abstract class Animal
{
    public virtual void Eat(Food f)
    {
    }
}

abstract class Food
{
}

class LionFood : Food
{
}

class ElephantFood : Food
{
}

class Lion : Animal
{
    public override void Eat(Food f)
    {
        // It is only ever valid to pass LionFood here as the parameter.
        // passing any other type of Food is invalid and should be prevented
        // or at least throw an exception if it does happen.
    }
}

In the past, I have usually made the base class generic to allow the implementing concrete class to define the type as follows..

abstract class Animal<T> where T : Food
{
    public abstract void Eat(T f);
}

class Lion : Animal<LionFood>
{
    public override void Eat(LionFood f)
    {
    }
}

At first this seems like a very good solution because it provides compile-time type safety. But the more I use it, the more I am starting to think that using generics in this way is infact an anti-pattern. The problem is that the Animal base class cannot be used in a polymorphic way. You cannot, for example, easily write a method that will process any type of Animal regardless of its actual concrete type.

Every time I use this generics solution, I always seem to end up with covariant and contravariant interfaces all over the place just to try and provide the polymorphic behaviour I want. This gets out of hand pretty quickly and some functionality is not possible simply because the correct interface cannot be provided.

Of course another option is to not use generics and perform runtime type checking in the Eat method like this:

    public override void Eat(Food f)
    {
        if (f.GetType() != typeof(LionFood))
        {
            throw new Exception();
        }
    }

This is better than nothing I suppose but I'm not a huge fan of it simply because of the lack of compile-time type safety.

So after all that.. My question is.. What is the best practice to provide polymorphic behaviour while at the same time ensuring some type safety?

Is there some OO design trick or pattern that I am missing that will allow me to avoid the parallel inheritance hierarchies all together?

I appreciate that this question is somewhat subjective, but there are points available for everyone who contributes and I'll choose the best response as the answer.

Thanks for looking.

Edit:

Having thought about this I realise that my example given doesn't really make sense. Of course it is not possible to use Animal in a polymorphic way because the type passed to Eat will always depend on the actual underlying type of Animal (which the initiator of a polymorphic call will not know)! I need to think of a better example that illustrates my actual situation.


回答1:


I think common sense and the requirements of the domain will dictate the proper approach. Working with this example, I'd do like this

 public class Lion:Animal
 {
       public override void Eat(Food f)
        {
           Eat(f as LionFood);
         }
       public void Eat(LionFood food)
        {
         //check for null food  
         //actually consume it
       }
  }

Edit

I think using generics is not suited in this case, because what if an Animal can play with a Toy, hunt a specific Animal and so on. You can have a number of methods with arugments that implement an abstraction, it's awkward to use generics every time there is a method with an argument that uses polymorhpism.




回答2:


Ok, could that be what you want? Sometimes, when you can't articulate what you want to do, what you want is actually a mixin.

template<typename base>
class Eater: public base {

template<typename T>
Eat(T (extends Food) food) { // you can do that extends thing in C++ but I won't bother
    // register what's eaten, or do whatever
    base::Eat(food);
}

}

class Lion {
    Eat(LionFood food) {
        cout<<"Hmm delicious!";
    }
}

int main() {
    Eater<Lion> lion;
    lion.eat(LionFood());
    return 0;
}

This'll give you a nice compiler error if you try to feed grass to the lion.



来源:https://stackoverflow.com/questions/9806785/best-practice-for-enforcing-type-safety-in-polymorphic-inheritance-hierarchies

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!