Pull to refresh

Comments 19

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

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

То есть, автор сначала поставил в заголовке заведомо неверный вопрос, а потом бодро на него ответил.

Это не значит что в статье нет смысла, наоборот, я много раз встречал отношение к SOLID как к инструменту получения готового решения, каковым он в общем-то не является.
Очередная «попытка отмены»? Не нравится SOLID — не используй в своих проектах. Но нет, я буду и буду писать статьи, что SOLID г-о.
Passionate about learning and sharing mobile development and its adjacent technology.

Автор даже не писал 5кк LOC продукты, которые живут десятилетиями. Ясно. Очередной отменятель. Тогда пусть перестанет сам писать под Android (его твиттер забит мобильными аппами).
Вот и вы похоже пост дальше заголовка не дочитали :) А автор между тем подчеркивает не раз:

Позвольте мне повторить, для меня принципы SOLID — это хорошие принципы для следования в разработке программного обеспечения.

Примеры странные,
например в подстановке Лисков,
Правильно делать два интерфейса, один Animal, второй MovingAnimal и MovingAnimal не должен наследовать Animal. Пусть животные, которые двигаются наследуют Animal и MovingAnimal


interface Animal

interface MovingAnimal {
   move()
}

class Sessile: Animal {}
class Dog: Animal, MovingAnimal  {}

Потом кстати он это и показывает на Принципе разделения интерфейса, т.е. тут показал неправильно и говорит, что это минус.


В Принципе разделения интерфейса опять надуманно, зачем один интерфейс наследует другой? Из-за этого вся проблема и есть.


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


В общем не совсем согласен.

Принцип открытости/закрытости

Этот принцип — идеалистический принцип


Верно подмечено. Ведь по сути, это попытка написания кода, не который требуется для работы в текущий момент, это написание кода, который может потребоваться когда то в будущем. Как показала практика, попытки написания кода «на будущее» приводят к тому, что до будущего обычно не доживают, и часто этот код потом просто удаляется (либо, что хуже, он на всегда остаётся в проекте).
К тому же, написание подобных «закладок на будущее» усложняет код. IMHO, лучше написать простой и понятный с первого прочтения не универсальный код, который при необходимости всегда можно изменить/усложнить добавив универсальности.
Вообще open close принцип не предполагает «закладок». Речь скорее о «плагинах», которые можно будет написать позже. Т.е. речь скорее не о написании кода про запас, а о наличии API, реализацию которого можно будет дополнить новыми вариациями. Если вы видите неиспользуемый код, который навсегда остался в проекте — это скорее всего про что-то другое (хотя в том чтобы обсуждать код без примеров конечно мало смысла).
Т.е. речь скорее не о написании кода про запас, а о наличии API,

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

Я обычно привожу простой пример, что такое open close — сортировка. Сортировка предполагает, что сортируемые сущности можно сравнивать, т.е. на их множестве определена операция < или >. Ну т.е. вы пишете сортировку таким образом, чтобы само сравнение было внешним куском кода — сортировка это функция высшего порядка, например, или в случае ООП ее аналог — объект с одним методом compare. Ну т.е. изначально всегда есть пара реализаций — скажем для сравнения чисел, и строк. А другие может написать пользователь, чтобы сортировать сложные объекты, которые сравниваются как-то не очевидно.

>И, через пару лет эксплуатации выясняется, что плагин как был один, так один и остался, и других уже не предвидится?
Ну да, в таком варианте бывает тоже. Просто в вашем изначальном варианте это немного иначе выглядело по тексту. И в целом я согласен — бездумное применение принципов усложняет код. Ведь они предназначены для упрощения расширения. Если расширения никогда не будет — планировать его заранее это лишнее. Разработчику остается угадать, «никогда» это на самом деле никогда, или есть варианты :)
Я обычно привожу простой пример, что такое open close — сортировка.

IMHO, плохой пример. Непосредственно в C# уже встроена сортировка. И, в СУБД уже встроена сортировка. Вам не хватает возможностей механизмов сортировки, уже встроенных в C#? Или, вы думаете, что напишите механизм сортитировки лучше, чем это сделали профи из MS? Часто вам приходится писать механизм сортитровки?
Пример должен быть примером. Есть другой пример? Какой нибудь из жизни?
>Непосредственно в C# уже встроена сортировка.
И что характерно, ее API построен ровно так, как я и говорил. Т.е. например:

Sort(Comparison<T>)


Как говорится, угадайте с трех раз, где тут наш плагин?
Создавая сортировку в C# программисты делали библиотеку, потому предусмотрели — что бы это был универсальный инструмент. Относится ли это как то к SOLID? Вряд ли. Ведь SOLID — он применительно к коду, а не к библиотекам.
Так понимаю, реального примера «из жизни» применения принципа открытости/закрытости не будет.
>Ведь SOLID — он применительно к коду, а не к библиотекам.
Давно ли библиотеки перестали быть кодом?

>Так понимаю, реального примера «из жизни» применения принципа открытости/закрытости не будет.
Вообще-то, тут никто никому ничем не обязан. Более того, мне совершенно наплевать, что вы думаете про принципы SOLID. Продолжайте это думать сколько угодно, я не ставил себе целью кого-то переубедить.
Давно ли библиотеки перестали быть кодом?

dll-ка перестаёт быть кодом в момент, когда её скомпилируют :) У вас нет доступа из проекта к коду библиотеки.

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

Ну, зачем на личности то переходить и грубить?
Здесь есть главный вызов. Всё программное обеспечение предназначено для изменения. А изменения действительно трудно предсказать, если вообще возможно. Поэтому нам действительно сложно определить четкую границу того, что останется и что изменится.

SOLID принципы — это не приговор, это руководство. Они не гарантирую, а помогают делать ПО легким для поддержки и расширения. SOLID не утверждает, что изменения в коде это плохо, а лишь дает рекомендации как минимизировать затраты в целом. Эти принципы хорошо работают не по отдельности, как в ваших примерах, а вместе. От куда мысль, что изменения — это плохо? Из принципа открытости/закрытости? Меняйте код сколько хотите, но это OCP принцип, ВМЕСТЕ с другими, позволит вам минимизировать изменения, а не избавиться от них полностью.
Скажем если в примере калькулятор делает только сложение и вычитание, то достаточно интерфейса ICalculator с методом compute с 3 аргументами для бинарных операций. Когда у вас появятся унарные операции, добавьте, например, IUnaryCalculator с методом compute и 2 аргументами для унарных операций (ISP). И это только ваше решение где он будет реализован: в старом Calculator или еще где. Когда вам покажется что Calculator начал распухать просто отрефакторите код (SRP), это ведь ваш код. Там, где нужно потребители потребуют (DI) в конструктор ICalculator, в других местах IUnaryCalculator. Но изменений будет минимум (OCP) и все потребители уверены в том, что, скажем, операция 1/x будет работать одинаково при использовании IUnaryCalculator, где бы эта реализация не находилась (LSP). Применение принципов SOLID просто позволит жонглировать вашим кодом с минимальными трудозатратами на поддержку и развитие. Лень — двигатель прогресса.

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

На самом деле эта строчка стоит целой статьи.
можно попросить немного развить мысль?

Можно, я разовью? Понимаете, есть люди, которые пишут лютый отвратный код. Они оправдывают это тем, что их проект быстро меняется, и код, который они пишут, всё-равно, будет выброшен. В то же самое время, правильное применение принципов SOLID, позволяет нам иметь набор интерфейсов и абстракций, с которыми модификация кода обходится дешевле, чем переписывание. Собственно, этот аргумент автор оригинала и не упомянул(а), и идею, соответственно, не развил(а).

«возможно, мы нарушили YAGNI»

Как отличить соображающего разработчика от не очень соображающего? Второй в любом разговоре оперирует аббревиатурами и терминами. Даже когда они значат ничего.

Автору, буде хайптрейн оседлать пытается, рекомендую сделать статью относительно термина «НБИ» (не будь идиотом) и «ППК» (пиши правильный код). Главное писать емоционально, а противопоставлять что-нибудь осмысленное и с ненулевым порогом вхождения.

Распространенная ошибка - привести изначально неправильное решение нарушая принципы SOLID, а потом сделать вывод что принципы SOLID не работают в кривом решении. Сначало нужно определиться зачем вам вообще объект калькулятор и должен ли у него быть метод с тремя а не одним параметром? Разные операции можно представить разными объектами которые нужные параметры принимают как и положено в конструкторе а вычисляют их методом без параметров. SumOperation(x, y).Result();. Нужен калькулятор? Пусть принимает инициализированные объекты и вызывает их общий интерфейс Result().

Эта статья — один из многих постов в блоге автора. Блог в целом посвящен гибкости, open mind и подобным вещам. Примеры подогнаны для иллюстрации основных идей блога, поэтому технически не всегда удачны.


изменения действительно трудно предсказать, если вообще возможно

Изменения чего? Если представить приложение, речь пойдет о некоей бизнес-логике, которая оперирует бизнес-сущностями (моделями).
Изменение моделей, над которыми на этапе проектирования хорошо подумали (вникли в доменную область заказчика и увидели, что в реальности стоит за описанным в user stories видом со стороны пользователя), происходит достаточно редко. Как правило, это добавление нужных новых полей; пометка deprecated, перевод в опциональные и последующее удаление более ненужных. Здравый смысл и опыт позволяют программисту избегать крайностей (как слишком больших моделей, которые описывают несколько сущностей вместо одной, так и излишне мелких, описывающих малую часть доменной области). И тут нарушенные принципы SOLID — источник вопросов к самому себе, некие флаги, подсказывающие, что что-то с моделью не так, и возможно она требует уточнения или декомпозиции.
Изменение же бизнес-логики при нормально написанных моделях не требует переписывания большого количества кода и не нуждается в предсказании изменений. Если наши сущности адекватно соотносятся с реальностью — ничего не рухнет от изменения порядка вызовов публичных методов или передачи в них других значений в заданных для их параметров рамках.


в будущем кто-то может просто захотеть выполнить операцию сложения без необходимости использовать класс Calculate. Тогда код выше нужно изменить!

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

Sign up to leave a comment.

Articles