Pull to refresh

Comments 50

Когда-то давно, будучи самоучкой и услышав слово паттерн, я пошел читать, что же это такое. Я тогда недоумевал, почему использование свойств языков являются паттернами и удивлялся, когда находил среди них, доселе придуманных мною решений. Затем, однажды, я накнулся на один ответ на SO, с таковой сутью: паттерны это то чем один человек объясняет другому, что и почему он написал. Когда вы пишите, вы должны думать в терминах языка и задачи, а не о том, какой паттерн лучше приткнуть. Не знаю если если так было у вас, но количество аббревиатур в тексте настораживает.
Это модно. Берется набор самых обычных, разумных правил, ему дается красивое название — и — добро пожаловать в новую технологию/метод разработки/еще что-нибудь. Ну здорово же, прищурив глаз, спросить, что думает собеседник по поводу SRP, SOLID, DRY, TDD и т.д.? Или сколько паттернов он знает наизусть.
— Я уметь инкапсулировать, не копипастить и проверять свой код. ¯\_(ツ)_/¯
— Мы с вами свяжемся.
А что ещё, кроме баззвордов, можно спросить на собеседовании за ограниченное время?..
Тестовое задание все равно определяет.
Я замечал интересную закономерность: чем больше человек произносит сокращений слов вроде SOLID, DRY, KISS, YAGNI, я уж молчу про более сложные вещи, тем больше в коде говна. Хотя казалось бы…
Я замечал интересную закономерность: чем меньше человек знает сокращений вроде SOLID, DRY, KISS, YAGNI, я уж молчу про более сложные вещи, тем больше в коде говна. Хотя казалось бы… всё же так просто и кому нужны аббревиатуры и смыслы под ними скрывающиеся.
Вот-вот, и особенно насчет аббревиатур.
Когда в угоду DRY, сам того не замечая, разработчик жертвует принципом SRP, первым и главным постулатом из SOLID.
Вот так живешь — живешь, сядешь в ванну, спинку себе потрешь… А потом оказывается, что ты, сам того не зная, ЁКЛМН в угоду ПРСТ, и это еще не говоря о ФХЦ. Хотя, какой я, в торец, программист, если не в курсе этих буковок.
И, кстати, да — правильная аббревиатура всенепременнейше должна быть сама по себе словом — DRY, KISS и так далее. Тогда ее автор, вероятно, прется неимоверно и считает себя ну просто писателем, не меньше.
И, кстати, да — правильная аббревиатура всенепременнейше должна быть сама по себе словом — DRY, KISS и так далее. Тогда ее автор, вероятно, прется неимоверно и считает себя ну просто писателем, не меньше.
Всё проще. Тогда она сама с большей вероятностью отложится на память (англоязычного) человека без усилий с его стороны.
Большая часть паттернов — это про эмуляцию ФП средствами ООП через механизм абстракции/наследования.
Дадада, ООП — это ФП для бедных… а ФП — это ООП для бедных.
Ну, с воплощением первого тезиса я встречался много раз, а вот с ситуациями, реализующими второй, интересно бы познакомиться… Эмуляция ООП на замыканиях?
Ещё одна из ошибок тут в том, что разработчик продолжает следовать плану, поставленным оценкам, и это происходит «в тишине», ведь ответственности как бы нет, она лежит на всей команде, принявшей такое решение и такую оценку.

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

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

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

И снова. Зная, что у вас есть хрупкое место — вы продолжали делать это место все хуже и хуже, обвиняя Стратегию, а не самих себя.

Вариант избавления от дублирования через...

На самом деле вы забыли ещё более приятный вариант — через полиморфизм. Стратегия — более узкий паттерн, чем универсальный метод для DRY. И полиформизм одновременно позволил бы избежать дублирования и не осложнил приложение как использование стратегии.

abstract class ThreeViewPageLayout {
  methodA () { /* code here */ } 
  methodB () { /* code here */ }
  methodC () { /* code here */ }
  runMethod () {
    methodA();
    methodB();
    methodC();
  }
}

class FirstPage extends ThreeViewPageLayout {
  methodB () { /* new code here */ }
}

class SecondPage extends ThreeViewPageLayout {
  methodB () { /* new code here */ }
}

class ThirdPage extends ThreeViewPageLayout {
  methodA () { /* new code here */ }
  methodB () { /* new code here */ }
}


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

Еще этому немало способствует желание заказчика реализовать все «подешевле», Ведь переиспользовать ранее созданный контейнер быстрее, чем реализовать страницу полностью.

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

Когда программисты говорят: «Заказчик лучше знает, что его прям сейчас устроит менее качественный код» меня это удивляет. Код — ответственность программиста. Он сейчас сказал и забыл об этом. И через три месяца спросит именно у вас, почему код не качественный. И у вас не будет прикрыться им, ибо он — непрофессионал и не знает среднесрочного влияния подобных решений. А вы, как профессионал не должны дать ему допустить этой ошибки.
На самом деле вы забыли ещё более приятный вариант — через полиморфизм.
Этот вариант худший. Его «обвинили, и наложили на него табу» уже давно. И я его таки упомянул. Наследование — зло. А проблема ровно та же самая. То, разработчик посчитал неизменяемым и вынес в базовый класс рано или поздно получит свой change request, в котором нужно будет изменить метод X для сценария А, но не для сценария В
То есть вы сами утверждаете, что допустили ошибку в планировании, но обвиняете при этом не собственную лень и непрофессиональное поведение, а принцип DRY.
Да, мы допустили ошибку, кто их не допускает? И нет, мы не обвиняем, а предлагаем метод ну если не решения, то улучшения, хотя даже скорее метод избежания заведомого ухудшения.
Есть такой параметр, малопонятный — вязкость, и мы, то есть я, утверждаю, что вязкость в указанном подходе высокая. Выше чем.
Его «обвинили, и наложили на него табу» уже давно

Хах. Подтвердили мои слова. Видите — проблема не в методах, подходах кое в чем другом.

Наследование — зло

Извините, но это бред. И абсолютно не аргументирован. Давайте запретим ножи и вилки, потому что ими можно убить человека, а это — зло. Будем кушать только пластиковыми ложками. В тюрьме, кстати, ножи и вилки не дают, ибо там есть много психованных социопатов, которые их могут так использовать. Вот только металическую арматуру (ваши функции) точно так же можно использовать для убийства. Необходимо растить культуру программирования в команде. Или иметь архитектора.

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

получит свой change request, в котором нужно будет изменить метод X для сценария А, но не для сценария В

Вы правда считаете это проблемой? Это рядовая задача в программировании. Опишите бизнес-цель и я дам вам ряд изящных способов решить её.

Да, мы допустили ошибку, кто их не допускает?

Нет ничего плохого в ошибках. Плохо то, что вы делаете неправильный вывод. Вместо того, чтобы осознать и починить ошибки — вы обвиняете подходы. Всего лишь вместо того, чтобы ставить костыли необходимо было реструктуировать код так, чтобы он соответствовал изменившимся требованиям. Да, это рефакторинг, а заказчики не любят это слово, ибо они редко понимают, какие среднесрочные выгоды он дает. «Программировать необходимо, а не рефакторить». Вот только рефакторинг — неотъемлемая часть программирования. И пока вы это не осознаете — будете накладывать табу на все подходы по очереди, пока не останется никаких подходов вообще.
Безальтернативно объявлять наследование злом — конечно, неправильно. Но и обратная «абсолютная» точка зрения ничем не лучше. К примеру, еще во всем известной книге GoF по паттернам проектирования было аргументировано правило «предпочитайте композицию наследованию»: en.wikipedia.org/wiki/Composition_over_inheritance
Да, но она не про то, что «Наследование — зло», а о том, что в огромном количестве случаев наследование используется просто неправильно описывая, по сути связь «has a» вместо «is a».
Извините, но это бред. И абсолютно не аргументирован.
аргументов предостаточно, и я не автор этого высказывания, просто с ним согласен.
Порицается наследование уже давно и много кем. вот например. Повторюсь, есть даже язык программирования, где наследования изначально нет.
Вместо того, чтобы осознать и починить ошибки — вы обвиняете подходы.
Я не обвиняю дождь в том, что он мокрый. Я лишь говорю, что он мокрый. Из трех вариантов разбиения кода вариант с наследованием имеет наибольшую вязкость, я его лишь упомянул, но не стал приводить псевдокодовый пример, ибо он заведомо худший.
Вариант с агрегацией имеет наименьшую вязкость, т.е. наибольшую устойчивость к изменениям, он лучше читается и проще модифицируется. Это все, что «накипело».
То, что я предлагаю — в условиях часто меняющейся бизнес логики — выбирать наименее вязкое решение изначально.
Оно будет накапливать наименьшее количество технического долга из трех озвученных вариантов
Всего лишь… необходимо было реструктуировать код… Да, это рефакторинг,
Мыши кололись, плакали, но продолжали грызть кактус. Выбирая менее устойчивое к изменениям решение, вы чаще будете нуждаться в рефакторинге. Ваш технический долг будет расти быстрее, только и всего.
Плохо то, что вы делаете неправильный вывод.
Вязкость такая характеристика, которую не измерить линейкой, килобайтами и строчками кода. Она видна лишь с течением времени и лишь в условиях постоянно меняющихся требований.
Я эмпирически увидел закономерность. Случай далеко не один, это происходит раз за разом, от проекта к проекту. Каждый раз такой код сложно читать, каждый раз трудно понять где и какое изменение нужно внести, каждый раз такие компоненты являются эпицентрами технического долга. Я сделал вывод из этого. Отличный от вывода «вы слишком много гогнокодите, просто перестаньте, и рефакторите код почаще».
> На самом деле вы забыли ещё более приятный вариант — через полиморфизм. Стратегия — более узкий паттерн, чем универсальный метод для DRY. И полиформизм одновременно позволил бы избежать дублирования и не осложнил приложение как использование стратегии.

То, что вы противопоставляете стратегию полиморфизму, а примером полиморфизма приводите наследование, говорит мне о том, что наследование от полиморфизма вы не отличаете. Не надо называть наследование красивым словом «полиморфизм».

Ну и преимуществ у вашего кода над стратегией тоже никаких, кроме вашей уверенности, что так «не осложнили».
Вы правы, что «наследование» — более подходящий термин.

Я не противоставляю стратегию наследованию, я противоставлял свой код тому, что приводит автор. И сказал лишь о том, что стратегия не есть универсальное средство для DRY.

Перечитав мой комментарий я вижу, что его можно понять иначе чем я предполагал.

Про осложнение — иногда осложняют, но само по себе использование стратегии не является осложнением. Об этом я и говорю. Автор приводит историю вроде «использовали стратегию, потому код стал слишком сложен». Да, иногда переиспользование или неправильное использование паттернов делает код сложным, но в моем сообщении утверждается, что стратегия работала неправильно исключительно из-за огромного технического долга. Ровно так же сложен в поддержке будет и подход с наследованием и подход через «прямую агрегацию».

Тем не менее, как на меня, на ту задачу, что описал автор, идеально ложится обычное наследование, которое не используется, судя из топика, потому что Гугл в Гоу его не добавил.
Концепция «Composition over inheritance» не изобретение авторов Go, ей минимум лет 20
Кстати, я сделаю отсылку к Фаулеру, где он применяет термин «полиморфизм» именно в том смысле, в котором применил его я. «Рефакторинг», «Замена условного оператора полиморфизмом». И там есть интересная цитата:
Создать иерархию наследования можно двумя способами: «Заменой кода типа подклассами» ( Replace Type
Code with Subclasses) и «Заменой кода типа состоянием/стратегией» (Replace Type Code with State/Strategy).
Более простым вариантом является создание подклассов, поэтому по возможности следует выбирать его.
Однако если код типа изменяется после того, как создан объект, применять создание подклассов нельзя, и
необходимо применять паттерн «состояния/стратегии»


И эта статья, в целом, описывает мой подход, так что я не понимаю, почему вы прицепились к моему термину полиморфизм (хотя он и более многозначен, чем «наследование»).
Я вижу, что Фаулер
1. описывает другую ситуацию
2. Называет и стратегию полиморфизмом
2. Называет и стратегию полиморфизмом

Стратегию он называет «способом создать иерархию наследования».

1. описывает другую ситуацию

Почитайте в книге всю статью с соответствующим заголовком.
> Стратегию он называет «способом создать иерархию наследования»

Это переводчик шалит. Эта фраза звучит как «To create the inheritance structure you have two options...».
И для описания решаемой задачи ухода от ифчиков это его описание корректно. Любая стратегия подразумевает inheritance structure, если нужна inheritance structure, то стратегия — один из вариантов.

Но не надо отрывать это предложение от предыдущего абзаца. Там задача ставиться именно в такой формулировке, «Before you can begin with Replace Conditional with Polymorphism you need to have the necessary inheritance structure.». Поэтому и в процитированном тексте вылезает «способом создать иерархию наследования». И это не определение стратегии.

И в этих абзацах ни слова про сам полиморфизм.

Полиморфизм — это не свойство класса/иерархии, это свойство кода, работающего с иерархией. В отличии от LSP.
Конечно, никто и не говорит, что это определение стратегии. Откуда вы такое додумали? Процитированный мною участок был к тому, что, согласно Фаулеру, в общем «Наследование» проще чем «Стратегия». Я акцентировал это жирным. Конечно, есть широкий ряд ситуаций, где применение Стратегии более уместно и это описано в этом же абзаце книги.

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

В этих абзацах — ни слова, конечно. Зато вся статья «Замена условного оператора полиморфизмом» про полиморфизм.
> Процитированный мною участок был к тому, что, согласно Фаулеру, в общем «Наследование» проще чем «Стратегия»

После вводного

> Кстати, я сделаю отсылку к Фаулеру, где он применяет термин «полиморфизм» именно в том смысле, в котором применил его я.

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

Да, статья про полиморфизм. И там описывается стратегия, как один из вариантов применения полиморфизма. Если вы с Фаулером понимаете одинаково, то почему вы при этом комментируете статью про стратегии (которые и есть про полиморфизм) как «вы забыли ещё более приятный вариант — через полиморфизм» — я не очень понимаю.

То, что наследование выглядит проще, но требует переписывания при любом шаге в сторону я с Фаулером согласен. То, что постоянное переписывание наследования «не осложняет» — нет. И наследование испытание сложными случаями не выдержит, поэтому ваш подход весьма ограничен (о чём Фаулер тоже пишет), и под кейс статьи тем более не подходит именно потому, что саму проблему «быстро выяснил, что функционала контейнера недостаточно, а полноценный рефакторинг не вписывается в оценки» такой подход только усугубляет (по крайней мере по моему опыту).

Поэтому надо различать «проще» (по фаулеру) и «не осложняет» (у Вас). С первым я согласен, второе — требует обоснования.
я ожидал цитату именно с этим.

Сперва не поняли, теперь разобрались?)

стратегии (которые и есть про полиморфизм)

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

проблему «быстро выяснил, что функционала контейнера недостаточно, а полноценный рефакторинг не вписывается в оценки»

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

Сперва ты решаешь задачу простым и подходящим способом, а потом выносишь блоки, которые «не вписываются» и ищешь для них хорошие подходы.

ваш подход весьма ограничен

И это хорошо. Подход полностью покрывает описанное в статье. Абстрактные «шестая задача похожа на 89%» — это уже дополнительный вопрос, который можно обсудить на живом примере. Когда этого не хватает — тогда растем. Без рефакторинга при изменяемых требованиях любой код рано или поздно превратится в неподдерживаемый.
> Абстрактные «шестая задача похожа на 89%» — это уже дополнительный вопрос, который можно обсудить на живом примере.

Они не «абстрактные», это как раз то, что приводит к коллапсу. Первые 2 одинаково хорошо решались хоть наследованием, хоть стратегиями — там много не сэкономишь.
Когда нужно реализовать третий сценарий, который похож на первые два, но не на 100%, возникает желание переиспользовать код, содержащийся в контейнере. Но его не получится переиспользовать частично, можно лишь взять его целиком, поэтому приходится вносить изменения в контейнер, что сразу несет риск сломать другие сценарии.


Похоже не в стратегии дело, а в одновременном нарушении OCP и ISP — начинаем модифицировать вместо того, чтобы расширять, и создаём один общий интерфейс вместо того, чтобы добавить новый.

Если же вы в разработке используете паттерн strategy, и в контейнер бизнес-логика попала — знайте, линию вы уже перешагнули, и стоите по щиколотку в… ммм, болоте.


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

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


Ну вот с этим я не согласен. А сортировка строк ФИО по второй букве фамилии то же не будет бизнес логикой (при том, что если буквы совпадают начинается сортировка по имени)?
А сколько вариантов сортировок просто строк где могут быть текст+цифры? Это всё явно бизнес логика и она явно реализуется в контейнере.
Посему я и обычную сортировку чисел, хоть пузырьком хоть qsort так или иначе запишу в бизнес логику… просто с известными оптимальными реализациями.
Алгоритм сортировки сам по себе не специфичен, но он обычно может использовать алгоритм сравнения, который уже может быть как угодно специфичен и быть частью бизнес-логики.
Бизнес-логика моделирует бизнес-процессы и иного понимания мы не мыслим. Если мы используем сортировку для представления (сортировка фамилий, например, в табличке, чисто для удобства), то она не является частью бизнес-логики. Если мы используем сортировку для моделирования предметной области (например, финансовые транзакции сортируем по времени проведения), то это бизнес-логика.
Если же вы в разработке используете паттерн strategy, и в контейнер бизнес-логика попала — знайте, линию вы уже перешагнули,

Вообще-то, нет. Например, у нас есть бизнес-сценарий, расчет стоимости заказа, в котором в качестве стратегии используется компонент, отвечающий за то, отправляется заказ в один заход, или поштучно. Ничего ненормального или опасного в этом нет.
Я именно об этом и говорил. Пока ваша бизнес-логика неизменна, вам будет казаться, что ваше решение хорошее. Собственно в условиях неизменной бизнес-логики оно весьма вероятно действительно хорошее. Недостаток этого не в изначальной плохости, а в неустойчивости к изменениям.
Другими словами, вопрос в том, что вы будете делать, когда заказчик скажет, что ее надо изменить. Именно ту логику, которая в «контейнере». Причем для одного сценария, допустим для поштучного, а для остальных сценариев оставить так как есть.

И когда компонентом продолжает заниматься тот же разработчик, что его изначально создал, то он, зная его «наизусть», вероятно сможет его зарефакторить так, чтобы «не увеличить энтропию». Но при большой команде, скрам, канбан и все такое, история уходит свободному разработчику, а не «нужному». Скилл у него другой, мышление другое, результат не заставит себя ждать.
Другими словами, вопрос в том, что вы будете делать, когда заказчик скажет, что ее надо изменить. Именно ту логику, которая в «контейнере». Причем для одного сценария, допустим для поштучного, а для остальных сценариев оставить так как есть.

Если разделение ответственностей между компонентами было изначально верным, такой сценарий маловероятен. Но если он все-таки случился — значит, либо мы ошиблись изначально, либо распределение поменялось. Нормальная ситуация, перепишем часть кода.

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

Если у вас в команде есть такие проблемы, то они будут проявляться на любых шаблонах, и даже вовсе без шаблонов. Надо бороться с тем, что «мышления и скиллы» у разных разработчиков конфликтуют.
Если разделение ответственностей между компонентами было изначально верным,
Как же можно узнать наперед, какие задачи в будущем будет ставить бизнес. Разделение ответственности основано на допущениях. И чем больше допущений, тем больше вероятность, что одно из них окажется неверным.
Если у вас в команде есть такие проблемы, то они будут проявляться на любых шаблонах, Надо бороться с тем, что «мышления и скиллы» у разных разработчиков конфликтуют.
Клонировать людей? Бороться с вязкостью как мне кажется проще, чем бороться с тем, что все люди разные.
Как же можно узнать наперед, какие задачи в будущем будет ставить бизнес.

Основываясь на предыдущем опыте. Но вообще — никак, конечно. Что, как ни странно, не мешает строить модифицируемые системы.

Клонировать людей?

Зачем? Просто обучать.

Бороться с вязкостью как мне кажется проще, чем бороться с тем, что все люди разные.

А вам не надо бороться с тем, что люди разные, вам надо учить их решать задачи в рамках одной системы совместимым путем. Это проблема из той же области, что и «использовать пробелы или табы», или, более обще, coding style, только уровнем повыше.
Как же можно узнать наперед, какие задачи в будущем будет ставить бизнес.

Так и не нужно это. Необходимо исходить из существующих и известных задач. А по мере изменений требований к системе — изменять саму систему.
Если новый разработчик начнёт изменять существующие стратегии, например, добавляя заглушки, значит он не понял задание — сказано же «для остальных сценариев оставить так как есть» :) А если серьёзно, то ситуация «пять сценариев похожи на 90%, а шестой лишь на 10%» — это повод вводить особый случай или двухуровневые стратегии. Как вариант — отказаться от стратегий вообще.
«пять сценариев похожи на 90%, а шестой лишь на 10%»
Предполагалось, что шестой на 85%. Ну или, скажем 89%. Как в том анекдоте «N мало, пусть будет M»©.
Вобщем достаточное совпадение, чтобы захотелось переиспользовать, а оставшийся процентик реализовать воткнув условный оператор.
Вот и надо его втыкать где-то выше, раз даже прямое указание есть «для остальных сценариев оставить так как есть», не говоря уж об открытости для расширения и закрытости для модификации.
Но при большой команде, скрам, канбан и все такое, история уходит свободному разработчику, а не «нужному». Скилл у него другой, мышление другое, результат не заставит себя ждать.


Вот по этому я ярый противник этих подходов в разработке. Или как минимум близкое следованием их гайдлайнам.
В программировании UI есть один тонкий момент — нужно уметь различать одинаковые части интерфейса и похожие части интерфейса.

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

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

Наивно думать, что, при таком подходе, после пары итераций «Вариант через прямую агрегацию» не превратиться в 50 строчек копипасты.

Так что это в кансерватории проблемы. Ну там, если вы генерити код страничек из БЛ — стратегия вам не поможет, конечно.
Когда в угоду DRY, сам того не замечая, разработчик жертвует принципом SRP

Скорее, «пытается применить DRY, игнорируя SRP, а потом удивляется, почему получилась тяжеловесная конструкция, которая тем не менее валится от малейшего неосторожного движения».
Есть мнение из лагеря Clojure, что проблема не в DRY самом по себе, а в DRY + мейнстрим ООП.

То есть DRY + наследование кода, жестко привязанное к иерархии.

Как результат, ваш «вариант через прямую агрегацию» весьма напоминает функциональное решение проблемы. В функциональном варианте аналог «стратегии» был бы первоклассной функцией, передаваемой как параметр.
Ну, ООП без интерфейсов — это вообще беда. Лечится множественным наследованием, но с большими побочными эффектами. Ну или пиханием агрегации всюду, куда только можно. Иначе — «God Object» и забудьте про SRP.
Правило, по которому один элемент равен другому или больше-меньше есть бизнес-логика.
Почему-то сразу вспомнились интерфейсы (=«стратегии» на языке дизайн-паттернов) Comparable и Comparator из Java. Помню, писал компараторы даже для Java-методов для одного интерпретатора. Ничего, нормально всё сработало. Магическое слово «бизнес-логика» ещё не повод не выносить алгоритм сравнения в «стратегию».
Sign up to leave a comment.

Articles