0
$\begingroup$

From Agile Principles, Patterns, and Practices in C# by Robert Martin,

Listing 10-1. A violation of LSP causing a violation of 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() violates OCP. It must know about every possible derivative of the Shape class, and it must be changed whenever new derivatives of Shape are created.

The fact that Square and Circle cannot be substituted for Shape is a violation of LSP. This violation forced the violation of OCP by DrawShape . Thus, a violation of LSP is a latent violation of OCP.

How does it violate Liskov substitution principle (LSP)? (In particular, why can Square and Circle not be substituted for Shape?)

How does violation of LSP cause violation of the open-closed principle (OCP)? (I can see it directly violates OCP, but I can't understand how violation of LSP causes violation of OCP.)

Thanks.

$\endgroup$
1
$\begingroup$

The listing does not appear to be a good illustration of LSP violation, since no clear reason is given why the draw method couldn't be a virtual method of Shape. If it was, then there would be no need to do any type query in drawShape: calling draw would work for all subclasses of Shape.

The Liskov Substitution Principle is typically described in terms of contracts. For example: the contract of the square root function requires that the input is non-negative and ensures that the square of the output is equal to the input.

Hence, an overridden method in a subclass is substitutable for the corresponding method in the superclass if it:

  • requires no more of its input than the superclass contract
  • ensures no less of its output

A subclass is LSP-conforming if all its methods are LSP conforming.

Hence, a better illustration of LSP violation might be a rotate method, in which all shapes except Square can be rotated by any angle, but Square is only rotatable by some multiple of 90% (such a requirement would actually demonstrate that the Square should not be a subclass of Shape).

In general, LSP violation causes OCP violation because there is no meaningful action that a 'closed' function can take without needing knowledge of specific subclasses in the type hierarchy.

The standard example of this is to have a Circle class be a subclass of an Ellipse class that has a resize(xaxis,yaxis) method. Unless the semantics of resize are so diluted as to be largely useless ('maybe resize yourself or throw an exception') then subclasses can end up in a state that is inconsistent with anything meaningful/useful to the 'closed' function that is calling its methods.

A good discussion of the LSP (including the 'Circle-Ellipse' issue) can be found here

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.