Как стать автором
Обновить

Комментарии 55

НЛО прилетело и опубликовало эту надпись здесь
Немного опередили. Хотел спросить, когда на хабре будем азбуку разжевывать?
НЛО прилетело и опубликовало эту надпись здесь
Смех смехом, но небольшой опрос среди знакомых показал, что не все в теме. Многие догадываются, что менять поведение методов — это плохо. А вот объяснить почему не могут. Так что вот такой топик-напоминание получился. Кому-то может быть и в новинку.
НЛО прилетело и опубликовало эту надпись здесь
Формулировки вида «2x2=4» просто выносят мой мозг. Какие-то непонятные крестики и черточки.
Давайте лучше разберемся, что это значит на практике. Пусть у нас есть набор совершенно реальных яблок, которые мы используем в нашем коде. Возьмите из набора сколько-то раз по сколько-то яблок. Если Вы брали 2 раза по 2 яблока, то поздравляю, Ваши действия соответствуют формулировке «2x2=4». Как видите, теперь все просто и понятно.


Вот как-то так :)
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
В профиле вы про себя очень правильно написали.
НЛО прилетело и опубликовало эту надпись здесь
спасибо, мне очень нравятся такие статьи, где рассказывают нормальным языком, а не пытаются вынести мозг!

к сожалению, про LSP да и многое другое я узнал из более «мозговыносящих» источников…
НЛО прилетело и опубликовало эту надпись здесь
На практике все на много сложнее из-за методов вроде equals. В результате возникают Generics и все сильно усложняется.
Автору спасибо за познавательную статью.
Тем кто говорит насчет азбуки, не забывайте, что не все посетители хабрахабра это опытные программисты со стажем.
Если что-то очевидно для нас с вами, то не факт что это будет так же очевидно для человека, который придет сюда из гугла по запросу «подстановка Барбары Лисков». И такая доступная статья может стать первым шагом в деле становления еще одного хорошего программиста :)
«Наследуемый класс должен дополнять» — Наследуемый? т.е. тот, кого наследуют, т.е. базовый?

Автор, поправь на «наследующий», плиз.
Ок. Подправил. Мой косяк.
«B, то если мы заменим все использования класса A на B, ничего не должно измениться в работе программы. Ведь класс B всего лишь расширяет функционал класса A. » — а как же на счёт того, что поведение программы должно «расшириться»? Мы ж заменяем A на B, на расширенную функциональность!
Насколько я понял смысл, расширенная функциональность должна быть реализована введением дополнительных методов, а не изменением поведения старых.
скорее внедрением новых классов. а так в точку.
Поясните плз вашу мысль :)
В данном случае речь идет о наследовании, т.е. действительно расширенный функционал должен быть реализован введением новых методов в наследованном классе. В смысле внедрение новых классов?

что поведение программы должно «расшириться»

поведение программы никак не расширяется.
Имеется в виду что была программа вида
a=new class_a();
a->method1();

и если написать
class class_b extends class_a
{
}
b=new class_a();
b->method1();

то эта программа будет делать то же самое, что первая.
Но ты сможешь использовать новые методы, которые были введены в классе b, естественно.
Но вообще говоря поведение программы 2 будет равно поведению программы 1, потому что программа сама никак не расширилась и новый введенный функционал класса в ней никак не задействован.
Надеюсь понятно объяснил :)
b=new class_b();
имелось в виду, конечно же.
вы изменили код клиента. вообще это очень долго описывать. например создание конкретных классов, подверженных изменениям, есть очень плохо, так как он нарушает принцип DIP(последняя буква в SOLID). Вы уже привязываете код клиента к конкретным классам и вам придется модифицировать работающий код вполне вероятно в большом модуле, и наверняка не один раз. Для защиты используется паттерн Factory. Но допустим мы заменили ваш код на такой IA* a = LalaFactory::make(«A»); // например задаем тип нужного объекта текстом
так вот суть LSP в том, что бы классы, реализцющие IA выполняли открытый его интерфейс однозначно и не противоречиво. Если вы сделаете новую модификацию — вы реализуете новый класс, и в фабрику добавите код для создания объекта. Текст «A» в этом примере например можно читать из конфига или константы.

Сам же класс, реализующий IA, может вытворять внутри себя что угодно, он также может реализовывать другие интерфейсы, но клиентский код, указанный выше, будет знать только об его поведении в терминах IA и никак иначе. Вот и вся суть OCP + LSP — [b]Зависимость от абстракций.[/b]

Попробуйте почитать книжку
Agile Principles, Patterns, and Practices in C#
By Martin C. Robert, Martin Micah

Только на английском, ибо русский перевод ужасен чуть больше, чем полностью. Сгодится разве что для чтения художественной части, а термины и правила лучше переводить из оригинала. По частям SRP, OCP, LSP, ISP, DIP дают мало инфы. А вот когда все вместе описывается — тогда да.
А разве не это называется «полиморфизм»?
Тут пропущен важный момент — не упоминается об open close principle (OCP). OCP подразумевает расширение функционала только за счет написания кода, используя полиморфизм. LSP дает нам ограничение, нарушив которое мы не сможем применить расширение за счет создания нового кода, не изменяя старый.
даешь остальные 4 принципа SOLID =) сам думал написать, но времени чтото нету.
«Как эту ситуацию исправить? Увы, это не тема данной статьи. Я думаю, что итак всех утомил.» — ну вот, блин. На самом интересном месте… Хоть названия паттернов перечислили бы.
Да, это как раз то, чего я ожидал прочитать в этой статье и был крайне расстроен, что вот так вышло… :-)
Как перекрывать методы-то все знают! :-)
Спасибо, хорошая статья. Наверное мне повезло, что я узнал об этом принципе из нее, так как тут все очень понятно написано.
я узнал на много раньше, но статья мне разъяснила более понятнее.
спасибо автору, ждем описание принципа Открытие/Закрытия.
интересно, а минуснули за красивые глаза?
Статья понравилась. Действительно приятно, что написано по-человечески (хотя я и так это знал), а не как в Наш мир, возможно, — одна огромная голограмма, от которой у меня было ощущение, что мой мозг изнасиловали в извращённой форме… А самые умные — проходите мимо — тут собрались люди разной степени подготовки и знаний, но объединённые одним общим интересом к IT (по-крайней мере я на это надеюсь) :)
«Зачем так много слов, зачем так много треска? Ты мне невестка или не невестка» (с) догадайтесь :)

Достаточно задуматься над такой задачкой — нужны классы для прямоугольника и квадрата, который от которого наследовать? Как корректно поставить эту задачу?
...«нужны классы для прямоугольника и квадрата» — это не задача, а начало решения не поставленной задачи.
Чтобы как-то определиться, нужно понять желаемую функциональность, в первую очередь.
Тем не менее, задачу иногда пытаются поставить именно так — нам нужны абстракции того-то и того-то. Вы совершенно правы — без определения интерфейса (функциональности) задачу не поставить. Я именно это и имел ввиду.
Али-Баба! :-)
Тут как раз всё понятно, если вспомнить про отношение «является».
Любой квадрат является прямоугольником.
Не любой прямоугольник является квадратом.
Квадрат расширяет Прямоугольник.
Для тех, у кого мозги повернуты думать с точки зрения тестов:

«Пусть тест q(x) выполняется, если в него передать любой x некоторого типа T. Тогда тест q(y) также должен проходить, если передать в него любой объект типа S, где S является подтипом типа T.»
Вам плюс за полезный поворот мозгов. А еще за то, что, в отличие от оригинала в википедии, запятые правильно расставлены)
Спасибо :) Надо передать это моей учительнице русского языка, она за грамотность сочинений мне всегда 2 ставила — на 6 страниц пропущенных запятых набиралось достаточно :)
Недавно тоже думал над этим вопросом с точки зрения рефакторинга.
Как такие проблемы решать — самое интересное, а автор умолчал :)

Знаю только одно решение: абстрагирование базового класса и внесение соответствующих методов в наследуемые классы. Но это влечет за собой кучу других проблем (дублирование кода и т.д.).
Может еще кто-то как-то боролся с таким?

Конечно боролись, причем до нас. Вот кратенький список того, что поможет:
igor.quatrocode.com/2008/09/solid-top-5.html

и книжка Мартина, которую указывал выше:
Agile Principles, Patterns, and Practices in C#
By Martin C. Robert, Martin Micah
Русский вариант определений\правил\етц категорически запрещен. Читать инглишь, непонятное сверять с русским, если в художественном языке пробелы(как у меня).
Не хватает одной простой иллюстрации — диаграммы Венна:


Принцип в терминах теории множеств будет звучать так:

Свойство q, выполняющееся для объектов множества (типа) S, должно выполняться для объектов подмножества (подтипа) T.

И еще кто-то будет говорить, что в программировании не нужна математика и матан? :-)
и причём же тут мат анализ?
ну если уже минусуете, то обьясняйте причем тут матан, мне тоже интересно :)
Просто человек пытается хоть как-то обосновать необходимость предмета, на который было потрачено так много сил…
Ну, как бы, теорию множеств изучает матан.
Боюсь это не так, даже если закрыть глаза на формулировку.
Дискретная математика имеет некоторое отношение к теории множеств, но никак не математический анализ.
Множество.
Второе множество.
Каждому элементу одного множества сопоставляем элемент другого множества по определенному правилу.
Получается функция.
Математический анализ — совокупность разделов математики, посвященных исследованию функций и их обобщений.
>>>Опишем это более понятным языком, то есть C++

Особенно приятно видеть эту фразу в понедельник :)
Забавно, что для языков с развитой интроспекцией, это правило не работает. Если у вас есть доступ к свойствам именно как к свойствам (на мета-уровне), то легко нарушить LSP.
интроспекция не является частью ООП, следовательно, не совсем относится и к обсуждаемому принципу. Если на то пошло, интроспекиция и метапрограммирование являются способами обхода жесткой ООП там, где задача плохо сводится к ООП, то есть являются костылями. Не хочу сказать, что эти метода плохи — наоборот, мне они очень нравятся — но это именно костыли для другого костыля под названием ООП )
Следование данному принципу довольно таки спорный вопрос по-моему, так как некоторые паттерны проектирования напрочь его нарушают, к примеру Decorator.

А вот создатели языка Go видимо решили его гарантировано воплотить запретив наследование )
Статья понравилась.
PS. Минутка занудства. У Вас виртуальная функция называется «Initialize», а в наследуемых классах везде используется «InitializeDevice».
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.