在多态继承层次结构中强制执行类型安全的最佳实践

时间:2012-03-21 14:37:14

标签: oop design-patterns polymorphism software-design

我似乎遇到了很多这种情况,还没有找到一个我认为可以接受的解决方案。

我经常会有并行继承层次结构,其中一个层次结构中的方法从另一个层次结构中作为参数传递匹配类。

这是一个可能更好地解释这个问题的例子。

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.
    }
}

过去,我通常将基类设为泛型,以允许实现具体类来定义类型,如下所示。

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

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

首先,这似乎是一个非常好的解决方案,因为它提供了编译时类型的安全性。但是我越用它,我越开始认为以这种方式使用泛型实际上是anti-pattern。问题是Animal基类不能以多态方式使用。例如,您无法轻松编写一个方法来处理任何类型的Animal,无论其实际具体类型如何。

每次我使用这个泛型解决方案时,我似乎总是在整个地方找到协变和逆变接口,只是为了尝试提供我想要的多态行为。这很快失控,一些功能无法简单,因为无法提供正确的界面。

当然另一个选择是不使用泛型并在Eat方法中执行运行时类型检查,如下所示:

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

这比我想的要好,但我不是因为缺乏编译时类型的安全性而不是它的忠实粉丝。

毕竟那就是..我的问题是......在确保某种类型安全的同时提供多态行为的最佳做法是什么?

是否有一些我缺少的OO设计技巧或模式可以让我一起避免并行继承层次结构?

我很欣赏这个问题有点主观,但是每个贡献者都可以获得积分,我会选择最佳答案作为答案。

感谢您的光临。

修改

考虑到这一点,我意识到我给出的例子并没有真正意义。当然,不可能以多态方式使用Animal,因为传递给Eat的类型将始终取决于Animal的实际基础类型(多态调用的发起者不知道)!我需要想一个更好的例子来说明我的实际情况。

2 个答案:

答案 0 :(得分:3)

我认为常识和域名的要求将决定正确的方法。使用这个例子,我会这样做

 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
       }
  }

修改

我认为使用泛型不适合这种情况,因为如果动物可以玩玩具,捕杀特定的动物等等。你可以使用许多方法来实现抽象,每次有一个带有使用多态性的参数的方法时,使用泛型都很尴尬。

答案 1 :(得分:1)

好的,这可能是你想要的吗?有时,当你无法清楚地表达自己想做的事情时,你想要的实际上就是混音。

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;
}

如果你试图给狮子喂草,这会给你一个很好的编译错误。