Pull to refresh

Comments 36

С публикацией что-то пошло не так? Проверил через incognito — вроде бы доступна, корректно отображается.

Наверное имелась ввиду низкая содержательность/объем


P.S.


Их не помогли понять ни

А что в итоге помогло? Имхо, все эти принципы легко обнаруживаются с опытом как нечто само собой разумеющееся, далее уже оттачивание мастерства их применения

Да, опыт, конечно :)

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

Для новичков статья в самый раз, пересказывает принципы солид простым языком.

Статья лёгенькая, буквально в двух словах, поэтому позволю себе показать ещё более простой вариант на картинке под спойлером (автор неизвестен):

буквально в двух словах

Как же, теперь, жить? После такого спойлера.

"Делай просто, насколько возможно, но не проще этого."

Может это просто плохая идея если уже в 1000 раз повторяете а они все не могут вашу молитву выучить?

Буква L

Принцип подстановки Барбары Лисков (Liskov Substitution Principle) говорит, что если мы дописываем новых наследников к классу, то нужно это делать таким образом, чтобы не пришлось менять весь старый код, который этих самых наследников будет использовать (старый код-то и не знает ничего про наследников). Опять-таки: легко дописываем, но тяжело ломаем.

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

Ну ковартантность и LSP они обсуждали ещё в том же самом тредике, где Дядя Боб имел неосторожность предложить свои принципы (их там кстати больше) - https://groups.google.com/g/comp.object/c/WICPDcXAMG8?hl=en&pli=1#adee7e5bd99ab111.

Барбара в интервью рассказывала, что формулировала LSP как неформальное правило, больше с целью порассуждать, без претензий на научную строгость. У Дяди же адаптация принципа под OOD (с оглядкой на C++) выглядит вообще как каламбур. Это уже потом Дядю канонизировали, а сказанное превратили в догму.

Ну как без претензий..

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

Вообще, определение подтипизации, зависящее от того какие функции мы вызываем в программе, - это какое-то вырывание гланд через анус.

Спасибо за ссылку
1987 - это ваша ссылка

3.3. Type Hierarchy A type hierarchy is composed of subtypes and supertypes. The intuitive idea of a eubtype is one whose objects provide all the behavior of objects of another type (th e supertype) plus something extra. What is wanted here is something like the following substitution property [S]: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for oz, then S is a subtype of T. (See also [2, 171 for other work in this area.)

Т.е. для всех программ, не для одной функции.

А вот википедия - 1994
 Barbara Liskov and Jeannette Wing described the principle succinctly in a 1994 paper as follows:[1]

Subtype Requirement: Let {\displaystyle \phi (x)}

 be a property provable about objects {\displaystyle x} of type T. Then {\displaystyle \phi (y)} should be true for objects {\displaystyle y} of type S where S is a subtype of T.

Вообще, определение подтипизации, зависящее от того какие функции мы вызываем в программе, - это какое-то вырывание гланд через анус.

Этот документ целиком не читал, но как мне кажется по определнию выше, это ограничение скорее как нечто связывающее употребление и отношение между типами. Если оно не соблюдается, то что-то надо менять. Или программу или отношение.

Если программа делает то, что нужно, но принцип не соблюдается, возможно вам нужны другие отношения между типами.

У нее есть более поздняя работа, где она придумала другое определение подтипов на полстраницы A behavioral notion of subtyping.

Для "всех возможных программ" по такому определению мутабельные типы вообще не образуют иерархии ибо являются инвариантными.

Что такое инвариантные типы?

Подставляются не типы а объекты типа. Конвариантны/контравариантны не типы а преобразования типов. Т.е. функция над типами. Ко-вариантны

Within the type system of a programming language, a typing rule or a type constructor is:

  • covariant if it preserves the ordering of types (≤), which orders types from more specific to more generic: If A ≤ B, then I<A> ≤ I<B>;

  • contravariant if it reverses this ordering: If A ≤ B, then I<B> ≤ I<A>;

  • bivariant if both of these apply (i.e., if A ≤ B, then I<A> ≡ I<B>);[1]

  • variant if covariant, contravariant or bivariant;

  • invariant or nonvariant if not variant.

т.е. у вас есть спрособ описать функцию на пространстве типов f(x) -> y
если из x1 is x2 следует f(x1) is f(x2) то преобразование ковариантно. (Аргумент со-варьируется с результатом)

Например в C# вот это не скомпилируется, так как List<> сам по себе не тип а фактически конструктор типов.

 public static void Main()
  {
    object x = null;
    if (x is List<object> || x is List<>)
    {


    }
  }

Впрочем, мы это уже обсуждали

Тут мне непонятно. Если есть коллекция ICollection с методами Add(Object x) и Object GetFirst() почему мутабельный List с такими же методами не является его подтипом?

Это исследовательская работа, скорее уровня реферата. Она там собирает примеры абстракции данных в паре языков и в одном из разделов рассуждает про разницу между наследованием реализации и иерархией типов (а сейчас будет лучше сказать - спецификаций - потому, что ее трактовка типа там отличается от привычной из type theory). А сам "принцип" это цитата из другого автора, который она привела просто в качестве примера:

The intuitive idea of a subtype is one whose objects provide all the behavior of objects of another type (the supertype) plus something extra. What is wanted here is something like the following substitution property [6]: If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2, then S is a subtype of T. (See also [2, 17] for other work in this area.)

Я так понимаю LSP для ООП это стало с подачи дяди, сама Барбара ни чего такого не предполагала. Даже наоборот:

We are using the words “subtype” and “supertype” here to emphasize that now we are talking about a semantic distinction. By contrast, “subclass” and “superclass” are simply linguistic concepts in programming languages that allow programs to be built in a particular way. They can be used to implement subtypes, but also, as mentioned above, in other ways.

SRP трактуют по разному. "Одна причина" - это про Big Bang или теологию?

Почему-то в статье (да и в самой методологии) ничего не говорится о том, что нужны различные уровни описания. Дело в том, что, обычно, берётся иерерхия классов, инкапсулирующих важнейшие библиотеки, и создаётся новая иерархия классов. Точнее, создаются наследники существующих классов и соответствующие им иерархии. Классов (например, в Java) и так очень много, а тут ещё добавляется новая иерархия для описания предметной области. Тут зарыта довольно серьёзная собака. Системный подход не допускает смешение уровней описания, а это значит, что иерархия системных объектов и иерархия прикладных объектов — это две незавиисмые друг от друга иерархии. Вы берёте иерархию системных объектов и создаёте, по сути, новый язык описания, в терминах которого описываете уже объекты предметной области.

в Java вот есть интерфейс Collection с методами в духе add(), remove() и так далее. И в неизменяемых реализациях коллекций эти методы, ясное дело, не нужны. Поэтому, согласно принципу I, интерфейс стоит разделить на Collection и его наследника, MutableCollection (как сделано в Kotlin, например).

А не будет более естественным иметь единый класс Collection, но иметь параметр в конструкторе, который описывает поведение коллекции? (В некоторых ситуациях, можно было бы, даже, ожидать создания шаблона Collection.) По своей сути, коллекция — это абстрактный тип данных, который в самом общем виде описывает некоторый контейнер однородных (в определённом отношении) объектов. Но этот абстрактный тип инкапсулирует два следующих объекта: способ доступа и внутреннее представление.

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

неизменяемые реализации коллекций

Подозреваю, топик стартер имел в виду, что в java.util.Collections есть методы .unmodifiableList, .unmodifiableSet, возвращающие экземпляры неизменяемых коллекций.

Совершенно выпустил из виду, что интересное начинается уж здесь:

в Java вот есть интерфейс Collection

То есть: колекция сама является интерфейсом (задумчиво уходит в совершенном замешательстве).

Как правило разработчикам приходится реализовывать новую функциональность, а не менять старую

А вот в английской wiki про SOLID

design principles intended to make software designs more understandable, flexible, and maintainable

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

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

Код живёт в разработке ну может пару дней

В этом же и суть: запилили фичу и забыли. Написанный код больше стараемся не трогать.

Статья простая и понятная. Много натыкался на статьи, которые можно объединить одной фразой "Не понимаю зачем в современном мире SOLID, никогда его не понимал, его написали старые бородатые дядьки под старое, никому не нужное ООП, юзать его не буду и вам не советую, на дворе 2020 + год", но человек не спасовал, разобрался, и написал свое понимание, и это хорошо, ибо новичкам будут попадаться выше упомянутые статейки, если не будет новой информации про опыт применения принципов SOLID.

А чисто от себя могу добавить, что понимание пришло довольно быстро. И на проекте на котором сейчас работаю благо все принципы закладывались со старта. И если какой либо принцип по той или иной причине нарушался было чувство, что ставишь на дороге грабли, и что вы думаете? Через пару месяцев эти грабли били по голове, и ты в очередной раз убеждаештся в их правомерности и актуальности.

Как правило разработчикам приходится реализовывать новую функциональность

Это неверное допущение. Принципы SOLID в первую очередь о том, как сделать так чтобы изменение существующей функциональности — равно как и добавление новой — не было сильно трудозатратым и не часто приводило к багам.

В частности принцип OCP предлагает проектировать код так, чтобы будущие изменения (как новаую функциональность так и модификацию старой) можно было осуществлять путём добавления новых программных сущностей, а не изменения существующих

Приз за самую короткую и бессодержательную статью о принципах SOLID сфоткаете?

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

с этой фразой согласен

Учитывая интуитивное соотношение новых/существующих систем, как правило, приходится дополнять уже неотъемлемую старую функциональность. Слово "новое" в этом ключе не совсем верное. Больше похоже на приобревшее дальнейшее развитие старое. И поскольку мы, как правило, докладываем небольшие кирпичики к уже существующей большой куче, основной акцент должен быть на том, как эту кучу не разрушить.

Принцип D было бы неплохо уточнить или объяснить по-другому - что такое зависимость и где она инвертируется? А то во многих кодобазах генерируются тысячи абстракций, а Принцип D нарушается. Иначе это ещё больше запутывает.

Sign up to leave a comment.

Articles