layout: post
title: "PHP設(shè)計模式(三)-策略模式"
date: 2016-06-06 10:32:22 +0800
comments: true
categories: [php]
在之前學(xué)習(xí)了PHP設(shè)計模式中的工廠模式,單例模式囱桨,注冊樹模式旺隙,適配器模式每瞒。今天來學(xué)習(xí)一下策略模式稽鞭。
一、策略模式概念
策略模式針對一組算法炼团,將每一個算法封裝到具有共同接口的獨立的類中,此模式讓算法的變化獨立于使用算法的客戶咪惠。從而讓程序結(jié)構(gòu)更靈活击吱,具有更好的擴展性和維護性。
策略模式:定義了算法族,分別封裝起來硝逢,讓它們之間可以互相替換姨拥,此模式讓算法的變化獨立于使用算法的客戶绅喉。
封裝:把行為用接口封裝起來渠鸽,我們可以把那些經(jīng)常變化的部分,從當(dāng)前的類中單獨取出來柴罐,用接口進行單獨的封裝徽缚。
互相替換:我們封裝好了接口,通過指定不同的接口實現(xiàn)類進行算法的變化革屠。
二凿试、策略模式結(jié)構(gòu)圖
[圖片上傳失敗...(image-fa1ffc-1532594075563)]
三、策略模式角色說明
抽象策略(Strategy)角色:定義所有支持的算法的公共接口似芝。通常是以一個接口或抽象來實現(xiàn)那婉。Context使用這個接口來調(diào)用其ConcreteStrategy定義的算法。
具體策略(ConcreteStrategy)角色:以Strategy接口實現(xiàn)某具體算法
環(huán)境(Context)角色:持有一個Strategy類的引用党瓮,用一個ConcreteStrategy對象來配置
四详炬、實例一
比如說購物車系統(tǒng),在給商品計算總價的時候寞奸,普通會員肯定是商品單價乘以數(shù)量呛谜,但是對中級會員提供8者折扣,對高級會員提供7折折扣枪萄,這種場景就可以使用策略模式實現(xiàn):
/**
* 策略模式實例
*
*/
//抽象策略角色《為接口或者抽象類隐岛,給具體策略類繼承》
interface Strategy
{
public function computePrice($price);
}
//具體策略角色-普通會員策略類
class GenernalMember implements Strategy
{
public function computePrice($price)
{
return $price;
}
}
//具體策略角色-中級會員策略類
class MiddleMember implements Strategy
{
public function computePrice($price)
{
return $price * 0.8;
}
}
//具體策略角色-高級會員策略類
class HignMember implements Strategy
{
public function computePrice($price)
{
return $price * 0.7;
}
}
//環(huán)境角色實現(xiàn)類
class Price
{
//具體策略對象
private $strategyInstance;
//構(gòu)造函數(shù)
public function __construct($instance)
{
$this->strategyInstance = $instance;
}
public function compute($price)
{
return $this->strategyInstance->computePrice($price);
}
}
//客戶端使用
$p = new Price(new HignMember());
$totalPrice = $p->compute(100);
echo $totalPrice; //70
實例二
我來解釋下這個思維導(dǎo)圖的過程:
1.Joe做了一套相當(dāng)成功的模擬鴨子的游戲。設(shè)計了一個超類Duck,然后讓各種鴨子繼承這個類瓷翻。
2.后來客戶提出要讓鴨子有飛的能力聚凹。所以Joe就在超類中加了個fly()方法,這樣下面的子類都有飛行的行為齐帚。
問題來了:
- 原來Duck的子類中竟然有橡皮鴨妒牙,橡皮鴨是不會飛的⊥耍——Joe用重載的方式单旁,把橡皮鴨的fly()方法設(shè)置為空.
- 覆蓋fly(),我們看到了橡皮鴨的fly()里饥伊,沒有任何代碼象浑,如果以后我們再添加別的不會飛的鴨子蔫饰,那我么還要這么處理嗎?——那么代碼重復(fù)了!
3.上面2的方式我們知道是有問題的,所以Joe想到把Duck做成接口愉豺,這樣每個子類必須實現(xiàn)Duck里的方法篓吁。這樣就保證每個鴨子都能根據(jù)自己的需要添加行為。
問題來了:
- 產(chǎn)品經(jīng)常處于更新中蚪拦,規(guī)格也在不斷的變化杖剪。導(dǎo)致每當(dāng)有新鴨子的時候,Joe就要被迫檢查一遍子類是否覆蓋了fly()方法驰贷∈⒑伲——當(dāng)你修改某個行為的時候,你必須得往下追蹤并在每一個定義此行為的類中修改它括袒。
4.綜合以上問題次兆,Joe想到了把那些變化的部分從不變化的位置中抽出來。比如锹锰,我們對fly()行為芥炭,做了單獨的接口FlyBehavior。如果鴨子想要飛行功能的時候恃慧,我們就讓鴨子實現(xiàn)FlyBehavior.
5.深造:我們想讓鴨子有不同的飛行功能园蝠,讓它在運行時候做不同的飛行動作。讓鴨子類實現(xiàn)接口痢士,只能讓鴨子有一種行為彪薛。
所以Joe,想到用組合的防止良瞧,當(dāng)鴨子需要其他飛行功能要求的時候陪汽,我們可以用setBehavior()方式,指定性的飛行方式褥蚯。
interface FlyBehavior{
public function fly();
}
class FlyWithWings implements FlyBehavior{
public function fly(){
echo "Fly With Wings \n";
}
}
class FlyWithNo implements FlyBehavior{
public function fly(){
echo "Fly With No Wings \n";
}
}
class Duck{
private $_flyBehavior;
public function performFly(){
$this->_flyBehavior->fly();
}
public function setFlyBehavior(FlyBehavior $behavior){
$this->_flyBehavior = $behavior;
}
}
class RubberDuck extends Duck{
}
// Test Case
$duck = new RubberDuck();
/* 想讓鴨子用翅膀飛行 */
$duck->setFlyBehavior(new FlyWithWings());
$duck->performFly();
/* 想讓鴨子不用翅膀飛行 */
$duck->setFlyBehavior(new FlyWithNo());
$duck->performFly();
五挚冤、策略模式的控制和反轉(zhuǎn)
使用策略模式可以實現(xiàn)Ioc,依賴倒置和控制反轉(zhuǎn)
在原本的類中赞庶,存在一個A類中調(diào)用B類训挡,則A類是依賴于B類的。
通過幾個接口的設(shè)置和策略的解耦歧强,就把依賴進行了反轉(zhuǎn)澜薄,也就是我們在寫A類的時候不需要去實現(xiàn)B類。
只有在最后執(zhí)行的時候才進行綁定摊册。