了解合同和Liskov替代原则

时间:2015-12-14 13:17:33

标签: design-patterns liskov-substitution-principle

考虑图表:

enter image description here

Collection - 一个抽象类,包含所有其他类的共同部分:抽象函数 将整数放入集合中并检查集合是否为空。

Bag - 一个将集合实现为哈希的类,您可以在其中轻松找到元素。 每个元素可能出现不止一次

我需要确定这种继承关系是否保持Liskov Substitution原则。

所以,我首先检查一下这个函数的签名是否相同。 (put()和isEmpty())。 之后,我检查两者的方法合约是否相同,但我可以签订抽象类合同吗?我无法创造'它。 如果它们是,那么说这两个类维护LSP是否足够? 还需要别的东西?

2 个答案:

答案 0 :(得分:1)

简介

Liskov Substitution principle是良好的面向对象设计的5个基本原则之一(AKA SOLID):

Barbara Liskov所说的原则是使用对基类的引用指针的函数必须能够使用派生类的对象而不知道它。这是一个很好的设计原则,因为当函数不符合LSP时,它必须知道基类的所有可能的导数。

当你考虑违反它的后果时,这个原则的重要性就变得很明显了。让我们举个例子。假设我们正在开发一个处理形状的程序。我们已经创建了类Rectangle,在设计类Square时,我们认为继承自Rectangle类是很自然的:

example of Liskov violation

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

从示例中可以清楚地看出,由于Rectangle具有不适用于Square的属性(高度和重量大小的独立性),因此违反了断言(和Liskov项目)。 / p>

要回答您的问题,您应该推断为基类保留的属性,而不是派生类的属性。如果界面设计得很好(看起来像),你将找不到任何东西,你将能够用任何派生类替换基类。

答案 1 :(得分:1)

问题,恕我直言,Square类正在破坏Rectangle的合同:

  • 对于RectangleWidthHeight属性是独立的,因此如果您更改其中一个,则其他属性保持不变
  • Square通过更改每个setter中的两个属性违反了此合同
  • 如果我们将Square传递给期望Rectangle的代码并指望WidthHeight不相关的前提,那么我们就会遇到问题

此外,我们违反了历史规则(请参阅维基百科的文章Liskov Substitution Principle)。听起来很奇怪我会删除这些类之间的继承关系并引入一个Shape基类,因为这些基类很难坚持LS原则。