Шаблон Стратегии хорошо работает, чтобы избежать огромных конструкций if ... else и облегчить добавление или замену функциональности. Тем не менее, это все еще оставляет один недостаток, на мой взгляд. Кажется, что в каждой реализации все еще должна быть ветвящаяся конструкция. Это может быть фабрика или файл данных. В качестве примера возьмем систему заказов.
Фабрика:
// All of these classes implement OrderStrategy
switch (orderType) {
case NEW_ORDER: return new NewOrder();
case CANCELLATION: return new Cancellation();
case RETURN: return new Return();
}
Код после этого не должен беспокоиться, и теперь есть только одно место для добавления нового типа заказа, но этот раздел кода по-прежнему не расширяем. Вытащив его в файл данных помогает читаемость несколько (спорно, я знаю):
<strategies>
<order type="NEW_ORDER">com.company.NewOrder</order>
<order type="CANCELLATION">com.company.Cancellation</order>
<order type="RETURN">com.company.Return</order>
</strategies>
Но это все еще добавляет стандартный код для обработки файла данных - предоставляется, легче тестируется на модуле и относительно стабильный код, но тем не менее дополнительная сложность.
Кроме того, этот вид конструкции плохо тестирует интеграцию. Каждую отдельную стратегию может быть проще тестировать, но каждая новая добавленная вами стратегия - сложность тестирования. Это меньше, чем если бы вы не использовали шаблон, но он все еще там.
Есть ли способ реализовать шаблон стратегии, который смягчает эту сложность? Или это настолько просто, насколько это возможно, и попытка пойти дальше будет только добавить еще один уровень абстракции для небольшой или бесполезной выгоды?
eval
... может не работать на Java, но может быть на других языках?Ответы:
Конечно, нет. Даже если вы используете контейнер IoC, вам придется где-то иметь условия, решающие, какую конкретную реализацию внедрять. Это природа паттерна Стратегия.
Я действительно не понимаю, почему люди думают, что это проблема. В некоторых книгах есть утверждения, например, «Рефакторинг Фаулера» , что если вы видите переключатель / регистр или цепочку if / elses в середине другого кода, вам следует подумать об этом запахе и попытаться перенести его в свой собственный метод. Если код в каждом случае больше, чем строка, может быть, две, то вы должны рассмотреть возможность сделать этот метод фабричным методом, возвращающим стратегии.
Некоторые люди считают, что переключение / случай - это плохо. Это не вариант. Но он должен жить самостоятельно, если это возможно.
источник
Да , используя хэш-карту / словарь, где каждая реализация Стратегии регистрируется в. Заводской метод станет чем-то вроде
Каждая реализация стратегии должна регистрировать фабрику с ее orderType и некоторой информацией о том, как создать класс.
Вы можете использовать статический конструктор для регистрации, если ваш язык поддерживает это.
Метод register ничего не делает, кроме добавления нового значения в hashmap:
[обновление 2012-05-04]
Это решение намного сложнее, чем оригинальное «решение по коммутации», которое я бы предпочел большую часть времени.
Однако в среде, где стратегии часто меняются (например, расчет цены в зависимости от клиента, времени, ....), это решение с хэш-картой в сочетании с IoC-контейнером может быть хорошим решением.
источник
factory.register(NEW_ORDER, NewOrder.class);
чище или менее нарушают OCP, чем рядыcase NEW_ORDER: return new NewOrder();
?«Стратегия» - это необходимость выбирать между альтернативными алгоритмами хотя бы один раз , а не меньше. Где-то в вашей программе кто-то должен принять решение - может быть, пользователь или ваша программа. Если вы используете IoC, отражение, средство оценки файла данных или конструкцию switch / case для реализации этого не изменит ситуацию.
источник
Шаблон стратегии используется, когда вы указываете возможные варианты поведения, и лучше всего используется при назначении обработчика при запуске. Указание того, какой экземпляр стратегии использовать, можно выполнить с помощью Mediator, вашего стандартного контейнера IoC, некоторого Фабрики, подобного описанному вами, или просто с помощью правильного, основанного на контексте (так как часто стратегия поставляется как часть более широкого использования). класса, который содержит его).
Для такого поведения, когда разные методы должны вызываться на основе данных, я бы предложил использовать конструкции полиморфизма, предоставляемые языком под рукой; не шаблон стратегии.
источник
Общаясь с другими разработчиками, где я работаю, я нашел другое интересное решение - специфическое для Java, но я уверен, что идея работает на других языках. Создайте перечисление (или карту, неважно, какая именно), используя ссылки на класс, и создайте экземпляр, используя отражение.
Это резко уменьшает заводской код:
(Пожалуйста, игнорируйте плохую обработку ошибок - пример кода :))
Это не самое гибкое решение , так как сборка все еще требуется ... но сокращает изменение кода до одной строки. Мне нравится, что данные отделены от заводского кода. Вы даже можете делать такие вещи, как установка параметров, либо используя абстрактные классы / интерфейсы для предоставления общего метода, либо создавая аннотации во время компиляции для принудительного использования определенных сигнатур конструктора.
источник
Расширяемость может быть улучшена, если каждый класс определяет свой собственный тип заказа. Затем ваш завод выбирает тот, который соответствует.
Например:
источник
Я думаю, что должно быть ограничение на количество приемлемых веток.
Например, если в моем операторе switch есть более восьми случаев, я переоцениваю свой код и ищу то, что я могу повторно пересчитать. Довольно часто я нахожу, что определенные случаи могут быть сгруппированы в отдельную фабрику. Мое предположение здесь состоит в том, что есть фабрика, которая строит стратегии.
В любом случае, вы не можете избежать этого, и где-то вам придется проверить состояние или тип объекта, с которым вы работаете. Затем вы создадите стратегию для этого. Старое доброе разделение проблем.
источник