java - 里氏替换原则和多重层次结构

标签 java c++ oop inheritance liskov-substitution-principle

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

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 具有派生类 Carnivore草食动物草食动物只能接受水果作为食物偏好,而食肉动物只能接受肉类

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

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

最佳答案

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

动态转换也可能是 LSP 的失败,但如果您将类型不变量安排得足够模糊,那么从技术上讲,这并不是失败。

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

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

所以:LSP 规定 Food 的任何子类都必须遵守所有 Food 不变量。对于动物也是如此。您可以通过使不变量变得模糊并且方法的行为变得不可预测来避免 LSP 违规;基本上是说“它可能会因未指定的原因而失败”。这……不太令人满意。

现在,您可以退后一步,确定 Food* 的动态类型是 Food*一部分界面;这使得接口(interface)变得异常宽泛,并且 mock 了 LSP。

LSP 的要点是您可以推理 Food* 而无需考虑其子类类型;它们是“它作为食物的工作原理”。您的代码与子类类型紧密绑定(bind),从而绕过了 LSP 点。

有一些方法可以解决这个问题。如果 Food 有一个枚举来说明它是什么类型的食物,并且您永远不会动态地向下转换为 Meat 而是询问 Food 是否是肉,你就避开它。现在,您可以根据 Food 的界面指定 setFoodPreference 的行为。

关于java - 里氏替换原则和多重层次结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36315970/

相关文章:

r - 定义调用 R6 对象之外的其他方法的方法

java - 使用 java Files.walk 仅从根文件夹获取文件名并跳过其子目录时出现问题

java - 不止一种用 @Before 注解的方法

java - 如何优化Java堆中未使用的空间

java - 实现 Web 客户端时出现 ClassNotFoundException

c++ - 重载运算符+/char* ch1 + char* ch2

c++ - 多线程访问一个 std::map ,会导致不安全行为吗?

java - 退回这个有什么问题?

c++ - 用于在 C/C++ 中实现二维数组的数据局部性

delphi - 如何一次将 "Sender"参数与 "As"运算符一起用于多个类?