坚实的原则:LSP违规

时间:2017-05-09 19:38:09

标签: oop solid-principles liskov-substitution-principle

我正在尝试以正确的方式学习OOP和OOD原则。我想对Liskov替换原则及其PRE和POST条件做一些澄清。我在这里阅读了一些主题,http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod和其他地方的一些文章。

我已经编写了一个简单的基类和几个示例子类,我对其中的前置和后置条件进行了假设,我想知道它们是否正确。 注释行是我的想法:它是否违反了PRE和POST条件。

public abstract class BaseClass
{
    public virtual int GetResult(int x, int y)
    {
        if (x > 10 && y < 20)
        {
            return y - x;
        }
        throw new Exception();
    }
}

public class LSPExample1 : BaseClass
{
    public override int GetResult(int x, int y)
    {
        // PRE: weakened pre condition is ok
        if (x > 10 && y <= 15)
        {
            // POST: Is it ok? because the available result range is narrowed by y <= 15 
            return y - x;
        }
        throw new Exception();
    }
}

public class LSPExample2 : BaseClass
{
    public override int GetResult(int x, int y)
    {
        // PRE: Same as base - OK
        if (x > 10 && y < 20)
        {
            // POST: I assume it's bad because of parameters place changed in substraction ?
            return x-y;
        }
        throw new Exception();
    }
}

public class LSPExample3 : BaseClass
{
    public override int GetResult(int x, int y)
    {
        // PRE Condition is bad (Strenghtened) because of (X >5) and (Y>20) ?
        if (x > 5 && y > 20)
        {
            // POST condition is ok because base class do substraction which is weaker than multiplication ?
            return x * y;
        }
        throw new Exception();
    }
}

我非常感谢你的时间

1 个答案:

答案 0 :(得分:5)

这是您可以实际访问来源的精彩情况之一。 Barbara Liskov的原始论文可用且易于阅读且易于阅读。 http://csnell.net/computerscience/Liskov_subtypes.pdf

您显示的所有四个GetResult方法都接受两个整数作为输入,并返回一个整数或抛出异常。在这方面,它们都具有相同的行为,因此可以说LSP完全满足。如果没有显式的前/后条件或不变量,那么关于代码的其他任何事情都无法说明。

你的例子中缺少的是合同。来自其来电者GetResult 需要的内容是什么保证会产生什么?

例如,如果合同保证返回值等于y-x,那么示例2和3将失败合同,从而破坏LSP。但是,如果唯一的保证是返回值是Int或Exception,那么它们都会通过。

如果合同保证在x&lt; = 10 ||时将抛出异常y> = 20然后实施例1&amp; 3破LSP。如果唯一的保证是该方法将返回一个Int或抛出一个异常,那么它们都满足它。

代码无法告诉您保证是什么,它只能告诉您希望代码执行。

由于我获得了大量的投票,我将添加一个示例(伪代码):

class Line {
    int start
    int end
    int length() { return end - start } // ensure: length = end - start

    void updateLength(int value) {
        end = start + value
        // ensure: this.length == value
    }
}

两个函数中的每一个中的“ensure”子句都是关于调用函数后Line对象状态的保证。只要我满足子类中的保证,我的子类就会符合LSP。例如:

class Example1: Line {
    void updateLength(int value) {
        start = end - value
    }
}

以上内容满足LSP。

如果Line的updateLength函数也有一个“this.start unchanged”的ensure子句,那么我的子类将不满足LSP。