Шаблоны проектирования: шаблон Стратегия в JavaScript

Spread the love

Существует 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 Pattern

Каждая стратегия представлена с использованием конкретного объекта. Таким образом, клиент/контекст содержит объект Strategy (concreteStrategyA, concreteStrategyB, …), который реализует стратегию в интерфейсе. Ключ взаимообмена между стратегиями состоит в том, чтобы реализовать метод в контексте, который меняет стратегию, например, setStrategy.

Когда использовать шаблон

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

Преимущества шаблона

Шаблон Стратегия имеет несколько преимуществ, которые можно обобщить в следующих пунктах:

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

Базовая реализация с использованием 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-CaballeroDesign Patterns: Strategy Pattern in JavaScript

Была ли вам полезна эта статья?
[1 / 5]

Spread the love
Подписаться
Уведомление о
guest
1 Комментарий
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Вячелсав
Вячелсав
5 лет назад

В node.js есть удобный пакет для создания авторизации passport.js. Можно выбрать много стратегий для аунтификации passport-local, passport-jwt, passport-facebook . Это же все реализовано с помощью данного шаблона проектирования? Правильно понимаю?