zoukankan      html  css  js  c++  java
  • 【设计模式】二:策略模式

    <?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类中的这个方法啊就可以了,和你没有其他任何的关联
     * 对于不同的算法来说,我编写我的算法就好了,只要我测试我的算法接受正常的参数时可以正常运行,那就可以了
     *
     * 与策略模式比对:将判断实例化哪个类放到工厂模式的方法中进行,客户端需要做的只是传递参数即可,工厂方法会根据具体的参数判断出该实例化哪个类
     * 这样也大大降低了服务端与客户端的耦合性
     */
  • 相关阅读:
    如何使用go module导入本地包
    gin-vue-admin 03 项目打包上线
    golang map转xml
    vim简明文档
    goframe gf-cli的使用
    supervisor 的安装与使用
    element Tree 树形控件
    gin-vue-admin开发教程 02 了解项目目录结构和代码执行的流程
    gin-vue-admin开发教程 01安装与启用
    oraclesql遇见的问题(一)
  • 原文地址:https://www.cnblogs.com/rcltocode/p/10542608.html
Copyright © 2011-2022 走看看