簡介
策略模式定義了演算法族,分別封裝起來,讓他們之間可以互相替換。此模式讓演算法獨立於使用它的客戶而獨立變化。
組成
抽象策略角色: 策略類,通常由一個介面或抽象類別實作。
具體策略角色:包裝了相關的演算法和行為。
環境角色:持有一個策略類別的引用,最終給客戶端呼叫。
應用場景
多個類別只區別在表現行為不同,可以使用策略模式,在運行時動態選擇具體要執行的行為。
需要在不同情況下使用不同的策略(演算法),或者策略還可能在未來用其它方式來實現。
對客戶隱藏具體策略(演算法)的實作細節,彼此完全獨立。
實作
步驟
定義抽象角色類別(定義好各個實作的共同抽象方法)
定義具體策略類別(具體實作父類別的共同方法)
定義環境角色類別(接收保存實例,統一執行策略性介面方法)
代碼
<?php header('Content-Type:text/html;charset=utf-8'); /** * 策略模式演示代码 * * 为了更好地突出“策略”,我们这里以出行为例演示,日常出行大概分为以下几种工具:自驾车,公交车,地铁,火车,飞机,轮船 * * 下面一起看代码,领会何为策略模式 */ /** * Interface Travel 抽象策略角色 * 约定具体方法 */ interface Travel { public function go(); } /** * Class selfDriving 具体策略角色 * 自驾车 */ class bySelfDriving implements Travel { public function go() { echo '我自己开着车出去玩<br>'; } } /** * Class byBus具体策略角色 * 乘公交 */ class byBus implements Travel { public function go() { echo '我乘公交出去玩<br>'; } } /** * Class byMetro 具体策略角色 * 乘地铁 */ class byMetro implements Travel { public function go() { echo '我乘地铁出去玩<br>'; } } /** * Class byTrain 具体策略角色 * 乘火车 */ class byTrain implements Travel { public function go() { echo '我乘火车出去玩<br>'; } } /** * Class byAirplane 具体策略角色 * 乘飞机 */ class byAirplane implements Travel { public function go() { echo '我乘飞机出去玩<br>'; } } /** * Class bySteamship 具体策略角色 * 乘轮船 */ class bySteamship implements Travel { public function go() { echo '我乘轮船出去玩<br>'; } } /** * Class Mine 环境角色 */ class Mine{ private $_strategy; private $_isChange = false; /** * 构造方法 * 此处使用到了依赖注入和类型约束的概念,详情请参考 * 1.聊一聊PHP的依赖注入(DI) 和 控制反转(IoC) * @link https://segmentfault.com/a/1190000007209266 * 2.浅谈PHP的类型约束 * @link https://segmentfault.com/a/1190000007226476 * * @param Travel $travel */ public function __construct(Travel $travel) { $this->_strategy = $travel; } /** * 改变出行方式 * * @param Travel $travel */ public function change(Travel $travel) { $this->_strategy = $travel; $this->_isChange = true; } public function goTravel() { if ($this->_isChange) { echo '现在改变主意,'; $this->_strategy->go(); } else { $this->_strategy->go(); } } } /** * 客户端使用 */ $strategy = new Mine(new byBus()); // 乘公交 $strategy->goTravel(); // 乘地铁 $strategy->change(new byMetro()); $strategy->goTravel(); // 自驾车 $strategy->change(new bySelfDriving()); $strategy->goTravel(); // 其他根据具体应用选择实现
運行結果
我搭公車出去玩
現在改變主意,我搭地鐵出去玩
現在改變主意,我自己開車出去玩
優缺點
現在改變主意,我自己開著車出去玩
策略模式提供了管理相關的演算法族的辦法。策略類別的等級結構定義了一個演算法或行為族。恰當使用繼承可以把公共的程式碼轉移到父類別裡面,從而避免重複的程式碼。
策略模式提供了可以替換繼承關係的方法。繼承可以處理多種演算法或行為。如果不是用策略模式,那麼使用演算法或行為的環境類別就可能會有一些子類,而每個子類別提供一個不同的演算法或行為。但是,這樣一來演算法或行為的使用者就和演算法或行為本身混在一起。決定使用哪一種演算法或採取哪一種行為的邏輯就和演算法或行為的邏輯混合在一起,因此不可能再獨立演化。繼承使得動態改變演算法或行為變得不可能。
使用策略模式可以避免使用多重條件轉移語句。多重轉移語句不易維護,它把採取哪一種演算法或採取哪一種行為的邏輯與演算法或行為的邏輯混合在一起,統統列在一個多重轉移語句裡面,比使用繼承的辦法還要原始和落後。
缺點
客戶端必須知道所有的策略類,並自行決定使用哪一個策略類。這就意味著客戶端必須理解這些演算法的區別,以便適時選擇適當的演算法類別。換言之,策略模式只適用於客戶端知道所有的演算法或行為的情況。
策略模式造成很多的策略類,每個具體策略類都會產生一個新類。有時候可以透過把依賴環境的狀態保存到客戶端裡面,而將策略類別設計成可共享的,這樣策略類別實例可以被不同客戶端使用。換言之,可以使用享元模式來減少物件的數量。