坚实的原则:LSP违规

Cik*_*nas 1 oop liskov-substitution-principle solid-principles

我正在尝试以正确的方式学习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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我真的很感激你的时间

Dan*_* T. 5

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

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

你的例子中缺少的是合同.什么是GetResult 需要从它的调用它有什么保证它会产生?

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

如果合同保证如果x <= 10 ||将抛出异常 y> = 20然后示例1和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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

class Example1: Line {
    void updateLength(int value) {
        start = end - value
    }
}
Run Code Online (Sandbox Code Playgroud)

以上满足LSP.

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