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

标签 c++ oop virtual-functions

我想提出一个面向对象的设计,但很难满足里氏替换原则。这是一个说明性示例:

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 的每个子类都应该允许设置食物偏好,而不会破坏 LSP
  2. 每个派生类动物的食物偏好是食物的一个子类

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

最佳答案

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

解决方案是将这两种方法分开。

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

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

关于c++ - 具有多重继承层次结构的 Liskov 替换原则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33268558/

相关文章:

c++ - 如果带有虚函数的类是用虚表实现的,那么没有虚函数的类是如何实现的呢?

c# - 构造函数重构中的虚方法调用

c++ - DDS DomainParticipantFactory 错误 CORBA

oop - 像 Java 的 instanceof 这样的结构函数是什么?

jquery - 将类从 Mootools 转换为 jQuery 或经典 javascript

java - 当我们在方法之外创建对象时会发生什么?

c++ - 在构造函数中调用虚函数

c++ - 从 C 包含并编译 C++ 头文件

c++ - hpp/cpp split with global function : Multiple definition of . .. 错误

c++ - 为什么没有给 std::forward_list 一个 count() 成员函数?