Лисковская замена пустот и ослабленных предпосылок

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

1) Является ли следующее нарушение LSP?

class Base
{
   public virtual void UpdateUI()
   { 
     //documented: immediately redraws UI completely
   }  
}

class Component: Base
{
  public override UpdateUI
  {
    if (Time.Seconds % 10 ==0)  //updates only every ten seconds
    {
      //drawing logic
    }
  }
}

В моем понимании описание кода в базовом классе представляет собой контракт, ожидаемое поведение, которое нарушено в подтипе.

2) Почему нарушение поведения не имеет значения для ослабления предусловия?

class Human
{
  public virtual void DoSomething(int age)
  {
     //precondition - age < 100
  {

}

class Cyborg : Human
{
  public virtual void DoSomething(int age)
  {
     //precondition - age < 200
  {
}

Класс Cyborg ослабил предварительное условие, которое разрешено. Для допустимых аргументов замена работает хорошо. Всякий раз, когда у меня есть объект Human, можно использовать объект Cyborg. Но что, если у меня будет такой тест: Человек (110) - должен терпеть неудачу, аргумент должен быть <100

Когда я заменю Cyborg, тест пройдет. То есть поведение изменилось. Почему это разрешено?

3 голоса | спросил user970696 20 Jpm1000000pmSat, 20 Jan 2018 13:38:43 +030018 2018, 13:38:43

3 ответа


3

К очень интересному ответу CandleOrange, я хотел бы добавить, что:

  • Не удалось укрепить Preconditons означает , что можно сделать с помощью супертипа, можно сделать с помощью подтипа
  • Постусловия не могут быть ослаблены, а инварианты должны быть сохранены. означает то, что гарантируется супертипом, гарантируется подтипом

Я не согласен, однако, с его выводами для случая 1.

Случай 2: это нормально!

Следующий код работает для супертипа. Если ваш код соответствует LSP, все, что работает для супертипа, работает для подтипа. Итак, вы в порядке:

 // suppose x is a Human or a Cyborg
 x.DoSomething (50);  // if it works for a Human, it should work for a cyborg

Внимание: Если предварительные условия супертипа не выполнены, вы не можете сказать ничего о том, должно ли оно работать или нет для подтипа. Это не говорит LSP, но является явным следствием отношения импликации (т.е. от p подразумевает q , вы можете вывести, что non q подразумевает p , но вы не можете вывести ничего из non p ):

// suppose x is a Human or a Cyborg
x.DoSomething (150);   // Id doesn't even work for a Human,
                       // so we don't care if it works for a Cyborg

Случай 2: вы можете быть смущены?

Теперь все будет совершенно иначе, если вы ожидаете какого-то исключения или неудачи для людей за определенный период. Но тогда вы больше не думаете о предварительных условиях, а о постусловиях. И подтип не позволяет ослабить его:

class Human
{
   public virtual void DoSomething(int age)
   {
      // post condition:  exception thrown if age >= 100
   {
  }

class Cyborg : Human
{
  public virtual void DoSomething(int age)
  {
      // post condition:  exception thrown if age >= 200  (!!! INVALID IN LSP)
  {
}

Случай 1: это не нормально, или это?

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

Но с точки зрения контракта это совсем не нормально: немедленное перерисовка - это постусловие , и подтип не позволяет ослабить его! Таким образом, это не соответствует LSP.

Чтобы проиллюстрировать проблему. Представьте, что Base и Component являются частью хирургический робот, и что Base будет роботизированной рукой, а Component рука, оборудованная скальпелем. Если гарантией хирурга является то, что пользовательский интерфейс с изображением камеры получает обновление в любое время в реальном времени, Component может привести к смерть пациента. Поэтому вы должны серьезно относиться к постусловиям!

ответил Christophe 20 Jpm1000000pmSat, 20 Jan 2018 15:33:25 +030018 2018, 15:33:25
3

Я отвечу на ваши вопросы в обратном порядке. Из 2)

  

Когда я заменю Cyborg, тест пройдет. То есть поведение изменилось. Почему это разрешено?

Поскольку принцип подстановки не требует, чтобы такое поведение не изменилось.

Подстановка Лискова четыре поведенческих условия

  
  • Предварительные условия не могут быть усилены в подтипе.
  •   
  • Постусловия не могут быть ослаблены в подтипе.
  •   
  • Инварианты супертипа должны быть сохранены в подтипе.
  •   
  • Ограничение истории («правило истории»). Объекты считаются модифицируемыми только через их методы (инкапсуляция). Поскольку подтипы могут вводить методы, отсутствующие в супертипе, введение этих методов может допускать изменения состояния в подтипе, которые недопустимы в супертипе. Ограничение истории запрещает это. Это был новый элемент, представленный Лисковым и Крылом. Нарушение этого ограничения можно проиллюстрировать путем определения изменяемой точки как подтипа неизменяемой точки. Это является нарушением ограничения истории, поскольку в истории неизменяемой точки состояние всегда остается неизменным после создания, поэтому оно не может включать историю изменчивой точки в целом. Однако поля, добавленные в подтип, могут быть безопасно изменены, поскольку они не наблюдаются с помощью методов супертипа. Таким образом, можно получить круг с неподвижным центром, но изменяемый радиус от неизменяемой точки, не нарушая LSP.
  •   

Википедия: принцип замещения Лискова

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

Вот почему я также сказал, что 1) не является нарушением. Здесь нет ничего нового, что клиентский код должен был бы заботиться.

ответил candied_orange 20 Jpm1000000pmSat, 20 Jan 2018 14:07:07 +030018 2018, 14:07:07
-3

Лисков применим к простому наследованию, а не к полиморфизму. Лиськов утверждает, что свойства и методы, существующие в базовом классе, должны быть доступны еще для любых потомков.

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

ответил Martin Maat 20 Jpm1000000pmSat, 20 Jan 2018 17:18:12 +030018 2018, 17:18:12

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

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132