一、设计模式
设计模式可以分为三类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
用图片描述如下所示:(图片来自网络)
二、策略模式
1. 什么是策略模式
策略模式是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理,最终可以实现解决多重if判断问题。
1.环境(Context)角色:持有一个Strategy的引用。 2.抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。 3.具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。
定义策略接口->实现不同的策略类->利用多态或其他方式调用策略。
2. 策略模式优缺点
优点:算法可以自由切换(高层屏蔽算法,角色自由切换) 避免使用多重条件判断(如果算法过多就会出现很多种相同的判断,很难维护) 扩展性好(可自由添加取消算法 而不影响整个功能)。
缺点:策略类数量增多(每一个策略类复用性很小,如果需要增加算法,就只能新增类) 所有的策略类都需要对外暴露(使用的人必须了解使用策略,这个就需要其它模式来补充,比如工厂模式、代理模式)。
3. 策略模式实例说明
我们以数组输出为例。
数组的输出有序列化输出、JSON字符串输出和数组格式输出等方式。每种输出方式都可以独立封装起来,作为一个策略。
应用时,如要把数组保存到数据库中,可以用序列化方式输出。要提供给APP作接口,可以用JSON字符串输出。其他程序调用,则直接输出数组格式。
3.1 无设计模式情况下存在的问题
在没有设计模式的情况,我们用一个类集中处理数组输出,如下:
1 /** 2 * 根据给定类型,将数组转换后输出 3 */ 4 class Output 5 { 6 public function render($array, $type = '') 7 { 8 if ($type === 'serialize') { 9 return serialize($array); 10 } elseif ($type === 'json') { 11 return json_encode($array); 12 } else { 13 return $array; 14 } 15 } 16 }
客户端直接使用这个类来处理数组,就能达到效果:
1 /** 2 * 客户端代码 3 */ 4 $test = ['a', 'b', 'c']; 5 6 // 实例化输出类 7 $output = new Output(); 8 9 // 直接返回数组 10 $data = $output->render($test, 'array'); 11 12 // 返回JSON字符串 13 $data = $output->render($test, 'json');
这种方法的优点是简单、快捷,在小方案中使用非常合适。
但是,如果是一个复杂方案,包括大量的处理逻辑需要封装,或者处理方式变动较大,则就显得混乱。当需要添加一种算法,就必须修改Output类,影响原有代码,可扩展性差。如果输出方式很多,if-else或switch-case语句也会很多,代码混乱难以维护。
3.2 使用策略模式解决问题
首先,定义一系列的策略类,它们独立封装,并且遵循统一的接口。1 /** 2 * 策略接口 3 */ 4 interface OutputStrategy 5 { 6 public function render($array); 7 } 8 9 /** 10 * 策略类1:返回序列化字符串 11 */ 12 class SerializeStrategy implements OutputStrategy 13 { 14 public function render($array) 15 { 16 return serialize($array); 17 } 18 } 19 20 /** 21 * 策略类2:返回JSON编码后的字符串 22 */ 23 class JsonStrategy implements OutputStrategy 24 { 25 public function render($array) 26 { 27 return json_encode($array); 28 } 29 } 30 31 /** 32 * 策略类3:直接返回数组 33 */ 34 class ArrayStrategy implements OutputStrategy 35 { 36 public function render($array) 37 { 38 return $array; 39 } 40 }
环境类:环境角色用来管理策略,实现不同策略的切换功能。同样,一旦写好,环境角色类以后也不需要修改了。
1 /** 2 * 环境角色类 3 */ 4 class Output 5 { 6 private $outputStrategy; 7 8 // 传入的参数必须是策略接口的子类或子类的实例 9 public function __construct(OutputStrategy $outputStrategy) 10 { 11 $this->outputStrategy = $outputStrategy; 12 } 13 14 public function renderOutput($array) 15 { 16 return $this->outputStrategy->render($array); 17 } 18 }
客户端代码:在客户端中,策略模式通过给予不同的具体策略,来获取不同的结果。对于较为复杂的业务逻辑显得更为直观,扩展也更为方便。
1 /** 2 * 客户端代码 3 */ 4 $test = ['a', 'b', 'c']; 5 6 // 需要返回数组 7 $output = new Output(new ArrayStrategy()); 8 $data = $output->renderOutput($test); 9 10 // 需要返回JSON 11 $output = new Output(new JsonStrategy()); 12 $data = $output->renderOutput($test);
3.3 特点分析:
策略模式主要用来分离算法,根据相同的行为抽象来做不同的具体策略实现。策略模式结构清晰明了、使用简单直观。并且耦合度相对而言较低,扩展方便。同时操作封装也更为彻底,数据更为安全。
当然策略模式也有缺点,就是随着策略的增加,子类也会变得繁多。但缺点并不会影响系统运行,所以在复杂业务中应该考虑使用。
4. github版本库URL
https://github.com/happyyouli/Strategy-mode