zoukankan      html  css  js  c++  java
  • 设计模式之 策略模式

    策略模式属于对象行为型的设计模式

    定义 :封装了一些列算法,它们之前可以相互替换,此模式使得算法的改变,不会影响到使用它们的客户端

    策略模式有以下3个角色组成

    抽象策略类 : 所有策略类的父类,为所支持的策略算法声明了抽象方法

    具体策略类 :实现抽象策略类的方法

    Context环境类 : 维护一个对Strategy对象的引用

    策略模式分离了算法的定义和使用,要做到这样客户端要依赖于策略接口,而不是具体的实现所有策略类对象可以互相替换,说明具有共同的特性->行为相同

    以下是PHP对上述UML的实现

    <?php
    /*
     *抽象策略类 
     */
    abstract class Stratrgy{
        abstract function AlgorithmInterface();
    }
    
    //具体实现类A
    class ConcreateStratrgyA extends Stratrgy{
        public function AlgorithmInterface() {
            echo '算法A', PHP_EOL;
        }
    }
    
    //具体实现类B
    class ConcreateStratrgyB extends Stratrgy{
        public function AlgorithmInterface() {
            echo '算法B', PHP_EOL;
        }
    }
    
    //具体实现类C
    class ConcreateStratrgyC extends Stratrgy{
        public function AlgorithmInterface() {
            echo '算法C', PHP_EOL;
        }
    }
    
    //上下文环境类
    class Context{
        private $_strategy;
        //通过构造方法注入策略对像
        public function __construct(Stratrgy $strategy) {
            $this->_strategy = $strategy;
        }
        public function ContextInterface(){
            $this->_strategy->AlgorithmInterface();
        }
    }
    
    //客户端类
    class Client{
        public static function main(){
            $context = new Context(new ConcreateStratrgyA());
            $context->ContextInterface();
            
            $context = new Context(new ConcreateStratrgyB());
            $context->ContextInterface();
            
            $context = new Context(new ConcreateStratrgyC());
            $context->ContextInterface();
        }
    }
    Client::main();

    在实际开发中,我们项目里很多地方都用到了策略模式,这里以支付回调为栗子介绍下策略模式在我们项目中的应用。

    首先所有的支付回调都是由第三方发起,行为相同,最终都是给用户发送道具并通知第三方。整个流程可以抽象为3个步骤 :验证第三方参数 ->发送道具给用户 ->返回报文给第三方,因此我们的抽象策类里可以定义3个抽象方法,另外一些公共使用的方法都可以放到抽象

    策略类里,具体策略类只需要继承就可以复用。策略环境类这里做了一些改动,加入了简单工厂模式生成具体的策略对象。每种支付的参数验证,发具体的道具数,以及报文响应都不相同,具体的支付类需要实现3个抽象方法。

    由微信切换成支付宝,需要增加对应的支付宝类,然后回调的时候在工厂方法传入支付宝参数即可,这样微信,支支付宝...达到了相互替换的目的,而且具体的支付宝微信类改变不会影响到回调的入口。

    抽简后的代码如下:

    <?php
    //抽象策略类
    abstract class StrategyNotify{
       //参数检查
       abstract function checkParam();
       //发送道具
       abstract function sendProp();
       //输出报文
       abstract function outputMessage();
       //支付回调状态
       protected $_status = -1;
       //第三方参数
       protected $_params = array();
    
       protected $_errMsg = array(
           '-1'=>'系统错误',
           '-2'=>'参数验证错误',
           '-3'=>'发送道具失败',
       );
       //通知方法
       public final function notify(){
           if(!$this->checkParam()){
               $this->_status = -2;
           }else if(!$this->sendProp()){
               $this->_status = -3;
           }else{
               $this->_status = 1;
           }
           if($this->_status != 1){
               $this->log();
           }
           $this->outputMessage();
       }
       //写日志例子
       protected function log(){
           echo isset($this->_errMsg[$this->_status]) ? $this->_errMsg[$this->_status] : '';
           echo PHP_EOL;
       }
    }
    
    //微信支付
    class Weixin extends StrategyNotify{
       public function checkParam() {
           return false;
       }
    
       public function outputMessage() {
           if($this->_status === 1){
               die('OK');
           }else{
               die('FAIL');
           }
       }
    
       public function sendProp() {
           return false;
       }
    }
    
    //支付环境类
    class Pay{
        private static $_notify = null;
        //策略工厂
        public static function factory($notify){
            if(class_exists($notify) && self::$_notify == null){
                self::$_notify = new $notify();
            }
            return self::$_notify;
        }
    }
    
    Pay::factory('Weixin')->notify();

    总结

    优点:

    1.当新增策略的时候,只需要增加具体的策略类,达到了开闭原则的目的

    2.分离了算法的定义和使用,使得算法可以复用

    缺点

    1.每增加一个策略就需要增加一个策略类(目前我们的app接近200种支付,意味着有200个子类)

    2.无法在客户端同时使用两种策略(我们的支付下单,需要从多种支付依照优先级一个个去下单,直到成功为止)

  • 相关阅读:
    openwrt编译出错处理记录
    转:小白编译openwrt固件教程
    转:openwrt中luci学习笔记
    (转)内存和外设的统一编址及独立编址
    (转)无法打开C盘,提示"本次操作由于这台计算机的限制被取消,请与你的管理员联系”
    (转)c# 解析JSON的几种办法
    计算机中断(转贴,很经典的解释)
    转:用WCAT进行IIS压力测试
    转:windows命令行下如何查看磁盘空间大小
    转:如何在32位程序中突破地址空间4G的限制
  • 原文地址:https://www.cnblogs.com/gaoqin31/p/7678469.html
Copyright © 2011-2022 走看看