Использование шаблона стратегии и шаблона команд


121

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

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

Какие решения определяют, использовать ли тот или иной паттерн?

Ответы:


94

Я включаю таблицу иерархии инкапсуляции нескольких шаблонов проектирования GoF, чтобы помочь объяснить различия между этими двумя шаблонами. Надеюсь, это лучше иллюстрирует то, что каждый из них инкапсулирует, поэтому мое объяснение имеет больше смысла.

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

таблица иерархии инкапсуляции шаблонов проектирования

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

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

Как это типично для шаблонов проектирования, они не требуют, чтобы все реализации были идентичными в деталях, чтобы иметь имя шаблона. Детали могут различаться в реализации и в том, какие данные закодированы в объекте, а не в аргументах метода.


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

4
@KTF, нет. Шаблон Command использует объект, который имеет большую часть (если не всю) информацию, необходимую (например, получатель, селектор, аргументы) для вызова метода объекта. Это упрощенный шаблон, который можно использовать в других шаблонах проектирования, таких как "Цепочка ответственности", "Сбор" и шаблон конвейера, который вы описываете. «Своеобразный контракт», предоставляемый вашими делегатами, - это еще один шаблон, интерфейс.
Huperniketes 02

50

Стратегии инкапсулируют алгоритмы. Команды отделяют отправителя от получателя запроса, они превращают запрос в объект.

Если это алгоритм, то как что-то будет сделано, используйте стратегию. Если вам нужно отделить вызов метода от его выполнения, используйте команду. Команды часто используются, когда вы ставите сообщения в очередь для последующего использования, например, задачи или транзакции.


в этом есть смысл en.wikipedia.org/wiki/Command_Pattern клиент и вызывающий объект связаны, но в то же время они не знают друг о друге!
Kalpesh Soni

26

Отвечая на очень старый вопрос. (Кто-нибудь видит самые последние ответы вместо наиболее проголосовавших?)

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

Ключевое отличие заключается в понимании того, что инкапсулировано. ОО-принцип, от которого зависят оба шаблона, - инкапсулировать то, что меняется .

В случае стратегии меняется алгоритм . Например, один объект стратегии знает, как выводить в файл XML, а другой -, скажем, в JSON. Различные алгоритмы хранятся ( инкапсулированы ) в разных классах. Это так просто.

В случае команды меняется сам запрос . Запрос может исходить от File Menu > Deleteили Right Click > Context Menu > Deleteили Just Delete Button pressed. Все три случая могут генерировать 3 командных объекта одного типа. Эти командные объекты представляют только 3 запроса на удаление; не алгоритм удаления. Поскольку запросы теперь представляют собой набор объектов, мы можем легко ими управлять. Внезапно стало тривиальным предоставлять такие функции, как отмена или повтор.

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

Сравните это со стратегией; этот шаблон касается только фактической выполняемой логики . Если мы это сделаем, это поможет достичь различных комбинаций поведения с минимальным набором классов, тем самым предотвращая взрыв классов.

Я думаю, Command помогает нам расширить наше понимание инкапсуляции, в то время как Strategy обеспечивает естественное использование инкапсуляции и полиморфизма.


15

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

Может быть, сначала попробуйте StrategyOne, если результаты недостаточно хороши, попробуйте StrategyTwo ...

Команды привязаны к отдельным вещам, которые должны произойти, например, TryToWalkAcrossTheRoomCommand. Эта команда будет запускаться всякий раз, когда какой-либо объект попытается пройти через комнату, но внутри он может попробовать StrategyOne и StrategyTwo для попытки пройти через комнату.

отметка


2
RE: «Несколько способов сделать одно и то же» - это, кажется, противоречит некоторым распространенным примерам стратегии. В частности, те, где есть классы реализации, которые выполняют сложение, вычитание, умножение и т. Д. Может быть, это плохие примеры?
Джошуа Дэвис

1
@JoshuaDavis все эти «субстраты» являются частными случаями одной стратегии: арифметической операции . они имеют общие аргументы (2 операнда) и в качестве результата выдают одно значение. в значительной степени делают одно и то же (будучи черными ящиками) по-своему, в зависимости от реализации. поэтому я не вижу здесь конфликта, но, как раз наоборот: хороший пример =)
jungle_mole 01

7

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

  • Все кнопки на панели инструментов приложения связаны с каким-либо действием.
  • В этом случае исполнителем является кнопка.
  • В этом случае действие - это команда.

Команда обычно ограничена определенной областью или сферой деятельности, но это не обязательно: у вас могут быть команды, которые выставляют счет, запускают ракету или удаляют файл, реализующий один и тот же интерфейс (например, единственный execute()метод) в одном приложении. Часто команды самодостаточны, поэтому им ничего не нужно от исполнителя для обработки задачи, которую они намереваются выполнить (вся необходимая информация предоставляется во время создания), иногда команды являются контекстно-зависимыми и должны иметь возможность обнаруживать этот контекст. ( Команда Backspace должна знать положение каретки в тексте, чтобы правильно удалить предыдущий символ; команда Rollback должна обнаружить текущую транзакцию для отката; ...).

Стратегия немного отличается: он более привязан к какой - то области. Стратегия может определять правило для форматирования даты (в формате UTC? Зависит от локали?) (Стратегия «форматирования даты») или для вычисления квадрата геометрической фигуры (стратегия «квадратного калькулятора»). В этом смысле стратегии - это легковесные объекты, которые принимают что-то в качестве входных данных («дату», «цифру», ...) и на их основе принимают какое-то решение. Возможно, не лучшим, но хорошим примером стратегии является стратегия, связанная с javax.xml.transform.Sourceинтерфейсом: в зависимости от того, является ли переданный объект DOMSourceили SAXSourceили StreamSourceстратегия (= преобразователь XSLT в данном случае) будет применять разные правила для его обработки. Реализация может быть простой switchили включать шаблон цепочки ответственности .

Но на самом деле между этими двумя шаблонами есть что-то общее: команды и стратегии инкапсулируют алгоритмы в одной семантической области.


1
Я рассматриваю команду как функцию обратного вызова или реакцию. Должно быть как минимум два игрока: один запрашивает действие, а другой выполняет ... - Я понимаю, что вы пытаетесь сказать, но я бы не стал использовать слово «обратный вызов», потому что часто слово «обратный вызов» подразумевает асинхронный вызов, и вам не нужно делать асинхронные вызовы, чтобы шаблон команды был полезным. Показательный пример: Microsoft Word. Нажатие кнопок на панели инструментов и нажатие сочетаний клавиш не вызывают асинхронные команды, но мы можем оценить, как шаблон команды будет полезен в этом случае,
Джим Дж.

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

Спасибо, я сделал несколько расширений. Дайте мне знать, если вы согласны / не согласны.
dma_k 05

5

Команда:

Основные компоненты:

  1. Команда объявляет интерфейс для абстрактных команд, таких какexecute()
  2. Получатель знает, как выполнить ту или иную команду
  3. Invoker содержит ConcreteCommand , который должен быть выполнен
  4. Клиент создает ConcreteCommand и назначает Receiver
  5. ConcreteCommand определяет привязку между Command и Receiver

Процедура:

Клиент вызывает Invoker => Invoker вызывает ConcreteCommand => ConcreteCommand вызывает метод Receiver , который реализует абстрактный метод Command .

Преимущество : на клиента не влияют изменения в Command и Receiver. Invoker обеспечивает слабую связь между клиентом и получателем. Вы можете запускать несколько команд с одним и тем же Invoker.

Команда шаблон позволяет выполнить команду на различных приемниках с помощьюже Invoker . Invoker не знает тип получателя

Для лучшего понимания концепций, взглянуть на эту JournalDev статью по Pankaj Kumar и DZone статьи по Джеймс Sugrue в дополнение к Википедии ссылки.

Вы можете использовать командный шаблон для

  1. Разделите вызывающего и получателя команды

  2. Реализовать механизм обратного вызова

  3. Реализовать функции отмены и повтора

  4. Вести историю команд

java.lang.Thread- одна хорошая реализация паттерна Command . Вы можете рассматривать Thread как вызывающий объект и класс, реализующий Runnable как ConcreteCommonad / Receiver, а run()метод как Command .

Версию шаблона команды Undo / Redo можно прочитать в статье Теодора Норвелла.

Стратегия:

Шаблон стратегии очень прост для понимания. Используйте этот шаблон, когда

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

Рассмотрим пример компонента Fare системы бронирования авиакомпаний.

Авиакомпании хотели бы предлагать разные тарифы в разные периоды времени - в пиковые и непиковые месяцы. Во время непиковых поездок компания хотела бы стимулировать спрос, предлагая привлекательные скидки.

Ключевые выводы паттерна Стратегия :

  1. Это поведенческий образец
  2. Он основан на делегировании
  3. Изменяет внутренности объекта, изменяя поведение метода.
  4. Используется для переключения между семейством алгоритмов
  5. Изменяет поведение объекта во время выполнения

Связанные сообщения с примерами кода:

Использование шаблона Command Design

Пример шаблона стратегии из реального мира


0

Для меня разница заключается в намерениях. Реализации обоих шаблонов очень похожи, но имеют разные цели:

  • Для стратегии компонент, использующий объект, знает, что делает объект (и будет использовать его для выполнения части своей работы), но ему все равно, как он это делает.

  • Для Команды компонент, использующий объект, не знает ни того, что делает Команда, ни того, как она это делает - он просто знает, как ее вызвать. Задача вызывающего абонента - просто запустить команду - обработка, выполняемая Командой, не является частью основной работы вызывающего.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.