利斯科夫替换原则和多重层次结构
Liskov substitution principle and multiple hierarchies
这个问题是这个问题的后续问题。我正在尝试定义涉及多个碱基派生对的类层次结构。举个说明性例子,假设我有一个类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]);
}
}
};
目前为止,一切都好。现在的问题是,Food
和Animal
有许多不同的派生类。 Food
有派生类 Fruit
和 Meat
,Animal
有派生类 Carnivore
和 Herbivore
。 Herbivore
只能接受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 类中放置规则,动物类可以使用这些规则来完成自己的规则。或者决定食物是否应该是一个类。您的示例显示的内容基本上是非常糟糕的字符串比较。
- 如何重构类层次结构以避免菱形问题
- C++ 中模板化类型的类层次结构
- 为什么不同类型层次结构的指针之间的dynamic_cast定义得很好?
- 继承层次结构并将元素添加到向量
- C++ 类层次结构中的"对齐"是什么意思?
- 相同的层次结构,访问基类的受保护成员时的行为不同
- 类层次结构中的运算符重载
- 如何在层次结构中实现运算符使用?
- 反向层次结构中的可变参数模板参数
- 如何在继承层次结构中调用具有默认参数的构造函数?
- C++ 提升 - 包含类层次结构对象的类的序列化
- 在C++继承层次结构时提取实现者
- 在C++中将类实例添加到对象层次结构中的问题
- 确定大层次结构中基本指针的实际类型,无需dynamic_cast
- 在继承层次结构中复制和移动
- 模板冲突的类型-但类型应该是相同的cfr类层次结构
- 删除父/子窗口层次结构的最佳方法
- 是否可以使一个类成为两个不同层次结构的子类?
- 利斯科夫替换原则和多重层次结构
- 如何使用容器中对象的继承层次结构来维护开放/封闭原则