Как этот пример нарушает LSP, который затем вызывает нарушение OCP? - PullRequest
4 голосов
/ 05 июля 2019

Из Гибких принципов, шаблонов и практик в 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 создано.

Тот факт, что Square и Circle не могут быть заменены на Shape, является нарушение ЛСП. Это нарушение заставило нарушить OCP DrawShape. Таким образом, нарушение LSP является скрытым нарушением OCP.

Как это нарушает LSP? (В частности, почему Square и Circle нельзя заменить на Shape?)

Как нарушение LSP вызывает нарушение OCP? (Я вижу, что это напрямую нарушает OCP, но я не могу понять, как нарушение LSP вызывает нарушение OCP.)

1 Ответ

4 голосов
/ 05 июля 2019

Это не является очевидным или типичным нарушением LSP, и можно утверждать, что это вообще не нарушение LSP, но вот моя интерпретация:

Ожидается, что Shape описывается своим полем type. Когда DrawShape получает объект Shape, может произойти одно из нескольких. В зависимости от значения поля type он может попытаться привести объект к Square и вызвать его функцию Draw, или попытаться привести его к Circle к тому же концу. Тем не менее, это не гарантированно работает как ожидалось для произвольного Shape. В частности, он будет работать только в том случае, если динамический тип объекта фактически соответствует семантическому значению его поля type. Если поле type не соответствует его динамическому типу, при попытке выполнить динамическое приведение произойдет исключение. Это поведение DrawShape данного Shape объекта.

Однако, учитывая Square или Circle, ожидалось другое. В частности, ожидается, что функция всегда будет выполнять тот или иной путь без исключения, поскольку семантическая импликация поля type всегда будет соответствовать динамическому типу объекта.

Другими словами, можно считать, что функция DrawShape имеет четыре интересных пути выполнения для объекта Shape: исключение, возникающее при динамическом приведении Circle, исключение, возникающее при динамическом приведении Square или успешное выполнение функций рисования Square или Circle.

Когда замещается дочерний элемент, первые два упомянутых пути больше не возможны, и для данного дочернего элемента возможен только один путь.

В качестве альтернативы можно утверждать, что нарушения LSP нет; функция по-прежнему «действует» для дочерней замены так же, как и для родительской. Squares и Circles просто имеют дополнительное значение, так как поле type будет точно соответствовать динамическому типу объекта, ограничивая результаты выполнения функции во время выполнения. Хотя это можно рассматривать как изменение ожиданий функции, его также можно рассматривать как навязывание предварительного условия.

Редактировать

Полагаю, я забыл ответить на часть вопроса: причина, по которой это предполагаемое нарушение LSP "вызывает" нарушение OCP, заключается в том, что логика функции, вызывающая различное поведение для Shapes и Squares и Circles будучи динамическим приведением к дочерним элементам, это та же логика, которая заставляет класс Shape зависеть от его дочерних элементов. Таким образом, нарушая LSP с помощью условной логики для подклассов, он, в свою очередь, нарушает OCP.

Я не знаю, действительно ли я сам назвал бы этот конкретный случай ситуацией "причинности", а не просто пересечением событий, но, возможно, это было намерение автора.

...