Comments 33
Всем известный System.IO.Stream, он и Read и Write и Seek… хотя потоки бывают разные.
Они конечно добавили костыли в виде CanRead, CanSeek, CanWrite…
Суть очень простая — это кибернетический подход управления сложными системами. Хороший пример — это иерархия власти в армии.
И к ооп это не имеет никакого отношения, SOLID — это пакет архитектурных принципов;
Почитать «Чистая архитектура» и «Чистый код» самого Мартина — одна из самых важных книжек для программиста и для кодера
Задача состоит в том, чтобы из сложной системы сделать простую управляемую. Сложная система — это система со множеством связей, результат работы которую сложно предсказать(лапша-код).
Один из вариантов — это декомпозиция модулей. Сверху-вниз. В итоге, если убрать циклические зависимости и минимизировать количество связей(инкапсуляция), мы получим дерево, которое разделено по уровням абстракции.
В армии, генерал управляет и владеет своими полковниками. Полковники не знают об генерале и не могут ему давать приказы. Но полковники управляют уже сержантами, а сержанты уже солдатами. А солдаты вообще ничего не знаю, они лишь выполняют что скажут. В итоге, мы получили классическую строгую многоуровневую архитектуру.
Если применить на границе слоя инверсию зависимости, например абстрагировать солдат и чтобы они реализовали интерефейс который им дал сержант. Т.е. сержанту все равно кем управлять, лишь бы умели копать, бежать и отжиматься. Тогда можно легко дополнить солдат боевыми роботами и боевыми дельфинами.
Управлять такой системой легко, нужно лишь дать команду генералу «Разбомби Вашинктон», он уже делегирует на полковников «запустить ракету по таким-то координатам», они делегирует команду солдатам «нажми красную кнопку». Красная кнопка делегирует уже системе запуска.
PS Принцип подстановки Барбары Лисков запомнить тоже легко, она о том что предок должен быть абстрактным, а не для того чтобы убрать дублирование кода.
И хороший пример нарушения принципа -
class Square: Rectangle
Мне почему-то всегда хочется сделать наоборот: верхние уровни зависимы от нижних. Видимо потому что абстрагировать верхних от нижних сложнее.
Вы вообще в армии служили?) Нормальный командир знает нюансы службы своих подчиненных, частично начальства, и при необходимости способен их замещать. Т.е. по сути это прямо наоборот. Армейская надежность исходит не из деления, а из многократного резервирования. God object по интерфейсам и копипаст в имплементации. Это антипаттерны по-вашему.
Большинство публикаций про SOLID представляют их как универсальную догму. Но с невыдуманными примерами, не притянутыми к догме за уши, а из жизни, почему-то всегда напряг. Покажите ссылки на реальный код с github где солид, где несолид и в чем в итоге разница для проекта.
S — не нужно мутить в одном классе парсинг XML, его сохранение на диск и отправку по Email.
O — Не храните рецепты в поваре. Не копируйте класс целиком если нужно создать такой же но с дополнительными полями.
L — не наследуйте пользователя от телефона, поезд от автомобиля.
I — не запихивайте в один I несвязанные контракты.
D — нужны тесты? хочешь пилить свою фичу не дожидаясь выполнения всех других? хочешь TDD? нужно написать модуль для работы с железом которого еще нет?
Понятно что нет пилюли, или лучших примеров на всех случаи. Хорошее решение сегодня, может оказаться плохим завтра.
Если вы найдете, то буду очень рад если поделитесь.
*И да, не надо пытаться применять SOLID/GRASP/GOF везде где только можно, это зачастую только усложняет код. Об этом пишут во многих книгах.
Из статьи вынес для себя только название книги, которую нужно прочитать.
В соответствии с принципом единственной ответственности класс должен решать лишь какую-то одну задачу. Он же решает две, занимаясь работой с хранилищем данных в методе saveAnimal и манипулируя свойствами объекта в конструкторе и в методе getAnimalName.
А как же ActiveRecord?
Ну и потом, поля объекта можно сделать открытыми, тогда, формально, объект будет заниматься только хранилищем.
Но допустим я согласен с аргументами почему это плохо, но как уйти от DTO я так и не нашел ответов (ну кроме по-моему мнению вредного совета найденого в гугле)
Как я понял из роликов Николай Алименков можно использовать лямбды вместо DTO. Но судя по выкладкам из документа не так легко написать лямбду производительный код с лямбдами.
Может кто-нибудь поделится опытом или мнением на счет DTO.
и в примере в криками зверей их было бы логичнее использовать, а не наследование
Просто расширить класс Animal категорией с реализацией метода возврата крика. И тогда у каждого, кто был Animal в проекте автоматически бы появился этот метод.
Класс должен быть ответственен лишь за что-то одно. Если класс отвечает за решение нескольких задач, его подсистемы, реализующие решение этих задач, оказываются связанными друг с другом. Изменения в одной такой подсистеме ведут к изменениям в другой.
Опять статья про SOLID в которой НЕВЕРНО даётся определение основного принципа. Кому интересно, можете обратиться к первоисточнику — книги Роберта Мартина «Clean Code» и «Clean Architecture» и почитать как звучит определение на самом деле, а главное какой смысл заложен в этот принцип.
хм, обсуждение пошло — пару часов после публикации под статьёй ничего не было.
понимаю что статья переводная, но мне интересно обсудить такой вопрос.
в статье хорошо, что для пояснения S и O принципов использовали один и тот-же пример с животными. В первом примере ввели классы Animal и AnimalDB — выделив в AnimalDB сохранение/загрузку данных в базу поскольку это другая ответственность (по отношению к хранению данных о животных, хотя тогда их по идее 2 должно было бы быть, один на сохранение другой на загрузку). Во втором — массив animals: Array и функцию работы с ним AnimalSound(), которая в первой версии нарушает принцип О.
это нарушение О решают добавлением в Animal виртуального метода и порождением классов потомков от Animal. Однако это решение:
- скорее всего сломает существующую реализацию AnimalDB;
- внесёт неточность в объявление списка animals (ведь там они базового класса создаются);
- породит баг — поскольку виртуальные методы экземпляров с разными именами но одним и тем-же типом (Animal) по идее вообще ничего возвращать не должны — следствие из п.2.
разве к этому — переработке всех мест где создаётся (и потенциально используется) класс Animal — нас принуждают принципы SOLID при выявлении нового фактора — системе надо знать звуки животных, и следовательно, где-то хранить взаимосвязь животное-звук?
Статья неплохая — простая и понятная. Но вынужден согласиться со многими коментаторами, которые хотят более наглядных примеров. Особенно для случаев, когда принципы SOLID применить невозможно. Ведь любая теория без ограничения области, в которой она справедлива, выглядит не совсем правдивой и не совсем понятной. Вот и тут хочется узнать: а до каких же пор эти принципы работают и в каких случаях они уже не справляются.
На мой взгляд автор не в состоянии объяснить принцип Лисков и вводит людей в заблуждение ошибочным объяснением.
Ну так статья же изначально заявлена, как иллюстрация. Сам автор в начале пишет
Автор материала не стремился к тому, чтобы выйти на работающий код
Странно при такой постановке вопроса ожидать детального доказательного изложения. Это просто наглядная иллюстрация. Возможно не самая удачная, но, зато, простая и наглядная.
Если уж на то пошло, то по каждому пункту, по хорошему, нужна отдельная статья с парой десятков примеров. И не из чьих-то ночных кошмаров, а из жизни.
Да вы наверное шутите. Очень неудачный пример.
К решениям проблем, что были показаны я бы придрался при случае, хотя для показания как НЕ надо делать, достаточно. В целом неплохой материал, спасибо.
Спасибо, было полезно освежить некоторые моменты в голове.
Принципы SOLID, о которых должен знать каждый разработчик