Почему полезно использовать шаблон стратегии, если вы можете просто написать свой код в случаях if / then?
Например: у меня есть класс TaxPayer, и один из его методов рассчитывает налоги, используя разные алгоритмы. Так почему же он не может иметь if / then случаев и выяснить, какой алгоритм использовать в этом методе, вместо использования шаблона стратегии? Кроме того, почему вы не можете просто реализовать отдельный метод для каждого алгоритма в классе TaxPayer?
Кроме того, что означает изменение алгоритма во время выполнения?
Ответы:
С одной стороны, большие скопления
if/else
блоков не легко проверить . Каждая новая «ветвь» добавляет другой путь выполнения и, таким образом, увеличивает цикломатическую сложность . Если вы хотите тщательно протестировать свой код, вам нужно будет охватить все пути выполнения, и каждое условие потребует от вас написать хотя бы еще один тест (при условии, что вы пишете небольшие, сфокусированные тесты). С другой стороны, классы, которые реализуют стратегии, обычно предоставляют только один публичный метод, который легко протестировать.Таким образом, с вложенными
if/else
вы получите множество тестов для одной части вашего кода, в то время как со Стратегией у вас будет несколько тестов для каждой из нескольких более простых стратегий. С последним легче получить лучший охват, потому что труднее пропустить пути выполнения.Что касается расширяемости , представьте, что вы пишете фреймворк, в котором пользователи должны иметь возможность внедрять свое собственное поведение. Например, вы хотите создать некую структуру налоговых расчетов и хотите поддерживать налоговые системы разных стран. Вместо того, чтобы реализовывать их все, вы просто хотите дать пользователям фреймворка возможность представить, как рассчитывать некоторые конкретные налоги.
Вот образец стратегии:
TaxCalculation
, и ваша структура принимает экземпляры этого типа для расчета налоговВы не можете сделать то же самое с
if/else
, потому что это потребует изменения кода фреймворка, и в этом случае он больше не будет фреймворком. Поскольку платформы часто распространяются в скомпилированном виде, это может быть единственным вариантом.Тем не менее, даже если вы просто пишете какой-то обычный код, стратегия выгодна, потому что она проясняет ваши намерения. В нем говорится «эта логика является подключаемой и условной», то есть может быть несколько реализаций, которые могут варьироваться в зависимости от действий пользователя, конфигурации или даже платформы.
Использование шаблона «Стратегия» может улучшить читабельность, потому что, хотя класс, который реализует какую-то конкретную стратегию, обычно должен иметь описательное имя, например
USAIncomeTaxCalculator
,if/else
блоки являются «безымянными», в лучшем случае просто прокомментированными, а комментарии могут лгать. Кроме того, по моему личному вкусу, просто более трехif/else
блоков подряд не читаются, и это плохо работает с вложенными блоками.Принцип Open / Closed также очень важен, потому что, как я описал в примере выше, Strategy позволяет расширять логику в некоторых частях вашего кода («открыто для расширения»), не переписывая эти части («закрыто для модификации»). ).
источник
if/else
блоки также снижают читаемость кода. Что касается стратегии, принцип Open / Closed стоит упомянуть IMO.if
вас есть, тем больше возможных путей существует в вашем коде, тем больше тестов вы должны написать и тем больше путей для сбоя этого метода. Если я могу процитировать покойного Йоги Берры: «Если вы придете к развилке на дороге, возьмите ее». Это блестяще относится к юнит-тестированию. Более того, многиеif
утверждения означают, что вы, скорее всего, будете повторять логику для этих условий, еще больше увеличивая тестовую нагрузку и увеличивая риск появления ошибок.if/else
блоков для их вызова (или внутри них, чтобы определить, должно ли оно что-то делать или нет), так что это не очень помогает, за исключением, возможно, более читабельного кода. А также нет расширяемости для пользователей вашей гипотетической структуры тогда.Иногда вы должны просто использовать if / then. Это простой код, который легко читать.
Две основные проблемы с простым кодом if / then заключаются в том, что он может нарушать принцип открытого закрытого типа . Если вам когда-либо понадобится добавить или изменить условие, вы модифицируете этот код. Если вы ожидаете, что будет больше условий, просто добавить новую стратегию проще / чище / с меньшей вероятностью что-то сломать.
Другая проблема - это связь. Используя if / then, все реализации привязываются к этой реализации, что затрудняет их изменение в будущем. Используя стратегию, единственное соединение - это интерфейс стратегии.
источник
Стратегия полезна, когда
if/then
условия основаны на типах , как описано в http://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.htmlУсловные проверки типов обычно не имеют большой цикломатической сложности, поэтому я бы не сказал, что Strategy обязательно улучшит ситуацию там.
Основная причина Стратегии объяснена в книге GoF с.316, в которой представлен шаблон:
Как упоминалось в других ответах, при правильном применении шаблон «Стратегия» позволяет добавлять новые расширения (конкретные стратегии) без необходимости изменения остальной части кода. Это так называемый принцип Open-Closed или Protected Variations . Конечно, вам все равно придется кодировать новую конкретную стратегию, а клиентский код должен распознавать стратегии как плагины (это не тривиально).
В случае
if/then
условных выражений необходимо изменить код класса, содержащего условную логику. Как упоминалось в других ответах, иногда это нормально, если вы не хотите добавлять сложность для поддержки добавления новых функций (плагинов) без перекомпиляции.источник
Это как раз самое большое преимущество стратегического паттерна. Не имея условий.
Вы хотите, чтобы ваши классы / методы / функции были максимально простыми и короткими. Короткий код очень легко проверить и очень легко прочитать.
Условия (
if
/elseif
/else
) делают ваши классы / методы / функции длинными, потому что обычно код, в котором оценивается одно решениеtrue
, отличается от части, в которой оценивается решениеfalse
.Еще одно большое преимущество шаблона стратегии заключается в том, что его можно использовать на протяжении всего вашего проекта.
При использовании шаблона разработки стратегии очень вероятно, что у вас будет какой-то контейнер IoC, из которого вы получаете желаемую реализацию интерфейса, возможно, с помощью
getById(int id)
метода, где онid
может быть членом перечислителя.Это означает, что создание реализации находится только в одном месте вашего кода.
Если вы хотите добавить больше реализаций, вы добавляете новую реализацию в
getById
метод, и это изменение отражается везде в коде, где вы его вызываете.С
if
/elseif
/else
это невозможно сделать. Добавляя новую реализацию, вы должны добавить новыйelseif
блок и делать это везде, где использовались реализации, или вы можете получить код, который является недопустимым, потому что вы забыли добавить реализацию в его структуру.В моем примере,
id
может быть переменная, которая заполняется на основе пользовательского ввода. Если пользователь нажимает кнопку A, тоid = 2
, если он нажимает кнопку B, тогдаid = 8
.Из-за разного
id
значения из контейнера IoC получается другая реализация интерфейса, и код выполняет разные операции.источник
if
/elseif
/else
состояние. Так же, как и раньше, просто в другом месте.id
переменную вgetById
методе, которая вернула бы конкретную реализацию. Каждый раз, когда вам потребуется реализация интерфейса, вы просите контейнер IoC доставить его вам.getSortByEnumType(SortEnum type)
возвращающий реализациюSort
интерфейса, иметь метод,getSortType
возвращающийSortEnum
переменную и принимающий коллекцию в качестве параметра, иgetSortByEnumType
метод снова будет содержатьtype
параметр, возвращающий вам правильный алгоритм сортировки. Если вам нужно было добавить новый алгоритм сортировки, вам нужно отредактировать только перечисление и один метод. И вы настроены.Шаблон стратегии позволяет вам отделить ваши алгоритмы (детали) от бизнес-логики (политики высокого уровня). Эти две вещи не только сбивают с толку, когда их смешивают, но и имеют совершенно разные причины для изменения.
Здесь также важен фактор масштабируемости командной работы. Представьте себе большую команду программистов, в которой много людей работают над этим бухгалтерским пакетом. Если все налоговые алгоритмы находятся в классе или модуле TaxPayer, то возникает вероятность слияния. Конфликты слияний занимают много времени и подвержены ошибкам. Это время снижает производительность труда команды, а ошибки, вызванные плохими слияниями, наносят ущерб надежности клиентов.
Алгоритм, который изменяется во время выполнения, - это алгоритм, поведение которого определяется конфигурацией или контекстом. Если / то подойти на месте не эффективно включить это , поскольку предполагает перегрузку существующих активно используемых классов. С помощью паттерна стратегии объекты стратегии, реализующие каждый алгоритм, могут быть сконструированы при использовании. В результате изменения этих алгоритмов (исправления ошибок или улучшения) могут быть внесены и перезагружены во время выполнения. Этот подход можно использовать для обеспечения непрерывной доступности и нулевого времени простоя релизов.
источник
В этом нет ничего плохого
if/else
. Во многих случаяхif/else
это самый простой и читаемый способ выражения логики. Таким образом, подход, который вы описываете, является вполне допустимым во многих случаях. (Это также отлично проверяется, так что это не проблема.)Но есть некоторые конкретные случаи, когда шаблон стратегии может улучшить удобство сопровождения всего кода. Например:
Чтобы шаблон стратегии имел смысл, интерфейс между базовой логикой и алгоритмами расчета налога должен быть более стабильным, чем отдельные компоненты. Если с такой же вероятностью изменение требований приведет к изменению интерфейса, тогда модель стратегии может фактически быть пассивом.
Все сводится к тому, что «алгоритмы расчета налогов» могут быть четко отделены от основной логики, которая их вызывает. Шаблон стратегии имеет некоторые накладные расходы по сравнению с шаблоном
if/else
, поэтому вам придется самостоятельно решать, стоит ли инвестировать.источник