Что является примером принципа подстановки Лискова?

Измените несколько файлов по расширению на UTF-8 без спецификации:

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False)
foreach($i in ls -recurse -filter "*.java") {
    $MyFile = Get-Content $i.fullname 
    [System.IO.File]::WriteAllLines($i.fullname, $MyFile, $Utf8NoBomEncoding)
}
798
задан Steve Chambers 1 September 2017 в 15:57
поделиться

9 ответов

Принцип замены Лисков (LSP, ) является понятием в Объектно-ориентированном программировании, которое указывает:

Функции, которые используют указатели или ссылки на базовые классы, должны быть в состоянии использовать объекты производных классов, не зная это.

В его сердечном LSP об интерфейсах и контрактах, а также как решить, когда расширить класс по сравнению с использованием другая стратегия, такая как состав для достижения цели.

самый эффективный способ, которым я видел, чтобы проиллюстрировать этот тезис, был в [1 122] Главный Первый OOA& D. Они представляют сценарий, где Вы - разработчик на проекте создать платформу для стратегических игр.

Они представляют класс, который представляет плату, которая похожа на это:

Class Diagram

Все методы берут координаты X и Y в качестве параметров для определения положения мозаики в двухмерной антенной решетке Tiles. Это позволит разработчику игр управлять единицами в плате в ходе игры.

книга продолжает изменять требования, чтобы сказать, что игровая основа должна также поддерживать 3D игровые доски для размещения игр, которые имеют полет. Так ThreeDBoard класс представлен, который расширяется Board.

На первый взгляд это походит на хорошее решение. Board обеспечивает и Height и Width, свойства и ThreeDBoard обеспечивают ось Z.

то, Где это ломается, - когда Вы смотрите на всех других участников, наследованных от Board. Методы для AddUnit, GetTile, GetUnits и так далее, все берут оба параметра X и Y в Board класс, но ThreeDBoard потребности параметр Z также.

, Таким образом, необходимо реализовать те методы снова с параметром Z. Параметр Z не имеет никакого контекста к Board класс и унаследованные методы от Board, класс теряет их значение. Единица кода, пытающегося использовать ThreeDBoard класс как его базовый класс Board, была бы очень неудачливой.

, Возможно, мы должны найти другой подход. Вместо того, чтобы расшириться Board, ThreeDBoard должен состоять из [1 119] объекты. Один Board объект на единицу оси Z.

Это позволяет нам использовать хорошие объектно-ориентированные принципы как инкапсуляция и повторное использование, и doesn’t нарушают LSP.

444
ответ дан Community 1 September 2017 в 15:57
поделиться

Был бы, реализовывая ThreeDBoard с точки зрения массива Совета это быть полезным?

, Возможно, можно хотеть рассматривать части ThreeDBoard в различных плоскостях как Совет. В этом случае можно хотеть абстрагировать интерфейс (или абстрактный класс) для Совета для обеспечения нескольких реализаций.

С точки зрения внешнего интерфейса, Вы могли бы хотеть факторизовать Интерфейс платы и для TwoDBoard и для ThreeDBoard (хотя ни одно из вышеупомянутого соответствия методов).

4
ответ дан Tom Hawtin - tackline 1 September 2017 в 15:57
поделиться

LSP касается инвариантов.

классический пример дан следующим объявлением псевдокода (опущенные реализации):

class Rectangle {
    int getHeight()
    void setHeight(int value)
    int getWidth()
    void setWidth(int value)
}

class Square : Rectangle { }

Теперь у нас есть проблема, хотя интерфейс соответствует. Причина состоит в том, что мы нарушили инварианты, происходящие от математического определения квадратов и прямоугольников. Путем методы get и работа методов set, Rectangle должны удовлетворить следующий инвариант:

void invariant(Rectangle r) {
    r.setHeight(200)
    r.setWidth(100)
    assert(r.getHeight() == 200 and r.getWidth() == 100)
}

Однако этот инвариант должен быть нарушенным корректной реализацией Square, поэтому это не допустимая замена Rectangle.

122
ответ дан Konrad Rudolph 1 September 2017 в 15:57
поделиться

У Robert Martin есть превосходное статья о принципе замены Лисков . Это обсуждает тонкие и not-so-subtle пути, которыми может быть нарушен принцип.

Некоторые соответствующие части статьи (отмечают, что второй пример в большой степени сжат):

А Простой Пример Нарушения LSP

Одно из самых явных нарушений этого принципа является использованием Информации о типах во время выполнения (RTTI) C++ для выбора функции, основанной на типе объекта. т.е.:

void DrawShape(const Shape& s)
{
  if (typeid(s) == typeid(Square))
    DrawSquare(static_cast<Square&>(s)); 
  else if (typeid(s) == typeid(Circle))
    DrawCircle(static_cast<Circle&>(s));
}

Очевидно эти DrawShape функция плохо формируется. Это должно знать о каждой возможной производной Shape класс, и это должно быть изменено каждый раз, когда новые производные Shape создаются. Действительно, многие просматривают структуру этой функции как анафема на Объектно-ориентированное проектирование.

Квадрат и Прямоугольник, Более тонкое Нарушение.

Однако существуют другие, намного более тонкие, способы нарушить LSP. Рассмотрите заявление, которое использует Rectangle класс, как описано ниже:

class Rectangle
{
  public:
    void SetWidth(double w) {itsWidth=w;}
    void SetHeight(double h) {itsHeight=w;}
    double GetHeight() const {return itsHeight;}
    double GetWidth() const {return itsWidth;}
  private:
    double itsWidth;
    double itsHeight;
};

[...] Предполагают, что однажды пользователи требуют способности управлять квадратами в дополнение к прямоугольникам. [...]

Очевидно, квадрат является прямоугольником для всех нормальных намерений и целей. Так как отношения ISA содержат, логично смоделировать Square класс, как получаемый от Rectangle. [...]

Square наследуется SetWidth и SetHeight функции. Эти функции являются совершенно несоответствующими для Square, так как ширина и высота квадрата идентичны. Это должно быть значительной подсказкой, что существует проблема с дизайном. Однако существует способ обойти проблему. Мы могли переопределить SetWidth и SetHeight [...]

, Но рассмотреть следующую функцию:

void f(Rectangle& r)
{
  r.SetWidth(32); // calls Rectangle::SetWidth
}

, Если мы передаем ссылку на Square объект в эту функцию, эти Square, объект будет поврежден потому что высота won’t быть измененным. Это - четкое нарушение LSP. Функция не работает на производные своих аргументов.

[...]

71
ответ дан shA.t 1 September 2017 в 15:57
поделиться

LSP является правилом о контракте классов: если базовый класс удовлетворяет контракт, то LSP производные классы должны также удовлетворить тот контракт.

В Псевдо-Python

class Base:
   def Foo(self, arg): 
       # *... do stuff*

class Derived(Base):
   def Foo(self, arg):
       # *... do stuff*

удовлетворяет LSP, если каждый раз Вы называете Нечто на Производном объекте, это дает точно те же результаты как вызов Нечто на Базовом объекте, пока аргумент является тем же.

20
ответ дан Charlie Martin 1 September 2017 в 15:57
поделиться

Функции, которые используют указатели или ссылки на базовые классы, должны быть в состоянии использовать объекты производных классов, не зная это.

, Когда я сначала читал о LSP, я предположил, что это было предназначено в очень строгом смысле, по существу приравнивая его для взаимодействия через интерфейс с реализацией и безопасным с точки зрения типов броском. Который означал бы, что LSP или обеспечен или не самим языком. Например, в этом строгом смысле, ThreeDBoard, конечно, substitutable для Совета, насколько компилятор затронут.

После чтения больше на понятии, хотя я нашел, что LSP обычно интерпретируется более широко, чем это.

Короче говоря, то, что это означает для клиентского кода "знать", что объект позади указателя имеет производный тип, а не тип указателя, не ограничивается безопасностью типов. Соблюдение LSP является также тестируемым посредством зондирования объектов фактическое поведение. Таким образом, исследуя влияние состояния объекта и аргументов метода на результатах вызовов метода или типах исключений, выданных от объекта.

Возвращение к примеру снова, в теории методы Совета могут быть сделаны работать просто великолепно на ThreeDBoard. На практике однако будет очень трудно предотвратить различия в поведении, которое клиент не может обработать правильно, не создавая помехи функциональности, которую ThreeDBoard предназначается для добавления.

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

19
ответ дан Peter Mortensen 1 September 2017 в 15:57
поделиться

LSP иллюстрирования яркого примера (данный Дядей Входят в подкаст, который я недавно услышал) был то, как иногда что-то, что звучит правильным на естественном языке, не вполне работает в коде.

В математике, Square Rectangle. Действительно это - специализация прямоугольника. "", Заставляет Вас хотеть смоделировать это с наследованием. Однако, если в коде Вы сделали Square, происходят от Rectangle, тогда Square должно быть применимым где угодно, Вы ожидаете Rectangle. Это делает для некоторого странного поведения.

Предполагают, что Вы имели SetWidth и SetHeight методы на Вашем Rectangle базовый класс; это кажется совершенно логичным. Однако, если Ваш Rectangle ссылка указала Square, то SetWidth и SetHeight не имеет смысла, потому что установка той изменила бы другой для соответствия ему. В этом случае Square проваливает Тест Замены Liskov с [1 114], и абстракция наличия Square наследовались от [1 116], плохой.

enter image description here

Y'all должен проверить другое бесценное ТВЕРДЫЕ Принципы Мотивационные Плакаты .

782
ответ дан Zoe the transgirl 2 September 2017 в 01:57
поделиться
  • 1
    Я предполагаю для меня, это чувствует себя более естественным для (1+2) для константы, потому что это - временный результат. – dchhetri 2 January 2013 в 01:26

Эта формулировка LSP слишком сильна:

Если для каждого объекта o1 типа S существует объект o2 типа T, таким образом, что для всех программ P defined с точки зрения T, поведение P неизменно, когда o1 заменяют o2, то S является подтипом T.

Который в основном означает, что S - другой, полностью инкапсулировавшая реализация той же самой вещи как T. И я мог быть полужирным и решить, что производительность является частью поведения P...

Так, в основном любое использование позднего связывания нарушает LSP. Это - смысл OO к получить другое поведение, когда мы заменяем объектом одного вида для одного из другого вида!

Формулировка, процитированная Википедией, лучше, так как свойство зависит от контекста и не обязательно включает целое поведение программы.

9
ответ дан 22 November 2019 в 21:11
поделиться

принцип замены Лисков

  • переопределенный метод shouldn’t остается пустым
  • , переопределенный метод shouldn’t бросает ошибку
  • , Базовый класс или интерфейсное поведение не должны идти для модификации (переделывают) как из-за поведений производного класса.
1
ответ дан 22 November 2019 в 21:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: