利斯科夫替换原则和多重层次结构

Liskov substitution principle and multiple hierarchies

本文关键字:层次结构 原则 替换      更新时间:2023-10-16

这个问题是这个问题的后续问题。我正在尝试定义涉及多个碱基派生对的类层次结构。举个说明性例子,假设我有一个类Animal和一个类FoodAnimal有一个纯虚函数来标记它的食物偏好,以食物为参数。

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

我需要编写仅处理这些基类的代码,并调用纯虚函数。例如,我有一个ZooManager课,它为每只动物设定食物偏好。

class ZooManager
{
    vector<Aninal*> animals;
    public:
    void setAllPreferences(vector<Food *> foods) {
        assert(animals.size() == foods.size());
        for(int i =0;i<animals.size();i++) {
            animals[i]->setFoodPreference(foods[i]);
        }
    }
};

目前为止,一切都好。现在的问题是,FoodAnimal有许多不同的派生类。 Food 有派生类 FruitMeatAnimal 有派生类 CarnivoreHerbivoreHerbivore只能接受Fruit作为食物偏好,Carnivore只能接受Meat

class Fruit : public Food
{
};
class Meat : public Food
{
};
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);
    }
};

我可以在不违反 Liskov 替换原则的情况下为此创建类层次结构吗?虽然我在这个问题中使用了C++,但我也欢迎特定于 Java 的答案。

首先,您的setFoodPreference必须具有失败的选项。 这让setFoodPreference采取Food*,并拥有设置食物偏好或失败的后置条件。

动态强制转换也可能是 LSP 的失败,但如果将类型不变量安排得足够模糊,则在技术上不是失败。

通常,dynamic_cast意味着传递的参数的类型及其属性不足以判断参数是否具有某些属性。

原则上,setFoodPreference(Food*)应根据传入参数必须具有哪些Food*属性来指定,以使设置成功;Food*的动态类型不是Food*属性。

所以:LSP声明Food的任何子类都必须遵守所有Food不变量。 同样适用于Animal. 您可以通过使不变量模糊且方法的行为不可预测来避免 LSP 冲突;基本上是说"它可能由于未指定的原因而失败"。 这是。。。不是很满意。

现在,你可以退后一步,决定Food*的动态类型是Food*接口的一部分;这使得接口宽得离谱,是对LSP的嘲弄。

LSP的要点是,您可以对Food*进行推理,而不必考虑其子类类型; 它们是"它如何作为Food工作"。 您的代码与子类类型紧密绑定,因此绕过了 LSP 的要点。

有办法解决这个问题。 如果Food有一个枚举来说明它是什么食物,而你从不动态地投向Meat而是问Food它是不是肉,你就会避免它。 现在,您可以根据Food 的接口指定 setFoodPreference 的行为。

你设计层次结构的方法很错误。OO 类表示紧密耦合的规则组,其中规则是一个函数,紧密耦合表示共享数据。OO 类不表示现实世界的对象。人们这样说是错误的。

如果您依赖于食物的具体类型,则违反了利斯科夫替代原则。时期。

要正确设计您的类,以便您不会像这里一样被迫违反 LSP,您需要在 Food 类中放置规则,动物类可以使用这些规则来完成自己的规则。或者决定食物是否应该是一个类。您的示例显示的内容基本上是非常糟糕的字符串比较。