CodeLAB
на главную карта сайта обратная связь
каталог | задачи | паттерны | исходники | стат | форумы | ссылки
 гость
искать в
Главная >> Каталог задач >> Паттерны >> Поведения >> Стратегия (Strategy)

<< назад
распечатать обсудить >>


Стратегия (Strategy)
реализации: java, количество: 5

Aвтор: this
Дата: 07.10.2007
Просмотров: 123537
Рейтинг: 7/0,7(1)
+
реализации(исходники) +добавить

Имя

«Паттерн
Strategy» оригинал источник codelab.ru codelab.ru
Стратегия - паттерн поведения объектов, инкапсулирующий отдельные алгоритмы. оригинал источник codelab.ru codelab.ru

Также известен под именем Policy. оригинал codelab.ru источник codelab.ru

Условия, Задача, Назначение

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

Паттерн стратегия это осуществляет. По-сути, он: оригинал codelab.ru источник codelab.ru

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

Мотивация

Рассмотрим такую задачу, как разбиение текста на строки. Существует много алгоритмов для этого. Жестко «зашивать» все подобные алгоритмы в классы, которые в них нуждаются, нежелательно по нескольким причинам: codelab.ru codelab.ru источник оригинал
  • Клиент, которому требуется алгоритм разбиения на строки, усложняется при включении в него соответствующего кода. Таким образом, клиенты становятся более громоздкими, а сопровождать их труднее, особенно если нужно поддержать сразу несколько алгоритмов. оригинал codelab.ru источник codelab.ru
  • В зависимости от обстоятельств стоит применять тот или иной алгоритм.
    Не хотелось бы поддерживать несколько алгоритмов разбиения на строки сразу во всех классах, которые это разбиение используют. Особенно, если мы не уверены, будет ли оно использоваться во всех этих классах. codelab.ru источник codelab.ru оригинал
  • Если разбиение на строки - неотъемлемая часть клиента, то задача добавления новых и модификации существующих алгоритмов усложняется. источник codelab.ru оригинал codelab.ru
Всех этих проблем можно избежать, если определить классы, инкапсулирующие различные алгоритмы разбиения на строки. Инкапсулированный таким образом алгоритм называется стратегией. источник codelab.ru codelab.ru оригинал

codelab.ru источник codelab.ru оригинал

Предположим, что класс Composition отвечает за разбиение на строки текста, отображаемого в окне программы просмотра, и его своевременное обновление. Стратегии разбиения на строки определяются не в классе Composition, а в подклассах абстрактного класса Compositor. Это могут быть, например, такие стратегии: codelab.ru источник codelab.ru оригинал
  • SimpleCompositor реализует простую стратегию, выделяющую по одной строке за раз. оригинал codelab.ru источник codelab.ru
  • TeXCompositor реализует алгоритм поиска точек разбиения на строки. Эта стратегия пытается выполнить глобальную оптимизацию разбиения на строки, рассматривая сразу целый параграф. оригинал источник codelab.ru codelab.ru
  • ArrayCompositor реализует стратегию расстановки переходов на новую строку таким образом, что в каждой строке оказывается одно и то же число элементов.
    Это полезно, например, при построчном отображении набора пиктограмм. codelab.ru codelab.ru оригинал источник

Объект Composition хранит ссылку на объект Compositor. Всякий раз, когда объекту Composition требуется переформатировать текст, он делегирует данную обязанность своему объекту Compositor. Клиент задает, какой объект Compositor следует использовать, параметризуя им объект Composition. codelab.ru оригинал codelab.ru источник

Признаки применения, использования паттерна Стратегия (Strategy)

Используйте паттерн стратегия, когда: оригинал codelab.ru источник codelab.ru
  1. Имеется много родственных классов, отличающихся только поведением.
    Т.е. имеющих похожие интерфейсы, но реализующих свою логику по разному.
    Стратегия позволяет сконфигурировать класс, задав одно из возможных поведений. codelab.ru оригинал источник codelab.ru
  2. Вам нужно иметь несколько разных вариантов алгоритма.
    Например, можно определить два варианта алгоритма, один из которых требует больше времени, а другой - больше памяти. codelab.ru codelab.ru оригинал источник
  3. В алгоритме содержатся данные, о которых клиент не должен «знать».
    Используйте паттерн стратегия, чтобы не раскрывать сложные, специфичные для алгоритма структуры данных (подход «черного ящика»). оригинал codelab.ru codelab.ru источник
  4. В классе определено много поведений.
    И все это добро представлено разветвленными условными операторами. В этом случае проще перенести код из ветвей в отдельные классы стратегий. codelab.ru источник оригинал codelab.ru

Решение

оригинал codelab.ru источник codelab.ru

источник codelab.ru codelab.ru оригинал

Участники паттерна Стратегия (Strategy)

codelab.ru codelab.ru источник оригинал
  1. Strategy (Compositor) – стратегия.
    Объявляет общий для всех поддерживаемых алгоритмов (стратегий) интерфейс. Класс Context пользуется этим интерфейсом для вызова конкретного алгоритма, определенного в классе ConcreteStrategy. оригинал источник codelab.ru codelab.ru
  2. ConcreteStrategy (SimpleCompositor, TeXCompositor, ArrayCompositor) - конкретная стратегия.
    Реализует алгоритм, использующий интерфейс, объявленный в классе Strategy. оригинал источник codelab.ru codelab.ru
  3. Context (Composition) – контекст.
    Конфигурируется объектом класса ConcreteStrategy.
    Хранит ссылку на объект класса Strategy.
    Может определять интерфейс, который позволяет объекту Strategy получить доступ к данным контекста. codelab.ru codelab.ru источник оригинал

Схема использования паттерна Стратегия (Strategy)

Классы Strategy и Context взаимодействуют для реализации выбранного алгоритма. Контекст может передать стратегии все необходимые алгоритму данные в момент его вызова. Вместо этого контекст может позволить обращаться к своим операциям в нужные моменты, передав ссылку на самого себя операциям класса Strategy. codelab.ru codelab.ru оригинал источник
Контекст переадресует запросы своих клиентов объекту-стратегии. Обычно codelab.ru источник оригинал codelab.ru

клиент создает объект ConcreteStrategy и передает его контексту, после чего клиент «общается» исключительно с контекстом. Часто в распоряжении клиента находится несколько классов ConcreteStrategy, которые он может выбирать. источник codelab.ru оригинал codelab.ru

Вопросы, касающиеся реализации паттерна Стратегия (Strategy)

Рассмотрим следующие вопросы реализации: оригинал codelab.ru codelab.ru источник
  1. Определение интерфейсов классов Strategy и Context.
    Интерфейсы классов Strategy и Context могут обеспечить объекту класса ConcreteStrategy эффективный доступ к любым данным контекста, и наоборот.
    Например, Context передает данные в виде параметров операциям класса Strategy. Это разрывает тесную связь между контекстом и стратегией. При этом не исключено, что иногда контекст будет передавать данные, которые стратегии не нужны.
    Другой метод - передать контекст в качестве аргумента, в таком случае стратегия сама будет запрашивать у него данные, или, например, сохранить ссылку на свой контекст, так что передавать вообще ничего не придется.
    И в том, и в другом случаях стратегия может запрашивать только ту информацию, которая реально необходима. Но тогда в контексте должен быть определен более развитый интерфейс доступа к своим данным, что несколько усиливает связанность классов Strategy и Context.
    Какой подход лучше, зависит от конкретного алгоритма и требований, которые он предъявляет к данным. источник codelab.ru оригинал codelab.ru
  2. Стратегии как параметры класса.
    В C++ для конфигурирования класса стратегией можно использовать шаблоны. Этот способ хорош, только если стратегия определяется на этапе компиляции и ее не нужно менять во время выполнения. Тогда конфигурируемый класс (например, Context) определяется в виде шаблона, для которого класс Strategy является параметром:
     паметризация шаблона стратегией [C++]  ссылка
    1. template <class AStrategy>
    2. class Context {
    3. void Operation)) { theStrategy .DoAlgorithm( ) ; }
    4. // ...
    5. private :
    6. AStrategy theStrategy;
    7. };
    8.  
    9. //...
    10. //Затем этот класс конфигурируется классом Strategy в момент инстанцирования:
    11. class MyStrategy {
    12. public:
    13. void DoAlgorithm( ) ;
    14. };
    15.  
    16. Context<MyStrategy> aContext;


    При использовании шаблонов отпадает необходимость в абстрактном классе для определения интерфейса Strategy. Кроме того, передача стратегии в виде параметра шаблона позволяет статически связать стратегию с контекстом, вследствие чего повышается эффективность программы.

    В языке Java имеется похожий механизм Generics. Вот как бы это все выглядело там:
     контект, параметризированный стратегией с помощью java Generics [java]  ссылка
    1. package strategy;
    2.  
    3. public class Context<T extends Strategy> {
    4. private T strategy;
    5.  
    6. public void Operation() {
    7. strategy.DoAlgorithm();
    8. }
    9. }
     использование параметризованного контекста [java]  ссылка
    1. package strategy;
    2.  
    3. public class Client2 {
    4. public static void main(String[] args) {
    5. /**
    6. * Новая стратегия в виде локального
    7. * внутреннего класса
    8. */
    9. class MyStrategy extends Strategy {
    10.  
    11. public void DoAlgorithm() {
    12. /* Переопределение стандартного поведения
    13. */
    14. }
    15. }
    16.  
    17. /* Теперь - используем */
    18. Context<MyStrategy> aContext;
    19.  
    20. /* дальнейшая логика
    21. * ...
    22. */
    23. }
    24. }

    codelab.ru источник codelab.ru оригинал
  3. Стратегия по-умалчанию.
    Класс Context разрешается упростить, если для него отсутствие какой бы то ни было стратегии является нормой. Прежде чем обращаться к объекту Strategy, объект Context проверяет наличие стратегии. Если да, то работа продолжается как обычно, в противном случае контекст реализует некое поведение по умолчанию. Достоинство такого подхода в том, что клиентам вообще не нужно иметь дело со стратегиями, если их устраивает поведение по умолчанию. источник codelab.ru оригинал codelab.ru

Результаты

У паттерна стратегия есть следующие достоинства и недостатки: источник codelab.ru codelab.ru оригинал
  1. Семейства родственных алгоритмов.
    Иерархия классов Strategy определяет семейство алгоритмов или поведений, которые можно повторно использовать в разных контекстах и приложениях. А наследование позволяет вычленить общую для всех алгоритмов функциональность. оригинал источник codelab.ru codelab.ru
  2. Альтернатива порождению подклассов.
    Наследование поддерживает многообразие алгоритмов или поведений. Можно напрямую породить от Context подклассы с различными поведениями. Но при этом поведение жестко «зашивается» в класс Context. Вот почему реализации алгоритма и контекста смешиваются, что затрудняет понимание, сопровождение и расширение контекста. Кроме того, заменить алгоритм динамически уже не удастся. В результате вы получите множество родственных классов, отличающихся только алгоритмом или поведением. Инкапсуляции алгоритма в отдельный класс Strategy позволяют изменять его независимо от контекста. источник codelab.ru codelab.ru оригинал
  3. Избавление от серии условных операторов.
    Благодаря паттерну стратегия удается отказаться от условных операторов при выборе нужного поведения. Когда различные поведения помещаются в один класс, трудно выбрать нужное без применения условных операторов. Инкапсуляция же каждого поведения в отдельный класс Strategy решает эту проблему.
    Так, без использования стратегий код для разбиения текста на строки мог бы выглядеть следующим образом:
     разбиение текста на строки, типовое решение [java]  ссылка
    1. package strategy;
    2.  
    3. public class Composition {
    4.  
    5. public void Repair() {
    6. switch (_breakingStrategy) {
    7. case SIMPLE_STRATEGY:
    8. ComposeWithSimpleCompositor () ;
    9. break;
    10. case TEX_STRATEGY:
    11. ComposeWithTeXCompositor() ;
    12. break;
    13. // ...
    14. }
    15. /* если необходимо, объединить результаты с имеющейся
    16. * композицией
    17. */
    18. }
    19.  
    20. }
    21.  

    Паттерн же стратегия позволяет обойтись без оператора переключения за счет делегирования задачи разбиения на строки объекту Strategy:
     разбиение текста на строки, реализация стратегией [java]  ссылка
    1. package strategy;
    2.  
    3. public class Composition {
    4. //...
    5.  
    6. public void Repair() {
    7. _compositor->Compose();
    8. }
    9. }

    Вообще, если код содержит много условных операторов, то часто это уже признак того, что нужно применить паттерн стратегия. codelab.ru источник оригинал codelab.ru
  4. Выбор реализации.
    Стратегии могут предлагать различные реализации одного и того же поведения. Клиент вправе выбирать подходящую стратегию в зависимости от своих требований к быстродействию и памяти. источник codelab.ru оригинал codelab.ru
  5. Клиенты должны «знатъ» о различных стратегиях.
    Потенциальный недостаток этого паттерна в том, что для выбора подходящей стратегии клиент должен понимать, чем отличаются разные стратегии. Поэтому наверняка придется раскрыть клиенту некоторые особенности реализации. Отсюда следует, что паттерн стратегия стоит применять лишь тогда, когда различия в поведении имеют значение для клиента. оригинал источник codelab.ru codelab.ru
  6. Обмен информацией между стратегией и контекстом.
    Интерфейс класса Strategy разделяется всеми подклассами ConcreteStrategy — неважно, сложна или тривиальна их реализация. Поэтому вполне вероятно, что некоторые стратегии не будут пользоваться всей передаваемой им информацией, особенно простые. Это означает, что в отдельных случаях контекст создаст и проинициализирует параметры, которые никому не нужны. Если возникнет проблема, то между классами Strategy и Context придется установить более тесную связь. источник codelab.ru оригинал codelab.ru
  7. Увеличение числа объектов.
    Применение стратегий увеличивает число объектов в приложении. Иногда эти издержки можно сократить, если реализовать стратегии в виде объектов без состояния, которые могут разделяться несколькими контекстами. Остаточное состояние хранится в самом контексте и передается при каждом обращении к объекту-стратегии. Разделяемые стратегии не должны сохранять состояние между вызовами. В описании паттерна приспособленец этот подход обсуждается более подробно. источник codelab.ru оригинал codelab.ru

Пример

В системе имеется список ее пользователей и в зависимости от ситуации он должен быть отсортирован по разным параметрам, как-то: ФИО пользователя, или дата его регистрации и т.д. codelab.ru источник codelab.ru оригинал

Класс пользователей выглядит так: User. codelab.ru оригинал источник codelab.ru

Подумаем, каким способом лучше представить список из этих объектов, который решал бы поставленные задачи. источник codelab.ru codelab.ru оригинал
Можно использовать стандартный список и в зависимости от ситуации каждый раз добавлять туда пользователей в нужной последовательности, соответсвующей требуемой сортировке. Не решается задача сортировки – ее придется решать отдельно. codelab.ru оригинал источник codelab.ru
Другой способ – заложить алгоритмы сортировки в сам объект список, при его создании указывать тип сортировки, а при добавлении – располагать елементы в нужной последовательности. Плохо по тем же причинам, указанным в «мотивации». codelab.ru codelab.ru источник оригинал
  оригинал codelab.ru источник codelab.ru
Данная ситуация – идеальный случай применения паттерна стратегия. источник codelab.ru codelab.ru оригинал
Список пользователей выступает в роли контекста (Context). Отдельные алгоритмы сортировки инкапсулируются в отдельные классы (стратегии) и при создании списка пользователей – передаются ему. codelab.ru codelab.ru оригинал источник
При этом в Java для этого уже все предусмотрено: имеется специальный интерфейс Comparator, объекты которого передаются в специальный тип коллекции (TreeSet). И далее эта коллекция сама сортирует все свои элементы в соответствии с указанным объектом Comparator-а. оригинал codelab.ru источник codelab.ru
Поэтому стратегии сортировки, Comparator-ы в нашем случае будут: SortByIDStrategy, SortByFioStrategy, SortByDateStrategy. оригинал источник codelab.ru codelab.ru
  оригинал источник codelab.ru codelab.ru
Контекст нам, как уже понятно, реализовывать не надо – в качестве реализации списка, сортирующего свои элементы, используется стандартный TreeSet, входящий в ядро языка. codelab.ru источник оригинал codelab.ru
Поэтому – определяем примерного клиента: Clientcodelab.ru codelab.ru источник оригинал
codelab.ru источник codelab.ru оригинал
Во-первых, обратите внимание, как указывается конкретный способ сортировки: клиент передает в контекст (в TreeSet т.е.) нужный Comparator (ConcreteStrategy). codelab.ru оригинал источник codelab.ru
Во-вторых, - выбор списка вынесен в отдельный метод, специально, чтобы продемонстрировать, что клиент не должен знать о каких либо подробностях контекста – он просто работает в соответсвии с его интерфейсом (интерфейсом стандартного множества Set в данном случае), который единый вне зависимости от использующихся стратегий. источник codelab.ru codelab.ru оригинал

И, в-третьих, имена переменных пользователей – на русском языке, что, конечно, к делу не относится, а просто демонстрирует очередные широкие возможности языка. В данном случае фамилии великих русских писателей очень уместно написать на их родном языке! оригинал codelab.ru источник codelab.ru

  источник codelab.ru codelab.ru оригинал

Известные применения паттерна Стратегия (Strategy)

В языке Java и в его многочисленных библиотеках паттерн-стратегия применяется довольно часто. Прежде всего, это классы сортированных коллекций TreeMap и TreeSet. Оба они имеют вариант конструктора, имеющего один параметр типа Comparator. Это интерфейс, который содержит одну операцию compare() с 2-мя аргументами объектами, которые сравниваются. Таким образом, передаются объекты, реализующие по-разному эту стратегию сравнения объектов, и соответственно, создавая упорядоченную коллекцию с разными стратегиями сравнения – мы прозразно получаем разную сортировку в ней. источник codelab.ru codelab.ru оригинал
Далее включенная в ядро языка подсистема логирования java.util.logging, берущая корни из известного log4j. И там и там для экспорта сообщений изпользуются специальные классы (Handler-ы или Appender-ы), реализующие разные способы записи логов в разные подсистемы (в файл, в базу данных, отправлять на почту и т.д.), имеющий единый интерфейс для своего контекста (Logger-а). Т.е. стратегии.  источник codelab.ru оригинал codelab.ru
codelab.ru оригинал codelab.ru источник
Библиотеки ЕТ++ и Interviews используют стратегии для инкапсуляции алгоритмов разбиения на строки - так, как мы только что видели. codelab.ru оригинал источник codelab.ru
оригинал источник codelab.ru codelab.ru
В системе RTL для оптимизации кода компиляторов с помощью стратегий определяются различные схемы распределения регистров (RegisterAllocator) и политики управления потоком команд (RISCscheduler, CISCscheduler). Это позволяет гибко настраивать оптимизатор для разных целевых машинных архитектур. codelab.ru codelab.ru источник оригинал
оригинал codelab.ru codelab.ru источник
Каркас ЕТ++ SwapsManager предназначен для построения программ, рассчитывающих цены для различных финансовых инструментов. Ключевыми абстракциями для него являются Instrument (инструмент) и YieldCurve (кривая дохода). Различные инструменты реализованы как подклассы класса Instrument. источник codelab.ru оригинал codelab.ru
YieldCurve рассчитывает коэффициенты дисконтирования, на основе которых вычисляется текущее значение будущего движения ликвидности. Оба класса делегируют часть своего поведения объектам-стратегиям класса Strategy. В каркасе присутствует семейство конкретных стратегий для генерирования движения ликвидности, оценки оборотов и вычисления коэффициентов дисконтирования. оригинал источник codelab.ru codelab.ru
Можно создавать новые механизмы расчетов, конфигурируя классы Instrument и YieldCurve другими объектами конкретных стратегий. Этот подход поддерживает как использование существующих реализаций стратегий в различных сочетаниях, так и определение новых. codelab.ru оригинал источник codelab.ru
  оригинал codelab.ru источник codelab.ru
В библиотеке компонентов Грейди Буча стратегии используются как аргументы шаблонов. В классах коллекций поддерживаются три разновидности стратегий распределения памяти: управляемая (распределение из пула), контролируемая (распределение и освобождение защищены замками) и неуправляемая (стандартное распределение памяти). Стратегия передается классу коллекции в виде аргумента шаблона в момент его инстанцирования.
оригинал источник codelab.ru codelab.ru
RApp - это система для проектирования топологии интегральных схем. Задача RApp - проложить провода между различными подсистемами на схеме. Алгоритмы трассировки в RApp определены как подклассы абстрактного класса Router, который является стратегией. источник codelab.ru codelab.ru оригинал
источник codelab.ru codelab.ru оригинал
В библиотеке ObjectWindows фирмы Borland стратегии используются в диалоговых окнах для проверки правильности введенных пользователем данных. codelab.ru codelab.ru источник оригинал
Например, можно контролировать, что число принадлежит заданному диапазону, а в данном поле должны быть только цифры. Не исключено, что при проверке корректности введенной строки потребуется поиск данных в справочной таблице. codelab.ru источник codelab.ru оригинал
Для инкапсуляции стратегий проверки в ObjectWindows используются объекты класса Validator — частный случай паттерна стратегия. Поля для ввода данных делегируют стратегию контроля необязательному объекту Validator. Клиент при необходимости присоединяет таких проверяющих к полю (пример необязательной стратегии). В момент закрытия диалогового окна поля «просят» своих контролеров проверить правильность данных. В библиотеке имеются классы контролеров для наиболее распространенных случаев, например RangeValidator для проверки принадлежности числа диапазону. Но клиент может легко определить и собственные стратегии проверки, порождая подклассы от класса Validator. источник оригинал codelab.ru codelab.ru
источник codelab.ru оригинал codelab.ru

Родственные паттерны

Приспособленец: объекты-стратегии для большей экономии и оптимизации могут быть реализованы как приспособленцы. codelab.ru источник codelab.ru оригинал


Реализации: java(5)   +добавить реализацию

1) User.java, code #522[автор:this]
2) SortByIDStrategy.java, code #523[автор:this]
3) SortByFioStrategy.java, code #524[автор:this]
4) SortByDateStrategy.java, code #525[автор:this]
5) Client.java, code #526[автор:this]


<< назад наверх
распечатать обсудить >>

 
каталог | задачи | паттерны | исходники | стат | форумы | карта сайта | контакты | ссылки 
© 2000-2017 CodeLAB Group
  Все права защищены
Страница сгенерирована за 0.042125 секунд
Количество запросов к БД: 14, gzip: 23.3kb/113.9kb(80%)