2015-10-21 39 views
0

我想要拿出一个面向对象的设计,并有难以满足Liskov替代原则。下面是一个说明性的例子:Liskov替代原则与多继承heirachies

class Food 
{ 
    public: 
    virtual void printName() { 
    //...... 
    } 
}; 
class Fruit : public Food 
{ 
}; 
class Meat : public Food 
{ 
}; 
class Animal 
{ 
    public: 
    Food *_preferredFood; 
    virtual void setFoodPreference(Food *food)=0; 

}; 
class Carnivore: public Animal 
{ 
    public: 
    void setFoodPreference(Food *food) { 
     this->_preferredFood = dynamic_cast<Meat *>(food); 
    } 
}; 
class Herbivore: public Animal 
{ 
    public: 
    void setFoodPreference(Food *food) { 
     this->_preferredFood = dynamic_cast<Fruit *>(food); 
    } 
}; 

我怎么能执行以下操作:

  1. 动物的每个子类都应该允许设置食物偏好,而不会破坏LSP
  2. 食物偏爱每个派生类动物是食物的一个子类

前面的例子,如果有人延伸Animal创建MarineMammal,foo d偏好可能是Fish(他们将通过延伸Food创建)。

+0

出于兴趣,您正在寻找的实际多态行为是什么?即客户端代码将针对基类调用什么确切的方法?我问这是因为我认为你冒险解决错误的问题。动物可能会乍看起来像继承问题,但它更可能是现实中的一个特征问题。 –

+0

你说得对,我不是在寻找多态行为。我真正想要的是任何扩展“Animal”的人来定义正确的“setFoodPreference”函数。当然,我可以争辩说我想调用''setFoodPreference''。 – SPMP

回答

1

Carnivore::setFoodPreference只接受MeatHerbivore::setFoodPreference只接受Fruit,那么他们不符合同一合同。这意味着他们实际上不是相同的方法。当你调用这个方法时,你必须知道你是在与食肉动物还是草食动物打交道,以避免错误的类型。当你忘记检查这个时,你可能会冒险创建一个在运行时出现铸造错误形式的错误。

解决方法是分离这两种方法。

我建议您从公共界面删除setFoodPreference,而是直接将方法Carnivore::setMeatPreference(Meat *meat)Herbivore::setFruitPreference(Fruit *fruit)添加到子类。这样,任何设定食物偏好的代码都必须知道它处理的是什么样的动物和食物类型,所以你不能再编写试图设置不兼容食物类型的代码。

在内部,两种方法都可以从公共基类设置protected Food *_preferredFood。或者更好,请致电protected void setPreferredFood(Food *food)这是一个private Food* _preferredFood的二传手。该变量绝对不应公开以确保正确封装。

+1

我认为我应该保持_preferredFood私人,并setFoodPreference保护虚拟。因为我需要强制派生类允许设置首选项。 – SPMP

+0

@ user2308211为什么“虚拟”?当任何子类仅通过它们自己的方法调用该方法时,可以在其中放置任何特定于该子类的代码。 – Philipp

+0

我应该说,纯粹是虚拟的。确保他们实施这些方法。虽然它不是一个完整的解决方案,但有人可以从Animal中派生出来,并只实现setFoodPreference。 – SPMP