Принцип подстановки Барбары Лисков
От: Alex Dav Россия  
Дата: 27.12.06 12:24
Оценка:
Расскажите, плиз, только попроще и по русски
Спасибо.
Re: Принцип подстановки Барбары Лисков
От: VladGalkin Украина  
Дата: 27.12.06 12:35
Оценка:
Здравствуйте, Alex Dav, Вы писали:

AD>Расскажите, плиз, только попроще и по русски

AD>Спасибо.

А что там военного? Не совсем по русски здесь.
Выдержка:

FUNCTIONS THAT USE POINTERS OR REFERENCES TO BASE CLASSES MUST BE ABLE TO USE OBJECTS OF DERIVED CLASSES WITHOUT KNOWING IT
Above is a paraphrase of the Liskov Substitution Principle (LSP). Barbara Liskov first
it as follows nearly 8 years ago. What is wanted here is something like the following substitution property: If
for each object o of type S there is an object o of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o is substituted for o then S is a subtype of T.


И пример кода нарушающего этот принцип:

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));
}
ДЭ!
Re: Принцип подстановки Барбары Лисков
От: Sshur Россия http://shurygin-sergey.livejournal.com
Дата: 27.12.06 12:47
Оценка:
Здравствуйте, Alex Dav, Вы писали:

AD>Расскажите, плиз, только попроще и по русски

AD>Спасибо.


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

"Не следует преумножать сущности сверх необходимости" (с) Оккам
Re: Принцип подстановки Барбары Лисков
От: LaptevVV Россия  
Дата: 27.12.06 13:42
Оценка:
Здравствуйте, Alex Dav, Вы писали:

AD>Расскажите, плиз, только попроще и по русски

В ООП примерно так: Объект производного типа является также и объектом базового типа; на место объекта базового типа всегда может быть подставлен объект производного типа... То же касается и указателей и ссылок...
Обратное — неверно! Всякий будильник (производный тип) является часами (базовый тип), но не всякие часы — будильник.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[2]: Принцип подстановки Барбары Лисков
От: Andrei N.Sobchuck Украина www.smalltalk.ru
Дата: 27.12.06 13:43
Оценка:
Здравствуйте, Sshur, Вы писали:

S> Чаще всего нарущения допусткаются в коде, использующем инфоррмацию о типах в runtime.


"Кто про что, а лысый про расческу." (с) Народная мудрость
http://www.smalltalk.ru | << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Я ненавижу Hibernate
Автор: Andrei N.Sobchuck
Дата: 08.01.08
!
Re[2]: Принцип подстановки Барбары Лисков
От: Alxndr Германия http://www.google.com/profiles/alexander.poluektov#buzz
Дата: 27.12.06 17:04
Оценка:
Здравствуйте, VladGalkin, Вы писали:

VG>Здравствуйте, Alex Dav, Вы писали:


VG>

VG>FUNCTIONS THAT USE POINTERS OR REFERENCES TO BASE CLASSES MUST BE ABLE TO USE OBJECTS OF DERIVED CLASSES WITHOUT KNOWING IT
VG>Above is a paraphrase of the Liskov Substitution Principle (LSP). Barbara Liskov first
VG>it as follows nearly 8 years ago. What is wanted here is something like the following substitution property: If
VG>for each object o of type S there is an object o of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o is substituted for o then S is a subtype of T.


VG>И пример кода нарушающего этот принцип:


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


Вообще-то это иллюстрация нарушения Open-Closed Principle. Похоже, к LSP пример иммет мало отношения

Классический пример нарушения — пресловутый квадрат, (не) являющийся подтипом прямоугольника (или наоборот )
Re[3]: Принцип подстановки Барбары Лисков
От: dottedmag Мальта http://dottedmag.net/
Дата: 27.12.06 18:11
Оценка: +1
Здравствуйте, Alxndr, Вы писали:

A>Вообще-то это иллюстрация нарушения Open-Closed Principle. Похоже, к LSP пример иммет мало отношения


Одно другому не мешает. LSP — это всего лишь OCP, применённый к классам.
Re[2]: Принцип подстановки Барбары Лисков
От: VladD2 Российская Империя www.nemerle.org
Дата: 28.12.06 00:51
Оценка: 6 (1)
Здравствуйте, VladGalkin, Вы писали:

VG>И пример кода нарушающего этот принцип:


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


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

Забавно было бы осудить насколько этот принцип вообще состоятелен? Не вызван ли он скудностью выразительных средств языка?

Я согласен, что если думать в понятиях ООП, то все выглядит более менее нормально. Но ведь можно думать и по другому!

Возьмем тот же паттерн Посетитель. Вся его суть заключается в попытке ранушить этот принцип. Мы как бы пытаемся изменить направление нашего взгляда на проблему. Вывести метод за пределы класса. Причем в ООЯ это получается очень неуклюже. А на языке с паттерн-матчингом просто и элегантно:
DrawShape(shape : Shape) : void
{
  | square is Square => DrawSquare(square);
  | circle is Circle => DrawCircle(circle);
    | _                => DoDefaultAction(shape);
}

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

В общем, эти прицыпы рассчитаны на ООП. А ООП не всегда лучший выбор. Так что по-моему не все так однозначно.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Принцип подстановки Барбары Лисков
От: VladD2 Российская Империя www.nemerle.org
Дата: 28.12.06 00:51
Оценка:
Здравствуйте, Sshur, Вы писали:

S>Введение в программу новых порожденных типов не должно изменять логику работы функционала, зависящего от базовых типов. Чаще всего нарущения допусткаются в коде, использующем инфоррмацию о типах в runtime.


На самом деле это догма. А по жизни могут быть разные приоритеты. Например, иногда просто нет возможности ввести виртуальные члены или не хочется создавать наследников. При этом внешнее решение может оказаться очень выгодным. Кое как проблему позволяет решить паттерн Посетитель, но увы у него море своих проблем.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Принцип подстановки Барбары Лисков
От: Sshur Россия http://shurygin-sergey.livejournal.com
Дата: 28.12.06 06:02
Оценка: :)
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, Sshur, Вы писали:


S>>Введение в программу новых порожденных типов не должно изменять логику работы функционала, зависящего от базовых типов. Чаще всего нарущения допусткаются в коде, использующем инфоррмацию о типах в runtime.


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



Нет, на самом деле это классика кун-фу
Автор: Sinclair
Дата: 19.01.06
.
Шурыгин Сергей

"Не следует преумножать сущности сверх необходимости" (с) Оккам
Re[4]: Принцип подстановки Барбары Лисков
От: gbear Россия  
Дата: 28.12.06 08:08
Оценка: -1
Здравствуйте, dottedmag, Вы писали:

D>Здравствуйте, Alxndr, Вы писали:


A>>Вообще-то это иллюстрация нарушения Open-Closed Principle. Похоже, к LSP пример иммет мало отношения


D>Одно другому не мешает. LSP — это всего лишь OCP, применённый к классам.


Ещё как мешает... LSP, вообще-то, имеет давольно опосредованное отношение к классам, как таковым, и к ООП — в частности. А Вы, скорее всего путаете, subtyping и subclassing.

---
С уважением, Сиваков Конастантин.
Re[3]: Принцип подстановки Барбары Лисков
От: VladGalkin Украина  
Дата: 28.12.06 08:29
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Кстати, всегда обсуждение подобных проблем у меня вызывало какое-то подспудное чувство несогласия.

VD>Познакомившись с паттерн-матчингом чуство сильно укрепилось. Я даже стал замечать, что порой нарушение ОО-традиций дает отличный VD>результат.

Чувство возникло и у меня (о pattern matching узнал угадайте сами при знакомстве с каким языком программирования ), однако я забил на него, поскольку надо четко осознавать что одним ООП и виртуальными методами все не исчерпывается, и профессионал должен хотя бы знать, хм, иные (я нарочно не написал "более мощные" ) концепции и средства (почему собственно сейчас и пытаюсь читать SICP )

VD>Забавно было бы осудить насколько этот принцип вообще состоятелен? Не вызван ли он скудностью выразительных средств языка?

VD>Я согласен, что если думать в понятиях ООП, то все выглядит более менее нормально. Но ведь можно думать и по другому!
Все зависит от точки зрения и концепции. В более простом, перефразированном виде концепция LSP звучит как

Subtypes must be substitutable for their base types

(С) Robert Martin. А при pattern matching такое определение, фактически, выкидывается. Но никто
и не говорит использовать LSP при pattern matching.

VD>Возьмем тот же паттерн Посетитель. Вся его суть заключается в попытке ранушить этот принцип. Мы как бы пытаемся изменить направление VD>нашего взгляда на проблему. Вывести метод за пределы класса. Причем в ООЯ это получается очень неуклюже. А на языке с VD>паттерн-матчингом просто и элегантно


Тут уже приводили цитату про паттерны GoF и динамические языки В данном случае она также справедлива на 99,9%.

VD>В общем, эти прицыпы рассчитаны на ООП. А ООП не всегда лучший выбор. Так что по-моему не все так однозначно.

VD>Тут уже пробегала цитата про паттерны GoF и динамические языки

Я, собственно, в контексте только ООП его и рассматривал, т.к. человек, видимо, хотел услышать ответ применительно к ООП , однако я рад таким развернутым замечаниям и дискуссии.
ДЭ!
Re: Принцип подстановки Барбары Лисков
От: gbear Россия  
Дата: 28.12.06 08:36
Оценка:
Здравствуйте, Alex Dav, Вы писали:

AD>Расскажите, плиз, только попроще и по русски

AD>Спасибо.

ЕМНИП, звучит он примерно так:


Если подстановка типа A, вместо типа B не приводит к изменению поведения программы P, то тип A, явялется подтипом B.


LSP, ЕМНИП, является [первой?] попыткой (не очень удачной, ИМХО) сформулировать положения т.н. behavioural subtyping.

В свое время имел неосторожность затронуть данный вопрос (см. Кто автор?
Автор: cranky
Дата: 30.07.05
)

---
С уважением, Сиваков Константин
Re[5]: Принцип подстановки Барбары Лисков
От: dottedmag Мальта http://dottedmag.net/
Дата: 28.12.06 08:44
Оценка:
Здравствуйте, gbear, Вы писали:

G> Ещё как мешает... LSP, вообще-то, имеет давольно опосредованное отношение к классам, как таковым, и к ООП — в частности. А Вы, скорее всего путаете, subtyping и subclassing.


Я прекрасно знаю разницу между subtyping и subclassing. А вам бы неплохо узнать, что в ОО-языках типы выражаются классами.
Re[3]: Принцип подстановки Барбары Лисков
От: IB Австрия http://rsdn.ru
Дата: 28.12.06 09:37
Оценка: +3
Здравствуйте, VladD2, Вы писали:

VD>На самом деле это догма.

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

VD> А по жизни могут быть разные приоритеты.

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

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

Не зацикливайся на ООП, говоря проще — код, который пользуется некоторым функционалом по заранее оговоренному контракту, не должен зависеть от конкретной реализации этого контракта.
... << RSDN@Home 1.2.0 alpha rev. 0>>
Мы уже победили, просто это еще не так заметно...
Re[2]: Принцип подстановки Барбары Лисков
От: Трурль  
Дата: 28.12.06 12:18
Оценка: +1
Здравствуйте, gbear, Вы писали:

G>LSP, ЕМНИП, является [первой?] попыткой (не очень удачной, ИМХО) сформулировать положения т.н. behavioural subtyping.


А именно поэтому говорить о нарушении этого принципа — примерно то же, что говорить о нарушении принципа наименьшего действия.
Re[2]: Принцип подстановки Барбары Лисков
От: Кирилл Лебедев Россия http://askofen.blogspot.com/
Дата: 28.12.06 15:06
Оценка: 5 (2)
Здравствуйте, LaptevVV, Вы писали:

LVV>Обратное — неверно! Всякий будильник (производный тип) является часами (базовый тип), но не всякие часы — будильник.


Я бы сказал несколько иначе. Существуют две функции:

1) Измерение времени.
2) Выдача сообщения (например, звонка) по сигналу.

Эти функции могут быть реализованы как в одном устройстве, так и в разных. Это я к тому, что далеко не всякий будильник является или может являться часами.
С уважением,
Кирилл Лебедев
Software Design blog — http://askofen.blogspot.ru/
Re[2]: Принцип подстановки Барбары Лисков
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 28.12.06 15:17
Оценка:
Здравствуйте, VladGalkin, Вы писали:

VG>И пример кода нарушающего этот принцип:

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


Странно, следующий код:

TYPE Message = ABSTRACT RECORD END;

...

PROCEDURE (this: MyObject) HandleMessage (VAR msg: Message);
BEGIN
WITH msg: KeyDownMsg DO ... | msg: MouseMoveMsg DO ... ELSE ... END
END HandleMessage;

вроде аналогичный, а ничего не нарушает
Re[3]: Принцип подстановки Барбары Лисков
От: VladGalkin Украина  
Дата: 28.12.06 15:42
Оценка: :))
Здравствуйте, Сергей Губанов, Вы писали:

СГ>Странно, следующий код:

СГ>TYPE Message = ABSTRACT RECORD END;
СГ>...
СГ>PROCEDURE (this: MyObject) HandleMessage (VAR msg: Message);
СГ>BEGIN
СГ> WITH msg: KeyDownMsg DO ... | msg: MouseMoveMsg DO ... ELSE ... END
СГ>END HandleMessage;

"И эти люди запрещают мне ковырятся в носу" (C) Это ж какой, пардон, "синтаксический оверхед", для всех этих msg: KeyDownMsg DO, вместо одного "msg.DoSomething()"

СГ>вроде аналогичный, а ничего не нарушает


Почему не нарушает? Потому что Вы так утверждаете? Я не знаю Oberon-2 (насколько я понял поддержка ООП появилась в нем). Вобщем почитал я про Oberon-2, и насколько понял, ООП там в понимании Вирта. Вот цитата:

Oberon-2 introduced limited reflection, methods ("type bound procedures"), and single inheritance ("type extension") without interfaces or mixins. Method calls were resolved at run-time, and limited polymorphism was possible by either overloading methods or by explicit typecase ("with" statement).


Дискутировать на тему Oberon, синтаксического оверхеда и Вирта я не намерен, говорю сразу.
ДЭ!
Re[4]: Принцип подстановки Барбары Лисков
От: Сергей Губанов Россия http://sergey-gubanov.livejournal.com/
Дата: 28.12.06 16:17
Оценка:
Здравствуйте, VladGalkin, Вы писали:

СГ>>TYPE Message = ABSTRACT RECORD END;

СГ>>...
СГ>>PROCEDURE (this: MyObject) HandleMessage (VAR msg: Message);
СГ>>BEGIN
СГ>> WITH msg: KeyDownMsg DO ... | msg: MouseMoveMsg DO ... ELSE ... END
СГ>>END HandleMessage;

VG>вместо одного "msg.DoSomething()"


У объекта сообщения Message не может быть метода DoSomething!!!
У него вообще ничего нету, это абстрактный тип-пустышка = ABSTRACT RECORD END.
Откуда сообщению знать как его будет обрабатывать какой-то конкретный получатель?
У сообщения не может быть метода его обработки — cообщение пришло, а уж как на него реагировать — это целиком в компетенции получателя.

Несколько примеров типов объектов сообщений:
  TYPE
    CursorMessage = ABSTRACT RECORD (RequestMessage)
      x, y: INTEGER
    END;

    DropMsg = RECORD (TransferMessage)
      view: Views.View;
      isSingle: BOOLEAN;
      w, h, rx, ry: INTEGER
    END;

    EditMsg = RECORD (RequestMessage)
      op: INTEGER;
      modifiers: SET;
      char: CHAR;
      view: Views.View;
      w, h: INTEGER;
      isSingle, clipboard: BOOLEAN
    END;

    MarkMsg = RECORD (Views.CtrlMessage)
      show, focus: BOOLEAN
    END;

    Message = Views.CtrlMessage;

    PageMsg = RECORD (Views.CtrlMessage)
      op, pageX, pageY: INTEGER;
      done, eox, eoy: BOOLEAN
    END;

    PollCursorMsg = RECORD (CursorMessage)
      cursor: INTEGER;
      modifiers: SET
    END;

    PollDropMsg = RECORD (TransferMessage)
      mark, show: BOOLEAN;
      type: Stores.TypeName;
      isSingle: BOOLEAN;
      w, h, rx, ry: INTEGER;
      dest: Views.Frame
    END;

    PollFocusMsg = EXTENSIBLE RECORD (Views.CtrlMessage)
      focus: Views.Frame
    END;

    PollOpsMsg = RECORD (Views.CtrlMessage)
      type, pasteType: Stores.TypeName;
      singleton: Views.View;
      selectable: BOOLEAN;
      valid: SET
    END;

    PollSectionMsg = RECORD (Views.CtrlMessage)
      focus, vertical: BOOLEAN;
      wholeSize, partSize, partPos: INTEGER;
      valid, done: BOOLEAN
    END;

    ReplaceViewMsg = RECORD (RequestMessage)
      old, new: Views.View
    END;

    RequestMessage = ABSTRACT RECORD (Views.CtrlMessage)
      requestFocus: BOOLEAN
    END;

    ScrollMsg = RECORD (Views.CtrlMessage)
      focus, vertical: BOOLEAN;
      op, pos: INTEGER;
      done: BOOLEAN
    END;

    SelectMsg = RECORD (Views.CtrlMessage)
      set: BOOLEAN
    END;

    TickMsg = RECORD (Views.CtrlMessage)
      tick: INTEGER
    END;

    TrackMsg = RECORD (CursorMessage)
      modifiers: SET
    END;

    TransferMessage = ABSTRACT RECORD (CursorMessage)
      source: Views.Frame;
      sourceX, sourceY: INTEGER
    END;

    WheelMsg = RECORD (CursorMessage)
      done: BOOLEAN;
      op, nofLines: INTEGER
    END;

Например, рассмотрим сообщение WheelMsg:

TYPE WheelMsg (CursorMessage)
Это сообщение посылается, когда колесо на мыши с колесом поворачивается.

done: BOOLEAN
Если отображение обрабатывает это сообщение, оно должно установить флаг done в TRUE.

op: INTEGER
Показывает, какой тип события колеса мыши произошел. Используются те же константы, что и для прокрутки, но допустимы только следующие из них: incPage, decPage, incLine and decLine.

nofLines: INTEGER nofLines >= 1
Если op или icnLine, или decLine, то nofLines показывает, сколько строк должно быть прокручено. Для incPage и decPage это значение не определено.



Кстати, это Component Pascal, а не Oberon-2, что видно по использованному ключевому слову ABSTRACT.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.