Шаблон стратегии - Strategy pattern

В компьютерное программирование, то шаблон стратегии (также известный как шаблон политики) это поведенческий шаблон разработки программного обеспечения что позволяет выбрать алгоритм во время выполнения. Вместо непосредственной реализации одного алгоритма код получает инструкции во время выполнения относительно того, какой из семейства алгоритмов использовать.[1]

Стратегия позволяет алгоритму изменяться независимо от клиентов, которые его используют.[2] Стратегия - один из паттернов, вошедших во влиятельную книгу Шаблоны проектирования от Gamma et al.[3] это популяризировало концепцию использования шаблонов проектирования для описания того, как разрабатывать гибкое и многократно используемое объектно-ориентированное программное обеспечение. Откладывание решения о том, какой алгоритм использовать до времени выполнения, позволяет вызывающему коду быть более гибким и многоразовым.

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

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

Структура

Схема классов и последовательности UML

Образец класса UML и диаграмма последовательности для шаблона разработки стратегии. [4]

В приведенном выше UML диаграмма классов, то Контекст класс не реализует алгоритм напрямую. Контекст относится к Стратегия интерфейс для выполнения алгоритма (strategy.algorithm ()), что делает Контекст независимо от того, как реализован алгоритм. Стратегия1 и Стратегия2 классы реализуют Стратегия интерфейс, то есть реализовать (инкапсулировать) алгоритм.
В UML схема последовательности показывает взаимодействия во время выполнения: Контекст объект делегирует алгоритм разным Стратегия объекты. Первый, Контекст звонки алгоритм() на Стратегия1 объект, который выполняет алгоритм и возвращает результат в Контекст. После этого Контекст меняет свою стратегию и призывает алгоритм() на Стратегия2 объект, который выполняет алгоритм и возвращает результат в Контекст.

Диаграмма классов

Шаблон стратегии в UML

[5]

Шаблон стратегии в LePUS3 (легенда )

пример

C #

Следующий пример находится в C #.

общественный класс Стратегия{    общественный статический пустота Основной(Строка[] аргументы)    {        // Готовим стратегии        вар normalStrategy    = новый Нормальная стратегия();        вар happyHourStrategy = новый HappyHourStrategy();        вар firstCustomer = новый КлиентСчет(normalStrategy);        // Обычный биллинг        firstCustomer.Добавить(1.0, 1);        // Начинаем счастливый час        firstCustomer.Стратегия = happyHourStrategy;        firstCustomer.Добавить(1.0, 2);        // Новый покупатель        КлиентСчет второйКлиент = новый КлиентСчет(happyHourStrategy);        второйКлиент.Добавить(0.8, 1);        // Клиент платит        firstCustomer.Распечатать();        // Конец счастливого часа        второйКлиент.Стратегия = normalStrategy;        второйКлиент.Добавить(1.3, 2);        второйКлиент.Добавить(2.5, 1);        второйКлиент.Распечатать();    }}// CustomerBill в качестве имени класса, поскольку он имеет непосредственное отношение к счету клиентакласс КлиентСчет{    частный IList<двойной> напитки;    // Получить / установить стратегию    общественный IBillingStrategy Стратегия { получать; набор; }    общественный КлиентСчет(IBillingStrategy стратегия)    {        этот.напитки = новый Список<двойной>();        этот.Стратегия = стратегия;    }    общественный пустота Добавить(двойной цена, int количество)    {        этот.напитки.Добавить(этот.Стратегия.GetActPrice(цена * количество));    }    // Оплата счета    общественный пустота Распечатать()    {        двойной сумма = 0;        для каждого (вар питьСтоимость в этот.напитки)        {            сумма += питьСтоимость;        }        Консоль.WriteLine($"Итого: {сумма}".);        этот.напитки.ясно();    }}интерфейс IBillingStrategy{    двойной GetActPrice(двойной rawPrice);}// Обычная стратегия биллинга (цена без изменений)класс Нормальная стратегия : IBillingStrategy{    общественный двойной GetActPrice(двойной rawPrice) => rawPrice;}// Стратегия счастливого часа (скидка 50%)класс HappyHourStrategy : IBillingStrategy{    общественный двойной GetActPrice(двойной rawPrice) => rawPrice * 0.5;}

Ява

Следующий пример находится в Ява.

импорт java.util.ArrayList;интерфейс BillingStrategy {    // Используйте цену в центах, чтобы избежать ошибки округления с плавающей запятой    int getActPrice(int rawPrice);      // Обычная стратегия биллинга (цена без изменений)    статический BillingStrategy normalStrategy() {        вернуть rawPrice -> rawPrice;    }      // Стратегия счастливого часа (скидка 50%)    статический BillingStrategy happyHourStrategy() {        вернуть rawPrice -> rawPrice / 2;    }}класс КлиентСчет {    частный окончательный Список<Целое число> напитки = новый ArrayList<>();    частный BillingStrategy стратегия;    общественный КлиентСчет(BillingStrategy стратегия) {        этот.стратегия = стратегия;    }    общественный пустота Добавить(int цена, int количество) {        этот.напитки.Добавить(этот.стратегия.getActPrice(цена*количество));    }    // Оплата счета    общественный пустота Распечатать() {        int сумма = этот.напитки.ручей().mapToInt(v -> v).сумма();        Система.вне.println(«Итого:» + сумма);        этот.напитки.Чисто();    }    // Установить стратегию    общественный пустота setStrategy(BillingStrategy стратегия) {        этот.стратегия = стратегия;    }}общественный класс Стратегия {    общественный статический пустота основной(Строка[] аргументы) {        // Готовим стратегии        BillingStrategy normalStrategy    = BillingStrategy.normalStrategy();        BillingStrategy happyHourStrategy = BillingStrategy.happyHourStrategy();        КлиентСчет firstCustomer = новый КлиентСчет(normalStrategy);        // Обычный биллинг        firstCustomer.Добавить(100, 1);        // Начинаем счастливый час        firstCustomer.setStrategy(happyHourStrategy);        firstCustomer.Добавить(100, 2);        // Новый покупатель        КлиентСчет второйКлиент = новый КлиентСчет(happyHourStrategy);        второйКлиент.Добавить(80, 1);        // Клиент платит        firstCustomer.Распечатать();        // Конец счастливого часа        второйКлиент.setStrategy(normalStrategy);        второйКлиент.Добавить(130, 2);        второйКлиент.Добавить(250, 1);        второйКлиент.Распечатать();    }}

Стратегия и принцип открытости / закрытости

Ускорение и тормозить поведение должно быть объявлено в каждом новом Модель автомобиля.

Согласно шаблону стратегии поведение класса не должно наследоваться. Вместо этого их следует инкапсулировать с помощью интерфейсов. Это совместимо с открытый / закрытый принцип (OCP), который предлагает, чтобы классы были открыты для расширения, но закрыты для модификации.

В качестве примера рассмотрим класс автомобиля. Две возможные функции для автомобиля: тормозить и ускоряться. Поскольку поведение при ускорении и торможении часто меняется от модели к модели, общий подход заключается в реализации этого поведения в подклассах. У этого подхода есть существенные недостатки: поведение при ускорении и торможении необходимо декларировать в каждой новой модели автомобиля. Работа по управлению этим поведением значительно возрастает по мере увеличения количества моделей и требует дублирования кода для разных моделей. Кроме того, нелегко определить точный характер поведения каждой модели, не исследуя код каждой из них.

Шаблон стратегии использует композиция вместо наследования. В шаблоне стратегии поведения определяются как отдельные интерфейсы и конкретные классы, реализующие эти интерфейсы. Это позволяет лучше отделить поведение от класса, который его использует. Поведение можно изменить, не нарушая классы, которые его используют, и классы могут переключаться между поведениями, изменяя конкретную используемую реализацию, не требуя каких-либо значительных изменений кода. Поведение также можно изменить во время выполнения, а также во время разработки. Например, поведение при торможении автомобильного объекта можно изменить с BrakeWithABS () к Тормоз () путем изменения тормоз член:

тормоз = новый Тормоз();
/ * Инкапсулированное семейство алгоритмов * Интерфейс и его реализации */общественный интерфейс IBrakeBehavior {    общественный пустота тормозить();}общественный класс Тормозить с ABS орудия IBrakeBehavior {    общественный пустота тормозить() {        Система.вне.println(«Тормоз с включенной АБС»);    }}общественный класс Тормоз орудия IBrakeBehavior {    общественный пустота тормозить() {        Система.вне.println("Простой тормоз включен");    }}/ * Клиент, который может использовать вышеуказанные алгоритмы взаимозаменяемо * /общественный Абстрактные класс Машина {    частный IBrakeBehavior тормоз;    общественный Машина(IBrakeBehavior тормоз) {      этот.тормоз = тормоз;    }    общественный пустота applyBrake() {        тормоз.тормозить();    }    общественный пустота setBrakeBehavior(IBrakeBehavior BrakeType) {        этот.тормоз = BrakeType;    }}/ * Клиент 1 использует один алгоритм (Тормоз) в конструкторе * /общественный класс Седан расширяет Машина {    общественный Седан() {        супер(новый Тормоз());    }}/ * Клиент 2 использует другой алгоритм (BrakeWithABS) в конструкторе * /общественный класс Внедорожник расширяет Машина {    общественный Внедорожник() {        супер(новый Тормозить с ABS());    }}/ * На примере автомобиля * /общественный класс АвтомобильПример {    общественный статический пустота основной(окончательный Строка[] аргументы) {        Машина седанАвтомобиль = новый Седан();        седанАвтомобиль.applyBrake();  // Это вызовет класс "Тормоз"        Машина внедорожник = новый Внедорожник();        внедорожник.applyBrake();    // Это вызовет класс "BrakeWithABS"        // динамически устанавливаем поведение тормоза        внедорожник.setBrakeBehavior( новый Тормоз() );        внедорожник.applyBrake();    // Это вызовет класс "Тормоз"    }}

Смотрите также

использованная литература

  1. ^ «Шаблон разработки стратегии - проблема, решение и применимость». w3sDesign.com. Получено 2017-08-12.
  2. ^ Эрик Фриман, Элизабет Фриман, Кэти Сьерра и Берт Бейтс, Шаблоны проектирования Head First, Первое издание, глава 1, стр. 24, O'Reilly Media, Inc, 2004. ISBN  978-0-596-00712-6
  3. ^ Эрих Гамма, Ричард Хелм, Ральф Джонсон, Джон Влиссидес (1994). Паттерны проектирования: элементы объектно-ориентированного программного обеспечения многократного использования. Эддисон Уэсли. стр.315ff. ISBN  0-201-63361-2.CS1 maint: несколько имен: список авторов (ссылка на сайт)
  4. ^ «Шаблон разработки стратегии - структура и взаимодействие». w3sDesign.com. Получено 2017-08-12.
  5. ^ http://www.mcdonaldland.info/2007/11/28/40/

внешние ссылки