此示例如何违反LSP,从而导致违反OCP?

时间:2019-07-05 00:29:25

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

摘自Robert Martin的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违反了OCP   DrawShape。因此,违反LSP是对OCP的潜在违反。

它如何违反LSP? (尤其是为什么不能用SquareCircle代替Shape?)

违反LSP如何导致违反OCP? (我可以看到它直接违反了OCP,但我不明白如何违反LSP会导致违反OCP。)

1 个答案:

答案 0 :(得分:4)

这不是明显或典型的LSP违规,并且可能会认为它根本不是LSP违规,但这是我的解释:

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

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

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

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

或者,可以说没有LSP违反;该功能对于子代的替换仍然与对父代的“作用”相同。 CircleSquares只是有一个额外的含义,就是Circles字段一定会匹配对象的动态类型,从而限制了函数在运行时的执行结果。虽然可以认为它改变了功能期望,但也可以简单地认为它是施加先决条件。

修改

我想我忘了回答部分问题了:这种假定的LSP违规“导致” OCP违规的原因是因为导致typeShapes行为不同的函数逻辑而Squares是对子级的动态转换,是迫使Circles类依赖其子级的逻辑。因此,通过使用关于子类的条件逻辑违反LSP,反过来又违反了OCP。

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