具有多重继承层次结构的 Liskov 替换原则

Liskov Substitution Principle with multiple inheritance 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. Animal 的每个子class 都应允许设置食物偏好,而不会破坏 LSP
  2. 每个派生 class 动物的食物偏好是食物
  3. 的子class

例如,如果有人扩展 Animal 创建 MarineMammal,食物偏好可能是 Fish(他们将通过扩展 Food 创建)。

Carnivore::setFoodPreference仅接受MeatHerbivore::setFoodPreference仅接受Fruit时,则它们不遵守同一合同。这意味着它们实际上不是同一种方法。当你调用这个方法时,你必须知道你是在处理食肉动物还是食草动物,以避免传递错误的类型。当您忘记检查它时,您可能会产生一个错误,该错误会在运行时以转换错误的形式出现。

解决办法就是把这两种方法分开。

我建议您从 public 接口中删除 setFoodPreference,而是将方法 Carnivore::setMeatPreference(Meat *meat)Herbivore::setFruitPreference(Fruit *fruit) 直接添加到子 class 中。这样,任何设置食物偏好的代码都必须知道它处理的是哪种动物和哪种食物,因此您不能再编写试图设置不兼容食物类型的代码。

在内部,这两种方法都可以从公共基础 class 设置 protected Food *_preferredFood。或者更好的是,调用 protected void setPreferredFood(Food *food),它是 setter 对应 private Food* _preferredFood。该变量绝对不应 public 以确保正确封装。