Шаблоны проектирования: шаблон Стратегия в JavaScript
Существует 23 классических шаблона проектирования, которые были описаны в книге «Design Patterns: Elements of Reusable Object-Oriented Software». Шаблоны предлагают путь решения конкретных повторяющихся проблем, при разработке программного обеспечения.
В этой статье я собираюсь описать шаблон Стратегия (Strategy Pattern), как он работает, как и когда его следует применять. Этот шаблон так же известен как Policy.
Шаблон стратегии: основная идея
Шаблон стратегии — это поведенческий шаблон, который позволяет выбирать алгоритм во время выполнения программы — Википедия
Определите семейство алгоритмов, инкапсулируйте каждый и сделайте их взаимозаменяемыми.
Стратегия позволяет алгоритму варьироваться независимо от клиентов, которые его используют — Design Patterns: Elements of Reusable Object-Oriented Software
Главной особенностью этого шаблона является то, что у клиента есть набор алгоритмов, из которых будет выбран конкретный алгоритм для использования во время выполнения. Эти алгоритмы взаимозаменяемы.
Следующий код демонстрирует классическую проблему, в которой нужно выбрать конкретный алгоритм в приложении. В этом коде используется управляющая структура switch
.
myMethod() { switch (this._strategySelected) { case SimpleStrategy: ComposeWithSimpleStrategy(); break; case TeXStrategy: ComposeWithTeXStrategy(); break; //... } // merge results with existing composition, if necessary }
Тем не менее, этот код может быть более гибким, используя шаблон стратегия, который будет иметь следующую структуру:
myMethod() { this._strategySelected->Compose(); // merge results with existing composition, if necessary }
UML-схема этого шаблона выглядит следующим образом:
Каждая стратегия представлена с использованием конкретного объекта. Таким образом, клиент/контекст содержит объект Strategy (concreteStrategyA, concreteStrategyB, …), который реализует стратегию в интерфейсе. Ключ взаимообмена между стратегиями состоит в том, чтобы реализовать метод в контексте, который меняет стратегию, например, setStrategy.
Когда использовать шаблон
- Задача, которую решает шаблон Стратегия, заключается в создание механизма выбора, одного алгоритма из несколько алгоритмов, которые имеют разные варианты.
- Типичным примером использования шаблона — это когда в коде есть множество условных операторов вокруг нескольких алгоритмов, которые связаны между собой.
- Так же шаблон может использоваться, когда большинство ваших классов имеют сходное поведение.
Преимущества шаблона
Шаблон Стратегия имеет несколько преимуществ, которые можно обобщить в следующих пунктах:
- Позволяет легко переключаться между различными алгоритмами (стратегиями) во время выполнения.
- Код становиться более чистым, потому что позволяет избегать загрязнением большим количество условных операторов.
- Так же код становиться более чистым, потому что позволяет разделять проблемы на классы (свой класс для каждой стратегии).
Базовая реализация с использованием JavaScript
Теперь я покажу вам, как вы можете реализовать этот шаблон с использованием JavaScript, вы должны помнить, что в Javascript отсутствуют интерфейсы. Поэтому, мы используем класс с именем StrategyManager, в качестве интерфейсов:
class StrategyManager { constructor() { this._strategy = null; } set strategy(strategy) { this._strategy = strategy; } get strategy() { return this._strategy; } doAction() { this._strategy.doAction(); } }
Этот класс содержит закрытый атрибут _strategy, который представляет стратегию, которая будет использоваться в данный момент. Метод doAction — это метод, который будет реализован в каждой конкретной Стратегии. Шаблон «Стратегия» отличается от UML в JavaScript из-за отсутствия в языке функций ООП.
Реализация каждой конкретной Стратегии выглядит следующим образом:
class Strategy1 { doAction() { console.log('Strategy1'); } } class Strategy2 { doAction() { console.log('Strategy2'); } }
Обратите внимание, что конкретный метод doAction реализован в каждой конкретной стратегии.
Наконец, контекст/клиент должен содержать StrategyManager, чтобы использовать конкретную стратегию:
const strategyManager = new StrategyManager(); const strategy1 = new Strategy1(); const strategy2 = new Strategy2(); strategyManager.strategy = strategy1; //Assign Strategy1; strategyManager.doAction(); strategyManager.strategy = strategy2; // Assign Strategy2; strategyManager.doAction();
Набор стратегий с использованием JavaScript
В следующей реализации наш StrategyManager будет более сложным и будет содержать список алгоритмов. В этом случае у нас будет массива _strategies вместо атрибута _strategy .
Добавим новые стратегии в наш список стратегий, используя метод addStrategy. Класс Strategy имеет два атрибута: 1) name — название стратегии; 2) handler — обработчик стратегии. Метод doAction используется для вызова конкретного алгоритма.
class StrategyManager { constructor() { this._strategies = []; } addStrategy(strategy) { this._strategies = [...this._strategies, strategy]; } getStrategy(name) { return this._strategies.find(strategy => strategy._name === name); } } class Strategy { constructor(name, handler) { this._name = name; this._handler = handler; } doAction() { this._handler(); } }
Наконец, клиент/контекстный код, в котором мы используем конкретную стратегию, выглядит следующим образом:
// Client-Context const strategyManager = new StrategyManager(); const strategy1 = new Strategy('strategy1', () => console.log('Strategy1')); const strategy2 = new Strategy('strategy2', () => console.log('Strategy2')); strategyManager.addStrategy(strategy1); strategyManager.addStrategy(strategy2); // Выбор первой стратегии. const strategyA = strategyManager.getStrategy('strategy1'); strategyA.doAction(); // Выбор второй стратегии. const strategyB = strategyManager.getStrategy('strategy2'); strategyB.doAction(); // Выбо не существующей стратегии. const strategyC = strategyManager.getStrategy('strategy3'); try { strategyC.doAction(); } catch (err) { console.error('Caught Error'); console.error(err); }
Первая часть — это создание конкретных стратегий (которые могут быть построены с использованием шаблона Singleton и шаблона Factory) и добавление в наш strategyManager (который может быть нашим интерфейсом). Следующая часть клиента выбирает стратегию для использования, эта стратегия может быть выбрана с помощью GUI или CLI из нашего приложения.
Наконец, вы можете заметить, что если выбрана неподдерживаемая стратегия, система возвращает ошибку.
Заключение
Стратегия — это шаблон, который позволяет избегать сложных условных конструкций в вашем коде, при выборе конкретного алгоритма. В этом посте рассмотрена простая реализацию с использованием языка JavaScript, в котором отсутствуют интерфейсы. В случае, если вы используете язык программирования, который имеет интерфейсы, вы можете следовать UML шаблону.
В конце концов самое главное — не реализация шаблона, (например так как я вам показал), а важно знать, в чем заключается проблема, которую решает шаблон, и почему вы должны использовать его, а сама реализация может отличаться в зависимости от языка программирования.
Оригинальная статья: Carlos-Caballero — Design Patterns: Strategy Pattern in JavaScript
В node.js есть удобный пакет для создания авторизации passport.js. Можно выбрать много стратегий для аунтификации passport-local, passport-jwt, passport-facebook . Это же все реализовано с помощью данного шаблона проектирования? Правильно понимаю?