1. Введение

Шаблон проектирования стратегии является одним из наиболее широко используемых шаблонов в мире разработки программного обеспечения. В этой статье мы узнаем о шаблоне разработки стратегии, когда его использовать, а когда нет, как мы можем использовать шаблон, чтобы сделать наш дизайн гибким? И мы увидим пример того, как это реализовать с лямбда-выражениями и без них.

2. Шаблон разработки стратегии

2.1 Определение

Чтобы сэкономить ваше время, потраченное на поиск в Википедии, вот определение:

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

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

2.2 UML-диаграмма

Класс Context не реализует никакой стратегии (алгоритма). Вместо этого он поддерживает ссылку на интерфейс Strategy. Класс Context не заботится о реализации этих алгоритмов. Все, что он знает, это то, что он может выполнять эти алгоритмы!

StrategyImpl1 и StrategyImpl2 реализуют интерфейс Strategy, то есть реализуют и инкапсулируют алгоритм.

2.3 Когда использовать/избегать стратегии

Используйте шаблон стратегии, если вы хотите:

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

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

2.4 Классическая стратегия в действии

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

Обратите внимание, что класс Client зависит от класса Context, а также от некоторых реализаций стратегии (я не нарисовал нас, так как мы не хотим, чтобы здесь были спагетти). Это реализация (фиктивная) для нашей демонстрации:

Вывод:

List sorted using Bubble sort implementation
list is binary searched
---------------
List sorted using Quick sort implementation
list is linearly searched

Класс Contextзависит только от интерфейсов, объявляющих стратегии, SortStrategy и SearchStrategy. Его не волнует реализация этих интерфейсов. BubbleSortStrategyImpl и BinarySearchStrategyImpl — это классы, реализующие эти интерфейсы соответственно. Как мы уже говорили ранее, они реализуют и инкапсулируют стратегию (алгоритм).

Например, в строке 75 эти реализации внедряются клиентом в класс Context. Поэтому, когда мы вызываем context.sort(list) и context.search(“b”) во время выполнения, контекст будет знать, какую реализацию выполнять (полиморфизм).

Обратите внимание, что класс Context предоставляет сеттеры, которые позволяют клиентам заменять реализацию стратегии, связанную с контекстом, во время выполнения (помните: стратегия позволяет алгоритму изменяться независимо от клиентов, которые его используют).

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

3. Лямбда-выражение и шаблон стратегии

3.1 Обзор

Лямбда-выражения изменили мир в Java, и мы можем эффективно использовать лямбда-выражения, чтобы избежать написания большого количества церемониального кода.

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

3.2 Упрощенная стратегия

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

В этой демонстрации мы рефакторим код для использования лямбда-выражений, чтобы избежать создания пользовательских интерфейсов и классов. Это рефакторинг реализации (фиктивный) код:

Заметьте, я не создавал никакого интерфейса, потому что использую функциональные интерфейсы из пакета java.util.function.

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

А на стороне клиента я передаю реализации (объект функции) классу Context.

4. Вывод

В этой статье мы увидели определение шаблона проектирования стратегии, как его использовать, чтобы сделать дизайн гибким. И мы изучили классическую реализацию паттерна стратегии, а также его реализацию с использованием возможностей Java 8. В следующей статье будет больше Core Java. Быть в курсе!

Дайте нам знать, что вы думаете в комментариях ниже, и не забудьте поделиться!