【設計模式】二:策略模式

<?php

/**
 * 接下來看的是策略模式,舉得例子是商場搞活動
 * 商場搞活動中須要不停的變化促銷方式,有打折啦,滿減啦,積分啦等等,並且打折力度也不同,積分也不同
 * 因此就須要使用策略模式,將變化封裝。策略模式的主要特色就是封裝變化,那爲何不使用工廠模式呢,
 * 每一次有活動我在工廠中新增一個選項,而後實例化一個新的類,這樣不也是能夠麼。那就下了就對比一下兩個模式的不同,
 * 看看爲何使用策略模式
 */

// 先從最基礎的構建開始,實現一個最簡單的功能,不須要考慮封裝繼承等

// 服務端
$total = 0;
function shop($price, $commodity) {
    $total = round($price * $commodity, 2);
    return $total;
}
// 客戶端
$total = shop(1.2, 2);
echo $total;

 

<?php

// 上述實現了正常銷售狀況,那接下來經理須要搞活動,要給商品打折,有不一樣的打折力度
// 服務端
$total = 0;
// 0: 五折, 1:8折, 2:9折
function shop($price, $commodity, $type) {
    switch ($type) {
        case '0':
            $total = round($price * $commodity, 2) * 0.5;
            break;
        case '1':
            $total = round($price * $commodity, 2) * 0.8;
            break;
        case '2':
            $total = round($price * $commodity, 2) * 0.9;
            break;
        default:
            return 'error';
    }
    return $total;
}
// 客戶端
$total = shop(1.2, 2, 1);
echo $total;

/**
 * 新增長了打折力度後,服務端須要修改的地方就比較多,若是如今經理又須要添加滿減的活動,有的要滿減,有的要在打折的基礎上滿減
 * 若是按照當前的架構來實現的話,只能是在switch中不斷的添加新的選項,同時要和客戶端約定好0是什麼,1是什麼等等,若是商場開了5年
 * 經理搞活動搞了2,300次,並且每次還搞的不同,那這個代碼將變的很龐大,很很差維護,並且客戶端與服務端對於type的約定也會致使bug的產生
 * 所以此處就須要使用面向對象的三大特性,以前還學習了簡單工廠模式,正好用來實現這個功能
 * 實現思路:
 * 1,將折扣封裝成一個抽象類,
 * 2,而後不一樣的折扣規則繼承這個抽象類
 * 3,新建一個建立工廠類,而後根據不一樣的打折規則,實例化不一樣的折扣規則
 */

 

 

<?php

// 抽象收費基類
abstract class CashSuper{
    public abstract function acceptCash($money);
}

// 正常收費類
class CashNormal extends  CashSuper
{
    public function acceptCash($money)
    {
        return $money;
    }
}

// 打折收費類
class CashRebate extends CashSuper
{
    private $moneyRebate = 1;
    public function __construct($moneyRebate)
    {
        $this->moneyRebate = $moneyRebate;
    }
    public function acceptCash($money)
    {
        // TODO: Implement acceptCash() method.
        return $money * $this->moneyRebate;
    }
}

// 滿減活動類
class CashReturn extends CashSuper
{
    // 滿減活動須要有滿減的條件和滿減的值,輸入300,100就是滿300減去100
    private $moneyCondition = 0;
    private $moneyReturn = 0;
    public function __construct($moneyCondition, $moneyReturn)
    {
        $this->moneyCondition = $moneyCondition;
        $this->moneyReturn = $moneyReturn;
    }
    public function acceptCash($money)
    {
        $result = $money;
        if ($money >= $this->moneyCondition) {
            $result = $money - floor($money / $this->moneyCondition) * $this->moneyReturn;
        }
        return $result;
    }
}

// 建立工廠內,實現自動實例化對象
class CashFactory
{
    private $cs = null;
    public function cteateCashAccept($type)
    {
        switch ($type)
        {
            case '正常收費':
                $this->cs = new CashNormal();
                break;
            case '打折':
                $this->cs = new CashRebate(0.8);
                break;
            case '滿300減100':
                $this->cs = new CashReturn(300, 100);
                break;
            default:
                return 'error';
        }
        return $this->cs;
    }
}

// 客戶端
$csf = new CashFactory();
$cs = $csf->cteateCashAccept('正常收費');
$res = $cs->acceptCash(100);
echo $res;
$cs = $csf->cteateCashAccept('打折');
$res = $cs->acceptCash(100);
echo $res;
$cs = $csf->cteateCashAccept('滿300減100');
$res = $cs->acceptCash(600);
echo $res;

/**
 * 上述方法是使用簡單工廠實現的,可是仍是具備缺點的,比較以前實現計算器的時候,
 * 計算器的規則是固定的,不會頻繁的改動,可是在此處這些促銷算法是不斷更新的
 */

 

<?php

/**
 * 針對上述問題,如今就能夠引出策略模式
 * 先看到策略模式的概念:
 * 策略模式定義了算法家族,分別封裝起來,讓他們之間能夠互相替換,此模式讓算法的變化,不會影響到使用算法的用戶
 * 接下來使用策略模式實現此功能,而後參照代碼理解上邊的概念
 */

// 抽象收費基類
abstract class CashSuper{
    public abstract function acceptCash($money);
}

// 正常收費類
class CashNormal extends  CashSuper
{
    public function acceptCash($money)
    {
        return $money;
    }
}

// 打折收費類
class CashRebate extends CashSuper
{
    private $moneyRebate = 1;
    public function __construct($moneyRebate)
    {
        $this->moneyRebate = $moneyRebate;
    }
    public function acceptCash($money)
    {
        // TODO: Implement acceptCash() method.
        return $money * $this->moneyRebate;
    }
}

// 滿減活動類
class CashReturn extends CashSuper
{
    // 滿減活動須要有滿減的條件和滿減的值,輸入300,100就是滿300減去100
    private $moneyCondition = 0;
    private $moneyReturn = 0;
    public function __construct($moneyCondition, $moneyReturn)
    {
        $this->moneyCondition = $moneyCondition;
        $this->moneyReturn = $moneyReturn;
    }
    public function acceptCash($money)
    {
        $result = $money;
        if ($money >= $this->moneyCondition) {
            $result = $money - floor($money / $this->moneyCondition) * $this->moneyReturn;
        }
        return $result;
    }
}

// 原先封裝好的具體活動規則視爲3個策略

// 接下來使用策略模式的 context 上下文處理類,就像簡單工廠模式具備建立工廠類同樣
class CashContext
{
    private $cs = null;
    // 使用構造方法,在實例化此類的時候,就須要將使用的策略傳遞進來
    public function __construct(CashSuper $cs)
    {
        $this->cs = $cs;
    }
    public function getResult($money)
    {
        return $this->cs->acceptCash($money);
    }
}

// 客戶端實現
function shop($type, $money) {
    $cs = null;
    switch ($type) {
        case '正常收費':
           $cs = new CashContext(new CashNormal());
           break;
        case '滿300減100':
            $cs = new CashContext(new CashReturn(300, 100));
            break;
        case '打折':
            $cs = new CashContext(new CashRebate(0.8));
            break;
    }
    $res = $cs->getResult($money);
    return $res;
}
$res = shop('滿300減100', 600);
echo $res;

/**
 * 此處使用了策略模式,使用了上下文處理類,調用者須要使用哪一種策略將對應的策略傳給進來就好
 * 到此處就有些懵逼,這策略模式和簡單工廠模式到底有什麼區別啊,並且還把判斷邏輯移動到客戶端去處理
 * 難道每一個使用此類的方法都本身去判斷啊?
 *
 * 若是這樣思考那就片面了,並非要去對比兩個模式哪一個好哪一個壞,並且每當碰到這種需求的時候,要學會使用哪一種模式去解決當前遇到的問題
 * 此處使用策略模式,能夠很好的將具體算法和客戶端進行了隔離,在未使用策略模式的時候,須要客戶端本身去調用算法的實現方法,
 * 這樣作的好處是什麼呢,就是下降耦合性,像在此處客戶端只接觸到了CashContext類,以及他認爲的算法實現方法getResult;
 * 這都是屬於CashContext類的,可是在工廠模式中就須要使用到具體算法的實現方法acceptCash。
 * 咱們使用策略模式很好的進行了封裝隔離,只將上下文處理類中的一個方法暴露給客戶端,大大下降了耦合性,客戶端升級,服務端升級改動等均可以
 * 很輕鬆的進行。那此處應當理解的就是封裝變化,封裝變化能夠下降耦合性。
 */

// 可是此處將判斷邏輯放到客戶端是很差的,那解決方案就是結合簡單工廠模式,將策略模式有簡單工廠模式結合使用

 

 

<?php

// 抽象收費基類
abstract class CashSuper{
    public abstract function acceptCash($money);
}

// 正常收費類
class CashNormal extends  CashSuper
{
    public function acceptCash($money)
    {
        return $money;
    }
}

// 打折收費類
class CashRebate extends CashSuper
{
    private $moneyRebate = 1;
    public function __construct($moneyRebate)
    {
        $this->moneyRebate = $moneyRebate;
    }
    public function acceptCash($money)
    {
        // TODO: Implement acceptCash() method.
        return $money * $this->moneyRebate;
    }
}

// 滿減活動類
class CashReturn extends CashSuper
{
    // 滿減活動須要有滿減的條件和滿減的值,輸入300,100就是滿300減去100
    private $moneyCondition = 0;
    private $moneyReturn = 0;
    public function __construct($moneyCondition, $moneyReturn)
    {
        $this->moneyCondition = $moneyCondition;
        $this->moneyReturn = $moneyReturn;
    }
    public function acceptCash($money)
    {
        $result = $money;
        if ($money >= $this->moneyCondition) {
            $result = $money - floor($money / $this->moneyCondition) * $this->moneyReturn;
        }
        return $result;
    }
}

// 原先封裝好的具體活動規則視爲3個策略

// 接下來使用策略模式的 context 上下文處理類,就像簡單工廠模式具備建立工廠類同樣
class CashContext
{
    private $cs = null;
    // 使用構造方法,在實例化此類的時候,就須要將使用的策略傳遞進來
    public function __construct($type)
    {
        switch ($type) {
            case '正常收費':
                $cs = new CashNormal();
                break;
            case '滿300減100':
                $cs = new CashReturn(300, 100);
                break;
            case '打折':
                $cs = new CashRebate(0.8);
                break;
        }
        $this->cs = $cs;
    }
    public function getResult($money)
    {
        return $this->cs->acceptCash($money);
    }
}

// 客戶端實現
function shop($type, $money) {
    $cs = new CashContext($type);
    $res = $cs->getResult($money);
    return $res;
}
$res = shop('滿300減100', 600);
echo $res;

/**
 * 此處將策略模式與簡單工廠模式的結合使用分別於策略模式,簡單工廠模式進行比對
 *
 * 與簡單工廠模式比對:其實就是增長了一個地方 getResult這個方法,而這個方法就是策略模式的使用,就是使用這個方法,將客戶端與實際的
 * 算法類進行了隔離,封裝了變化(算法),下降了耦合性。我客戶端只讓定你這個CashContext類中的這個方法啊就能夠了,和你沒有其餘任何的關聯
 * 對於不一樣的算法來講,我編寫個人算法就行了,只要我測試個人算法接受正常的參數時能夠正常運行,那就能夠了
 *
 * 與策略模式比對:將判斷實例化哪一個類放到工廠模式的方法中進行,客戶端須要作的只是傳遞參數便可,工廠方法會根據具體的參數判斷出該實例化哪一個類
 * 這樣也大大下降了服務端與客戶端的耦合性
 */
相關文章
相關標籤/搜索