理解合同和里氏替换原则
Understanding contracts and Liskov Substitution principle
考虑图表:
Collection - 抽象 class 与所有其他的公共部分:抽象函数
将整数放入集合并检查集合是否为空。
Bag - class 将集合实现为散列,您可以在其中轻松找到元素。
每个元素可能出现不止一次
我需要确定这种继承关系是否符合里氏代换原则。
所以,我首先检查函数的签名是否相同。 (put() 和 isEmpty())。
之后,我检查两者的方法合同是否相同,但我可以有合同抽象class吗?我不能 'create' 它。
如果是,就足以说明这两个 class 维护了 LSP?
还是需要其他东西?
简介
Liskov Substitution principle 是良好的面向对象设计 (AKA SOLID) 的 5 条基本原则之一:
- S(RP): Single responsibility principle
- O(CP): Open/closed principle
- L(SP): Liskov substitution principle
- 我(SP): Interface segregation principle
- D(IP): Dependency inversion principle
Barbara Liskov 陈述的原则说 使用基 classes 引用指针的函数必须能够在不知情的情况下使用派生 classes 的对象。这是一个很好的设计原则,因为当一个函数不符合 LSP 时,它必须知道基 class.
所有可能的导数
当您考虑违反该原则的后果时,该原则的重要性就显而易见了。让我们举个例子。假设我们正在开发一个处理形状的程序。我们已经创建了 class Rectangle,并且在设计 class Square 时,我们认为很自然地继承自 class Rectangle:
使用 C++ 我们会写:
class Rectangle
{
public:
virtual void SetWidth(const double w) {width_=w;}
virtual void SetHeight(const double h) {height_=h;}
virtual double GetHeight() const {return height_;}
virtual double GetWidth() const {return width_;}
private:
double width_;
double height_;
};
class Square : public Rectangle
{
public:
virtual void SetWidth(const double w) {
Rectangle::SetWidth(w);
Rectangle::SetHeight(w);
}
virtual void SetHeight(const double h) {
Rectangle::SetWidth(w);
Rectangle::SetHeight(h);
}
};
void function (Rectangle& r)
{
r.SetWidth(5);
r.SetHeight(4);
assert(r.GetWidth() * r.GetHeight()) == 20);
}
从示例中可以清楚地看出,断言(和 Liskov 项目)被违反了,因为矩形有一个 属性(身高和体重大小的独立性),它不坚持一个广场。
要回答您的问题,您应该推理适用于基础 class 但不适用于派生 class 的属性。如果界面设计得很好(看起来),你将找不到任何东西,你将能够用任何派生的 class.
替换基础 class
恕我直言,Square
class 的问题是违反了 Rectangle
的合同:
- 对于
Rectangle
,Width
和 Height
属性是独立的,因此如果您更改一个,另一个将保持原样
Square
通过更改每个 setter 中的两个属性违反了此合同
- 如果我们将
Square
传递给期望 Rectangle
的代码,并在 Width
和 Height
不相关的前提下进行计数,那么我们就会遇到问题
我们还违反了历史规则(参见维基百科的文章Liskov Substitution Principle)。听起来很奇怪我将删除这些 class 之间的继承关系并引入一个 Shape
基础 class,因为那些拖车很难坚持 LS 原则。
考虑图表:
Collection - 抽象 class 与所有其他的公共部分:抽象函数 将整数放入集合并检查集合是否为空。
Bag - class 将集合实现为散列,您可以在其中轻松找到元素。 每个元素可能出现不止一次
我需要确定这种继承关系是否符合里氏代换原则。
所以,我首先检查函数的签名是否相同。 (put() 和 isEmpty())。 之后,我检查两者的方法合同是否相同,但我可以有合同抽象class吗?我不能 'create' 它。 如果是,就足以说明这两个 class 维护了 LSP? 还是需要其他东西?
简介
Liskov Substitution principle 是良好的面向对象设计 (AKA SOLID) 的 5 条基本原则之一:
- S(RP): Single responsibility principle
- O(CP): Open/closed principle
- L(SP): Liskov substitution principle
- 我(SP): Interface segregation principle
- D(IP): Dependency inversion principle
Barbara Liskov 陈述的原则说 使用基 classes 引用指针的函数必须能够在不知情的情况下使用派生 classes 的对象。这是一个很好的设计原则,因为当一个函数不符合 LSP 时,它必须知道基 class.
所有可能的导数当您考虑违反该原则的后果时,该原则的重要性就显而易见了。让我们举个例子。假设我们正在开发一个处理形状的程序。我们已经创建了 class Rectangle,并且在设计 class Square 时,我们认为很自然地继承自 class Rectangle:
使用 C++ 我们会写:
class Rectangle
{
public:
virtual void SetWidth(const double w) {width_=w;}
virtual void SetHeight(const double h) {height_=h;}
virtual double GetHeight() const {return height_;}
virtual double GetWidth() const {return width_;}
private:
double width_;
double height_;
};
class Square : public Rectangle
{
public:
virtual void SetWidth(const double w) {
Rectangle::SetWidth(w);
Rectangle::SetHeight(w);
}
virtual void SetHeight(const double h) {
Rectangle::SetWidth(w);
Rectangle::SetHeight(h);
}
};
void function (Rectangle& r)
{
r.SetWidth(5);
r.SetHeight(4);
assert(r.GetWidth() * r.GetHeight()) == 20);
}
从示例中可以清楚地看出,断言(和 Liskov 项目)被违反了,因为矩形有一个 属性(身高和体重大小的独立性),它不坚持一个广场。
要回答您的问题,您应该推理适用于基础 class 但不适用于派生 class 的属性。如果界面设计得很好(看起来),你将找不到任何东西,你将能够用任何派生的 class.
替换基础 class恕我直言,Square
class 的问题是违反了 Rectangle
的合同:
- 对于
Rectangle
,Width
和Height
属性是独立的,因此如果您更改一个,另一个将保持原样 Square
通过更改每个 setter 中的两个属性违反了此合同
- 如果我们将
Square
传递给期望Rectangle
的代码,并在Width
和Height
不相关的前提下进行计数,那么我们就会遇到问题
我们还违反了历史规则(参见维基百科的文章Liskov Substitution Principle)。听起来很奇怪我将删除这些 class 之间的继承关系并引入一个 Shape
基础 class,因为那些拖车很难坚持 LS 原则。