【问题标题】:How does this example violate LSP, which then causes violation of OCP?此示例如何违反 LSP,从而导致违反 OCP?
【发布时间】:2019-07-05 00:29:25
【问题描述】:

来自罗伯特·马丁的Agile Principles, Patterns, and Practices in C#

清单 10-1。违反LSP导致违反OCP

struct    Point   {double x,  y;}
public    enum    ShapeType   {square,    circle};
public    class   Shape
{
      private ShapeType   type;
      public  Shape(ShapeType t){type =   t;}
      public  static  void    DrawShape(Shape s)
      {
              if(s.type   ==  ShapeType.square)
                      (s  as  Square).Draw();
              else    if(s.type   ==  ShapeType.circle)
                      (s  as  Circle).Draw();
      }
}
public    class   Circle  :   Shape
{
      private Point   center;
      private double  radius;
      public  Circle()    :   base(ShapeType.circle)  {}
      public  void    Draw()  {/* draws   the circle  */}
}
public    class   Square  :   Shape
{
      private Point   topLeft;
      private double  side;
      public  Square()    :   base(ShapeType.square)  {}
      public  void    Draw()  {/* draws   the square  */}
}

DrawShape() 违反 OCP。它 必须知道 Shape 类的每一个可能的派生词, 并且每当 Shape 的新衍生物出现时,它必须被改变 已创建。

SquareCircle 不能替代 Shape 的事实是 违反 LSP。这种违规行为迫使违反 OCP 绘图形状。因此,违反 LSP 是潜在违反 OCP。

它如何违反 LSP? (特别是SquareCircle为什么不能代替Shape?)

违反 LSP 如何导致违反 OCP? (我可以看到它直接违反了OCP,但是我无法理解违反LSP如何导致违反OCP。)

【问题讨论】:

    标签: solid-principles design-principles liskov-substitution-principle open-closed-principle


    【解决方案1】:

    这不是明显或典型的 LSP 违规,有人可能会争辩说它根本不是 LSP 违规,但这是我的解释:

    期望Shape 由其type 字段描述。当DrawShape 收到Shape 对象时,可能会发生一些事情之一。根据type 字段的值,它可以尝试将对象转换为Square 并调用其Draw 函数,或者尝试将其转换为Circle 到同一端。但是,对于任意Shape,这不能保证按预期工作。具体来说,只有当对象的动态类型实际上匹配它的type 字段的语义含义时,它才会起作用。如果type 字段与其动态类型不匹配,则在尝试执行动态转换时会发生异常。这是给定Shape 对象时DrawShape 的行为。

    但是,给定 SquareCircle,会有不同的期望。具体来说,该函数应始终无异常地执行一条或另一条路径,因为type 字段的语义含义将始终与对象的动态类型匹配。

    换句话说,您可以认为DrawShape 函数对于Shape 对象有四个有趣的执行路径:在动态Circle 转换时发生异常,在动态Square 转换时发生异常,或成功执行 SquareCircle 绘制函数。

    当一个孩子被替换时,前面提到的两条路径不再可能,对于给定的孩子,只有一条路径是可能的。

    或者,可以说不存在 LSP 违规;该函数仍然以与父代相同的方式“作用”子代。 SquaresCircles只是多了一个含义,就是type字段肯定会匹配对象的动态类型,限制了函数在运行时的执行结果。虽然它可以被认为是改变函数期望,但也可以简单地被认为是强加一个先决条件。

    编辑

    我想我忘了回答部分问题:这种所谓的 LSP 违规“导致”OCP 违规的原因是因为函数逻辑导致 ShapesSquaresCircles 的行为不同,作为对孩子的动态转换,与强制 Shape 类依赖其孩子的逻辑完全相同。因此,通过使用关于子类的条件逻辑来违反 LSP,它反过来又违反了 OCP。

    我不知道我自己是否真的会把这种特殊情况称为“因果关系”的情况,就像简单的事件交叉一样,但也许这是作者的意图。

    【讨论】:

    • 当你说“如果类型字段与其动态类型不匹配”时,为什么会发生这种情况?
    • 说实话,我不确定我的意思。在给定的示例中,对于定义良好的程序,这种情况永远不会发生。好吧,如果用type == ShapeType.square 引入了一个new 派生类,那将破坏Shape 类。但这与SquareCircle 以及它们是否可以替代Shape 无关。因此,在我看来,通过引入具有错误 type 值的新派生类,存在明显的 OCP 违规和 LSP 违规的巨大可能性,但我不同意实际上中存在任何 LSP 违规给定的例子。
    猜你喜欢
    • 2013-06-15
    • 1970-01-01
    • 1970-01-01
    • 2017-03-17
    • 2017-02-11
    • 1970-01-01
    • 2019-08-21
    • 2016-03-28
    • 1970-01-01
    相关资源
    最近更新 更多