策略模式属于对象行为型的设计模式
定义 :封装了一些列算法,它们之前可以相互替换,此模式使得算法的改变,不会影响到使用它们的客户端
策略模式有以下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.无法在客户端同时使用两种策略(我们的支付下单,需要从多种支付依照优先级一个个去下单,直到成功为止)