Шаблон стратегии: шаблоны проектирования программного обеспечения для переменных стратегий поведения

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

Что такое паттерн стратегии?

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

Как паттерн стратегии представлен в UML

Паттерны стратегии обычно проектируются с помощью унифицированного языка моделирования (UML). Он визуализирует паттерны проектирования с помощью стандартизированной нотации и использует специальные знаки и символы. UML предоставляет различные типы диаграмм для объектно-ориентированного программирования. Для представления паттерна проектирования стратегии обычно используется диаграмма классов, состоящая как минимум из трех основных компонентов:

  • Контекст
  • Стратегия
  • ConcreteStrategy

В паттерне проектирования стратегии базовые компоненты берут на себя особые роли: Поведенческие модели класса Context передаются на аутсорсинг различным классам Strategy. Эти отдельные классы содержат алгоритмы, которые называются ConcreteStrategies. Ссылка позволяет Context при необходимости обращаться к аутсорсинговым вариантам вычислений (ConcreteStrategyA, ConcreteStrategyB и т.д.). При этом он взаимодействует не непосредственно с алгоритмами, а с интерфейсом.

Интерфейс стратегии инкапсулирует варианты вычислений и может быть реализован всеми алгоритмами одновременно. Для взаимодействия с Контекстом общий интерфейс представляет собой лишь один из способов запуска алгоритмов ConcreteStrategy. Помимо запроса стратегии, взаимодействие с Контекстом также включает обмен данными. Интерфейс стратегии также участвует в изменении стратегии, которое может происходить во время выполнения программы.

Факт

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

Теперь мы объясним, как паттерн проектирования реализуется в практическом проекте на примере паттерна стратегии.

Объяснение паттерна стратегии на примере

В нашем примере (мы ориентируемся на немецкий проект Филиппа Хауэра по изучению паттерна стратегии, в котором с помощью паттерна стратегии должно быть реализовано навигационное приложение. Приложение должно рассчитывать маршрут на основе обычных видов транспорта. Пользователь может выбрать один из трех вариантов:

  • Пешеход (ConcreteStrategyA)
  • Автомобиль (ConcreteStrategyB)
  • Общественный транспорт (ConcreteStrategyC).

Структура и функции шаблона необходимой стратегии становятся понятными, когда эти спецификации показаны на диаграмме UML:

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

Например, если пользователь вызывает команду с помощью кнопки клиента «Пешеход», запрашивается сервис «Рассчитать пешеходный маршрут» (ConcreteStrategyA). Метод executeAlgorithm() (в нашем примере метод: calculateRoute (A, B)) принимает начальную точку и пункт назначения и возвращает коллекцию контрольных точек маршрута. Контекст принимает команду клиента и принимает решение о выборе правильной стратегии (setStrategy: Pedestrian) на основе ранее описанных политик. Он делегирует запрос объекту стратегии и его интерфейсу посредством вызова.

Выбранная в данный момент стратегия хранится в Context (класс навигатора) с помощью функции getStrategy(). Результаты вычислений ConcreteStrategy используются в дальнейшей обработке и графическом представлении маршрута в навигационном приложении. Если после этого пользователь выбирает другой маршрут, например, нажав на кнопку «Автомобиль», Контекст переключается на запрошенную стратегию (ConcreteStrategyB) и инициирует новый расчет с помощью другого вызова. В конце процесса предоставляется модифицированное описание маршрута для поездки на автомобиле.

В нашем примере механизм паттерна может быть реализован с помощью относительно простого кода:

Context:

public class Context {
    //prescribed standard value (default behavior): ConcreteStrategyA
    private Strategy strategy = new ConcreteStrategyA(); 
    public void execute() { 
        //delegates the behavior to a Strategy object
        strategy.executeAlgorithm(); 
    }
    public void setStrategy(Strategy strategy) {
        strategy = strategy;
    }
    public Strategy getStrategy() { 
        return strategy; 
    } 
} 

Стратегия, ConcreteStrategyA, ConcreteStrategyB:

interface Strategy { 
    public void executeAlgorithm(); 
} 
class ConcreteStrategyA implements Strategy { 
    public void executeAlgorithm() { 
        System.out.println("Concrete Strategy A"); 
    } 
} 
class ConcreteStrategyB implements Strategy { 
    public void executeAlgorithm() { 
        System.out.println("Concrete Strategy B"); 
    } 
}  

Клиент:

public class Client { 

    public static void main(String[] args) { 
        //Default-Verhalten 
        Context context = new Context(); 
        context.execute(); 

        //Verhalten ändern 
        context.setStrategy(new ConcreteStrategyB()); 
        context.execute(); 
    } 

}

Каковы преимущества и недостатки паттерна стратегии?

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

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

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

Поскольку Контекст навигационного приложения взаимодействует только с интерфейсом благодаря инкапсуляции алгоритмов, он не зависит от конкретной реализации отдельных алгоритмов. Если впоследствии алгоритмы будут изменены или будут введены новые стратегии, код Context не нужно будет менять. Таким образом, расчет маршрута может быть быстро и легко расширен за счет дополнительных ConcreteStrategies для маршрутов самолетов и кораблей, а также междугородних перевозок. Новые стратегии должны лишь правильно реализовать интерфейс Strategy.

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

Несмотря на многочисленные преимущества, паттерн стратегии также имеет некоторые недостатки. Из-за его более сложной структуры при проектировании программного обеспечения могут возникать избыточность и неэффективность внутренней связи. Например, общий интерфейс Strategy, который все алгоритмы должны реализовывать одинаково, в отдельных случаях может оказаться избыточным.

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

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

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

Где используется паттерн стратегии?

Будучи фундаментальным паттерном проектирования при разработке программного обеспечения, паттерн стратегии не ограничивается определенной областью применения. Напротив, тип проблемы имеет большое значение для использования паттерна проектирования. Любое программное обеспечение, которое должно решать нерешенные задачи и проблемы с изменчивостью, вариантами поведения и изменениями, является главным кандидатом на использование паттерна проектирования.

Например, программы, предлагающие различные форматы хранения файлов или различные функции сортировки и поиска, могут использовать шаблоны проектирования стратегий. Аналогично в сжатии данных используются программы, реализующие различные алгоритмы сжатия на основе паттерна проектирования. Таким образом, они могут вариативно преобразовывать видео в нужный формат файла, экономящий место, или восстанавливать сжатые архивные файлы (например, файлы ZIP или RAR) в исходное состояние с помощью специальных стратегий распаковки. Другой пример — сохранение документа или изображения в различных форматах файлов.

Более того, паттерн проектирования задействован в разработке и реализации игрового программного обеспечения, которое должно гибко реагировать на изменение игровой ситуации во время выполнения игры. Различные персонажи, специальное оборудование, поведение фигурок или различные ходы (специальные движения игрового персонажа) могут храниться в виде ConcreteStrategies.

Еще одна область применения паттернов стратегий — налоговое программное обеспечение. Обмениваясь ConcreteStrategies, можно легко регулировать ставки для профессиональных групп, стран и регионов. Кроме того, программы, преобразующие данные в различные графические форматы (например, в линейные, круговые или столбчатые диаграммы), используют шаблоны стратегий.

Более конкретные применения шаблонов стратегий можно найти в стандартной библиотеке Java (Java API) и в инструментах Java GUI (например, AWT, Swing и SWT), которые используют менеджер компоновки при разработке и создании графических пользовательских интерфейсов. Он способен реализовать различные стратегии конфигурирования компонентов при разработке интерфейса. Другие области применения паттернов проектирования стратегий включают системы баз данных, драйверы устройств и серверные программы.

Ключевые свойства паттерна стратегии с первого взгляда

В обширном ряду паттернов проектирования паттерн стратегии отличается следующими характеристиками:

  • Поведенческий (поведенческие модели и изменения легче программируются и реализуются; изменения возможны даже во время выполнения программы).
  • Ориентированность на эффективность (аутсорсинг упрощает и оптимизирует код и его сопровождение)
  • Ориентированность на будущее (изменения и оптимизации также легко реализовать в среднесрочной и долгосрочной перспективе)
  • Нацеленность на расширяемость (этому способствует модульная система и независимость объектов и классов)
  • Нацеленность на повторное использование (например, многократное использование стратегий)
  • Нацелен на оптимизацию управления, удобства использования и конфигурируемости программного обеспечения
  • Требует предварительных концептуальных проработок (что может быть передано на аутсорсинг каким классам стратегий в какие моменты и каким образом?)
Резюме

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

Оцените статью
cdelat.ru
Добавить комментарий