Strategy (Стратегия)
Ну как обычно затравочка:
Всё тот же сайтик с кодом на плюсах
Всё тот же паттернкрафт с его стратегией
Погнали.
Суть паттерна стратегии в том что у нас к примеру есть некое поведение. Например "заархивировать". Или же "скачать". Или же "двигаться".
В глобальном смысле с точки зрения системы нам побоку КАК мы будем качать. Нам важен конечный результат.
Поэтому мы пытаемся отрезать сам алгоритм поведения от объекта и реализовать его отдельно. Далее вводим опять же как и со стейтом понятие контекста, который будет заниматься определением того КАК именно мы будем качать либо двигаться, т.е. выбором конкретной стратегии из набора.
Если вы начнете гуглить по этому паттерну то наткнетесь на кучу вопросов "чем паттерн стейт отличается от паттерна стратегии". По сути и тот и тот паттерн меняют поведение объекта, и более того - умл диаграмма идентична.
Но тут важно понимать. Стейт без объекта НЕ СУЩЕСТВУЕТ. Стейт это одно из состояний одного КОНКРЕТНОГО объекта.
А стратегии же пофигу даже на сам объект. Это только поведение. В идеале стратегия должна укладываться в один метод.
Саму стратегию неплохо описал ДжонЛингвист в своем паттернкрафте. У нас есть Три каких-то юнита. И есть каких-то 4 способа передвижения. Мы можем этими способами передвижения двигать любой из этих обэектов. Чистая стратегия, всё красиво. Контекста как такового у него в примере нету, в роли контекста выступает документКласс приложения и слушатель клавиатуры.
Рекомендую предварительно ознакомиться с его примером и видео.
Но дабы объяснить разницу между стейтом с тратегией я немного расширил его пример. Добавил к нему код из предыдущего поста о стейтах и постарался показать на примере как в одном коде красиво могут сочетаться стейт и тратегия и в чем между ними разница.
Вот что получилось.
На сцене лежат два квадратика. Танк и Ворон.
Кликаем мышкой по "юниту", чотбы выбрать его. Сверху в текстовом поле будет подписано что конкретно мы выбрали.
Далее можем кликнуть мышкой по пустому месту и выбранный юнит туда побежит.
Кнопками 1-3 переключаем стейт текущего выбранного юнита.
Кликом по другому юниту можем поменять выделение.
Вот исходник всего проекта. Приводить его здесь целиком сложно потому что кода много. Остановлюсь только на ключевых моментах.
StateWithStrategy.zip
У нас есть интерфейс абстрактной стратегии передвижения, который описывает метод передвижения. Метод может двигать любой Vechicle(первый параметр) как угодно(второй параметр)
package patterncraft.move { import patterncraft.Vehicle; public interface IMoveVehicleBehavior { function move(vehicle:Vehicle, tweenVars:Object):void; } }
package patterncraft { import patterncraft.move.*; import flash.display.Sprite; public class Vehicle extends Sprite { public var moveBehavior:IMoveVehicleBehavior; public function move(tweenVars:Object):void { if (moveBehavior) moveBehavior.move(this, tweenVars); } public function setState(state:String):void { throw new Error("need to override"); } } }
Немного поменялась реализация самих стейтов. Теперь стейт не сам реализовывает передвижение. Теперь стейт является контекстом стратегии. На момент транзишина стейт убирает конкретную стратегию из танка. Соответственно танк не может двигаться. Когда стейт установлен - он устанавливает танку конкретную стратегию передвижения, и танк опять может двигаться.
internal class SiegeState extends EventDispatcher implements ISiegeTankState { private var siegeTank:SiegeTank; public function SiegeState(siegeTank:SiegeTank) { this.siegeTank = siegeTank; } public function get damage():Number { return 20; } public function get canMove():Boolean { return false; } public function get color():uint { return 0x00cc00; } public function apply():void { // Когда транзишн закончился мы можем использовать танк по назначению. siegeTank.mouseEnabled = true; siegeTank.alpha = 1; // Пристегиваем конкретную стратегию движения siegeTank.moveBehavior = new Walk(); } public function transitionTo(state:String):void { // Убираем возможность двигаться и отображаем это визуально полупрозрачностью siegeTank.mouseEnabled = false; siegeTank.alpha = 0.3; siegeTank.moveBehavior = null; var timer:Timer = new Timer(1000, 1); timer.addEventListener(TimerEvent.TIMER, onTransitionComplete); timer.start(); } private function onTransitionComplete(event:TimerEvent):void { event.currentTarget.reset(); dispatchEvent(new Event(Event.COMPLETE)); } }
Это не мешает стейтам и это не замена стейту, но как дополнение - вполне подойдет.
Важно понимать, что стратегия не привязана к объекту.
Стейт без объекта не существует.
Стратегия реализовывает какое-то одно абстрактное поведение и несколько его конкретных реализаций для нескольких схожих объектов.
Стейт реализует разное поведение для одного объекта(в нашем кейсе стейт это делает посредством стратегии, хотя могла быть и любая другая реализация).
Вот как-то так. Не уверен что вышло суперпонятно, на вопросы отвечать готов, текст править тоже без проблем. Пока что будем считать задачу выполненной

Всего комментариев 11
Комментарии
![]() ![]() |
|
И пока со статьями пауза.
Позже думаю рассмотреть фабрику и фабричный метод. |
![]() ![]() |
|
В целом уже чуток текста накатал, буду благодарен за конструктивный фидбек по качеству изложения. Не уверен что всё получается понятно описать.
|
![]() ![]() |
|
Спасибо, очень доступно изложено.
|
![]() ![]() |
|
Dukobpa3 жду с нетерпением
![]() |
![]() ![]() |
|
Если стратегии нужна ссылка на контекст - скорее всего это стейт.
|
![]() ![]() |
|
"стейт" - он чей, "стратегия" - она для чего...
|
![]() ![]() |
|
Стейт ничей. Он реализует интерфейс. Это все, что его интересует.
|
Последние записи от Dukobpa3
- Strategy (Стратегия) (27.12.2013)
- State (Состояние) (27.12.2013)
- State-machine (конечный автомат, машина состояний) (25.12.2013)
- Медиатор, Прокси (14.11.2013)
- Инкапсуляция объекта vs инкапсуляция поведения (14.11.2013)