【设计模式】二:策略模式

<?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类中的这个方法啊就能够了,和你没有其余任何的关联
 * 对于不一样的算法来讲,我编写个人算法就行了,只要我测试个人算法接受正常的参数时能够正常运行,那就能够了
 *
 * 与策略模式比对:将判断实例化哪一个类放到工厂模式的方法中进行,客户端须要作的只是传递参数便可,工厂方法会根据具体的参数判断出该实例化哪一个类
 * 这样也大大下降了服务端与客户端的耦合性
 */
相关文章
相关标签/搜索