案例分析:策略模式

一、设计模式

  设计模式可以分为三类:

  创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

  结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

  行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

  用图片描述如下所示:(图片来自网络)

  

二、策略模式

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

猜你喜欢

转载自www.cnblogs.com/happyyouli/p/12007929.html
今日推荐