设计模式历险记之策略模式

策略模式:它定义了算法家族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的客户。

我们先看看它的结构图:

从结构图我们大致知道策略模式有一个应用策略的上下文类,该类主要是用来选择用哪一个策略类。它的右边是策略抽象类,它和策略抽象类之间是聚合关系。抽象策略类下面有不同继承它的具体策略类。

举一个栗子:

某超市现在需要做一款收银软件,超市会不定期对不同商品有不同的优惠,比如说打折,满减,积分等等。

我们先分析:
对商品的最终价格计算是通过算法计算出来的。
不同的优惠方法虽然计算价格的方式不一样,但是最终的目的都是是算出一个价格。
无论通过哪一种方式计算价格,我们整个购物流程中,计算的位置总是一样的,总是最后才计算。

下面用代码来实现上面的分析

<?php

//选择应用策略的上下文
class strategyContext{

    protected $stategy;

    //通过简单工厂方法来选择应该是用哪一种类
    public function getStrategy($select){
        switch ($select) {
            case 0:
                $this->stategy = new normalStrategy();
                break;
            case 1:
                $this->stategy = new discountStrategy();
                break;
            case 2:
            default:
                $this->stategy = new reduceStrategy();
                break;
        }

        return $this;
    }

    //获取实际金额
    public function getMoney($money,...$arg){
        return $this->stategy->getMoney($money,$arg);
    }
}

//抽象策略
abstract class CashStrategy{
     public abstract function getMoney($money,...$arg);
}

//打折策略
class discountStrategy extends CashStrategy{
    public function getMoney($money,...$arg){
        return $money * $arg[0][0];
    }
}

//减满策略
class reduceStrategy extends CashStrategy{
    public function getMoney($money,...$arg){
        if($money >= $arg[0][0]){
            return $money - $arg[0][1];
        }
    }
}

//没有策略
class normalStrategy extends CashStrategy{
    public function getMoney($money,...$arg){
        return $money;
    }
}

$strategyContext = new strategyContext();

echo '价值100元的商品不打折,实际付款:';
echo $strategyContext->getStrategy(0)->getMoney(100),PHP_EOL;

echo '价值200元的商品打8折,实际付款:';
echo $strategyContext->getStrategy(1)->getMoney(200,0.8),PHP_EOL;

echo '价值300元的商品满300减20,实际付款:';
echo $strategyContext->getStrategy(2)->getMoney(300,300,20),PHP_EOL;

结合我们的分析和实际的代码分析:
每种策略类实现获得金额的过程不一样,最后实现的目都是为了获得实际金额。所以我们用抽象类来规定每种策略的目的方法,该方法通过不同的实现来达到相同的目的。
在应用策略的上下文类中,存在一个简单工厂方法,它通过上下文的逻辑来选择具体的策略。

emmm,分析了这么多,代码也写了,可能有写同学还是不明白策略模式的好处在哪里,这里我再总结一下:
策略模式将调用策略的类和策略类解耦,这样使得添加策略,修改策略满足开放关闭原则。
所有策略继承一个抽象策略类,这样有利于公用不同策略的公共部分。
因为每个策略都是一个单独的类,方便测试。

拓展一下吧:

A.这里,我们的抽象策略类为什么用抽象类而不用接口呢?
原因是:不同的策略很大几率都会存在共同的公共部分,这个时候我们可以将公共部分可以放到抽象类中,如果遇到特殊的实现,可以在具体特殊的类中重写即可。

B.策略模式也不一定用到算法中,它能封装任何类型的规则,只要是在不同的时间或者不同的场景下应用不同的业务规则来达到相同的目的,都可以考虑用策略模式。

c.我们再看一下上面的应用策略上下文类:

//选择应用策略的上下文
class strategyContext{

    protected $stategy;

    //通过简单工厂方法来选择应该是用哪一种类
    public function getStrategy($select){
        switch ($select) {
            case 0:
                $this->stategy = new normalStrategy();
                break;
            case 1:
                $this->stategy = new discountStrategy();
                break;
            case 2:
            default:
                $this->stategy = new reduceStrategy();
                break;
        }

        return $this;
    }
    .
    .
    .

我们若添加了一个新的策略,就必须要去修改这个简单工厂了。
emmm,这样不是就不满足我们的开放关闭原则了啊,我们有办法避免这种情况吗?
当然是有的,我们可以通过反射将需要的策略注入到策略选择类。
具体的代码实现在此就不给出,有兴趣的童鞋可以自己实现下。

Snail's Blog
请先登录后发表评论
  • 最新评论
  • 总共0条评论
  • 本博客使用免费开源的 laravel-bjyblog v5.5.1.3 -develop 搭建 © 2014-2018 www.snail-c.cn 版权所有 ICP证:蜀ICP备18023253号-1
  • 联系邮箱:459921737@qq.com