【问题标题】:Solid principle: LSP violation坚实的原则:违反LSP
【发布时间】:2017-05-09 19:38:08
【问题描述】:

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

非常感谢您的宝贵时间

【问题讨论】:

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


    【解决方案1】:

    这是您可以真正找到源头的美妙情况之一。 Barbara Liskov 的原始论文是可用的,并且非常容易阅读和阅读。 http://csnell.net/computerscience/Liskov_subtypes.pdf

    你展示的所有四个GetResult 方法都接受两个整数作为输入,要么返回一个整数,要么抛出一个异常。在这方面他们都具有相同的行为,因此可以说 LSP 完全满意。如果没有显式前置/后置条件或不变量,就无法真正描述代码。

    您的示例中缺少的是合同。 GetResult 要求从它的调用者那里得到什么?它保证它会产生什么?

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

    如果合约保证 x = 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
        }
    }
    

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

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

    以上满足LSP。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-06
      • 2013-06-15
      • 1970-01-01
      • 2013-01-01
      • 1970-01-01
      • 2014-08-16
      • 1970-01-01
      • 2017-03-17
      相关资源
      最近更新 更多