Pull to refresh

Comments 33

В мире .NET есть хороший образец нарушения принципа Interface Segregation.
Всем известный System.IO.Stream, он и Read и Write и Seek… хотя потоки бывают разные.
Они конечно добавили костыли в виде CanRead, CanSeek, CanWrite…
в .Net много костылей. Та же утинная типизация для коллекций, например
Запоминать расшифровку SOLID не нужно, нужно лишь запомнить перевод solid;
Суть очень простая — это кибернетический подход управления сложными системами. Хороший пример — это иерархия власти в армии.
И к ооп это не имеет никакого отношения, SOLID — это пакет архитектурных принципов;
Почитать «Чистая архитектура» и «Чистый код» самого Мартина — одна из самых важных книжек для программиста и для кодера
Парадигмы программирования:

Объектно-ориентированное программирование… 1966 год.… привело к открытию полиморфизма -> Инверсия зависимости (D).

Источник
полиморфизм же был задолго до ооп, iostream например. Да и реализация ооп была еще даже на чистом Си.

В 1966 Си не было даже в проекте)

Можете поподробнее рассказать о приведенных примерах?
пирамида управления — это из системного анализа.
Задача состоит в том, чтобы из сложной системы сделать простую управляемую. Сложная система — это система со множеством связей, результат работы которую сложно предсказать(лапша-код).
Один из вариантов — это декомпозиция модулей. Сверху-вниз. В итоге, если убрать циклические зависимости и минимизировать количество связей(инкапсуляция), мы получим дерево, которое разделено по уровням абстракции.
В армии, генерал управляет и владеет своими полковниками. Полковники не знают об генерале и не могут ему давать приказы. Но полковники управляют уже сержантами, а сержанты уже солдатами. А солдаты вообще ничего не знаю, они лишь выполняют что скажут. В итоге, мы получили классическую строгую многоуровневую архитектуру.
Если применить на границе слоя инверсию зависимости, например абстрагировать солдат и чтобы они реализовали интерефейс который им дал сержант. Т.е. сержанту все равно кем управлять, лишь бы умели копать, бежать и отжиматься. Тогда можно легко дополнить солдат боевыми роботами и боевыми дельфинами.
Управлять такой системой легко, нужно лишь дать команду генералу «Разбомби Вашинктон», он уже делегирует на полковников «запустить ракету по таким-то координатам», они делегирует команду солдатам «нажми красную кнопку». Красная кнопка делегирует уже системе запуска.
Но идеально применить SOLID не получиться, чем-то придется жертвовать и все будещие изменения не предскажешь. В добавок, у нас получиться solid система, очень жесткая и не гибкая.
PS Принцип подстановки Барбары Лисков запомнить тоже легко, она о том что предок должен быть абстрактным, а не для того чтобы убрать дублирование кода.
И хороший пример нарушения принципа -
class Square: Rectangle
Забавно, интересно было почитать.
Мне почему-то всегда хочется сделать наоборот: верхние уровни зависимы от нижних. Видимо потому что абстрагировать верхних от нижних сложнее.

Вы вообще в армии служили?) Нормальный командир знает нюансы службы своих подчиненных, частично начальства, и при необходимости способен их замещать. Т.е. по сути это прямо наоборот. Армейская надежность исходит не из деления, а из многократного резервирования. God object по интерфейсам и копипаст в имплементации. Это антипаттерны по-вашему.

Ну сомневаюсь что генерал сможет заменить солдата)
Ну про надежность я с вами согласен. Но это уже другая архитектура, более жидкая и уже ближе к распределённой системе. Натуральная, так сказать
Мало того, что это стотысячная статья про SOLID, дак еще и на Shape & Animals примерах. Даже в давно написанном Чистом Коде Мартина, листинги более жизненные были. Лучше читать оригинал.

Большинство публикаций про SOLID представляют их как универсальную догму. Но с невыдуманными примерами, не притянутыми к догме за уши, а из жизни, почему-то всегда напряг. Покажите ссылки на реальный код с github где солид, где несолид и в чем в итоге разница для проекта.

Примеры, не кода, а из головы:
S — не нужно мутить в одном классе парсинг XML, его сохранение на диск и отправку по Email.
O — Не храните рецепты в поваре. Не копируйте класс целиком если нужно создать такой же но с дополнительными полями.
L — не наследуйте пользователя от телефона, поезд от автомобиля.
I — не запихивайте в один I несвязанные контракты.
D — нужны тесты? хочешь пилить свою фичу не дожидаясь выполнения всех других? хочешь TDD? нужно написать модуль для работы с железом которого еще нет?

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

*И да, не надо пытаться применять SOLID/GRASP/GOF везде где только можно, это зачастую только усложняет код. Об этом пишут во многих книгах.
Сам себе еще добавлю.

S — драйвер видеокарты не установит драйвер на принтер, значит и внтури драйвера (код) нет ничего для этого. У каждого пользователя windows свой фолдер в списке Users.
O — плагины для фотошопа, vs.
D — windows — hardware abstraction layer (HAL).

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

Что уже достаточно неплохо
В соответствии с принципом единственной ответственности класс должен решать лишь какую-то одну задачу. Он же решает две, занимаясь работой с хранилищем данных в методе saveAnimal и манипулируя свойствами объекта в конструкторе и в методе getAnimalName. 

А как же ActiveRecord?
ActiveRecord как раз нарушает этот принцип.
А разве любые архитектурные принципы обязаны сочетаться друг с другом?
Ну и потом, поля объекта можно сделать открытыми, тогда, формально, объект будет заниматься только хранилищем.
DTO это плохо, что гуглится по запросу «dto is bad», а еще поверхностно обсуждается в этом ролике.
Но допустим я согласен с аргументами почему это плохо, но как уйти от DTO я так и не нашел ответов (ну кроме по-моему мнению вредного совета найденого в гугле)
Как я понял из роликов Николай Алименков можно использовать лямбды вместо DTO. Но судя по выкладкам из документа не так легко написать лямбду производительный код с лямбдами.
Может кто-нибудь поделится опытом или мнением на счет DTO.
а еще есть такие вещи, как категории
и в примере в криками зверей их было бы логичнее использовать, а не наследование
Просто расширить класс Animal категорией с реализацией метода возврата крика. И тогда у каждого, кто был Animal в проекте автоматически бы появился этот метод.
Класс должен быть ответственен лишь за что-то одно. Если класс отвечает за решение нескольких задач, его подсистемы, реализующие решение этих задач, оказываются связанными друг с другом. Изменения в одной такой подсистеме ведут к изменениям в другой.


Опять статья про SOLID в которой НЕВЕРНО даётся определение основного принципа. Кому интересно, можете обратиться к первоисточнику — книги Роберта Мартина «Clean Code» и «Clean Architecture» и почитать как звучит определение на самом деле, а главное какой смысл заложен в этот принцип.
Хоть сформулировано и не так как у дядюшки боба, но лично я больше согласен с такой формулировкой. Хотя конечно и на оригинальный принцип посматривать стоит.

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

хм, обсуждение пошло — пару часов после публикации под статьёй ничего не было.


понимаю что статья переводная, но мне интересно обсудить такой вопрос.


в статье хорошо, что для пояснения S и O принципов использовали один и тот-же пример с животными. В первом примере ввели классы Animal и AnimalDB — выделив в AnimalDB сохранение/загрузку данных в базу поскольку это другая ответственность (по отношению к хранению данных о животных, хотя тогда их по идее 2 должно было бы быть, один на сохранение другой на загрузку). Во втором — массив animals: Array и функцию работы с ним AnimalSound(), которая в первой версии нарушает принцип О.

это нарушение О решают добавлением в Animal виртуального метода и порождением классов потомков от Animal. Однако это решение:


  1. скорее всего сломает существующую реализацию AnimalDB;
  2. внесёт неточность в объявление списка animals (ведь там они базового класса создаются);
  3. породит баг — поскольку виртуальные методы экземпляров с разными именами но одним и тем-же типом (Animal) по идее вообще ничего возвращать не должны — следствие из п.2.

разве к этому — переработке всех мест где создаётся (и потенциально используется) класс Animal — нас принуждают принципы SOLID при выявлении нового фактора — системе надо знать звуки животных, и следовательно, где-то хранить взаимосвязь животное-звук?

Статья неплохая — простая и понятная. Но вынужден согласиться со многими коментаторами, которые хотят более наглядных примеров. Особенно для случаев, когда принципы SOLID применить невозможно. Ведь любая теория без ограничения области, в которой она справедлива, выглядит не совсем правдивой и не совсем понятной. Вот и тут хочется узнать: а до каких же пор эти принципы работают и в каких случаях они уже не справляются.

На мой взгляд автор не в состоянии объяснить принцип Лисков и вводит людей в заблуждение ошибочным объяснением.

Ну так статья же изначально заявлена, как иллюстрация. Сам автор в начале пишет

Автор материала не стремился к тому, чтобы выйти на работающий код

Странно при такой постановке вопроса ожидать детального доказательного изложения. Это просто наглядная иллюстрация. Возможно не самая удачная, но, зато, простая и наглядная.
Если уж на то пошло, то по каждому пункту, по хорошему, нужна отдельная статья с парой десятков примеров. И не из чьих-то ночных кошмаров, а из жизни.
>Такой подход нарушает принцип открытости-закрытости. Как видно, здесь, если нам надо дать некоей группе клиентов особую скидку, приходится добавлять в класс новый код.<
Да вы наверное шутите. Очень неудачный пример.

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

Спасибо, было полезно освежить некоторые моменты в голове.

Sign up to leave a comment.