设计模式 ( 十八 ) 策略模式Strategy(对象行为型)

设计模式 ( 十八 ) 策略模式Strategy(对象行为型)

1.概述

        在软件开发中也经常遇到类似的情况,实现某一个功能有多种算法或者策略,我们能够依据环境或者条件的不同选择不同的算法或者策略来完毕该功能。如查找、排序等,一种经常使用的方法是硬编码(Hard Coding)在一个类中,如须要提供多种查找算法,能够将这些算法写到一个类中,在该类中提供多个方法,每个方法相应一个详细的查找算法;当然也能够将这些查找算法封装在一个统一的方法中,通过if…else…或者case等条件推断语句来进行选择。这两种实现方法我们都能够称之为硬编码,假设须要添加一种新的查找算法,须要改动封装算法类的源码;更换查找算法,也须要改动client调用代码。在这个算法类中封装了大量查找算法,该类代码将较复杂,维护较为困难。假设我们将这些策略包括在client,这样的做法更不可取,将导致client程序庞大并且难以维护,假设存在大量可供选择的算法时问题将变得更加严重。

样例1:一个菜单功能可以依据用户的“皮肤”首选项来决定是否採用水平的还是垂直的排列形式。同事可以灵活添加菜单那的显示样式。

样例2:出行旅游:我们能够有几个策略能够考虑:能够骑自行车,汽车,做火车,飞机。每一个策略都能够得到同样的结果,可是它们使用了不同的资源。选择策略的根据是费用,时间,使用工具还有每种方式的方便程度 。



2.问题

怎样让算法和对象分开来,使得算法能够独立于使用它的客户而变化?

3.解决方式

策略模式义一系列的算法,把每个算法封装起来, 而且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。也称为政策模式(Policy)。(Definea family of algorithms,encapsulate each one, andmake them interchangeable. Strategy lets the algorithmvary independently from clients that use it. 

策略模式把对象本身和运算规则区分开来,其功能很强大,由于这个设计模式本身的核心思想就是面向对象编程的多形性的思想。

4.适用性

当存在下面情况时使用Strategy模式
1)• 很多相关的类不过行为有异。 “策略”提供了一种用多个行为中的一个行为来配置一个类的方法。即一个系统须要动态地在几种算法中选择一种。
2)• 须要使用一个算法的不同变体。比如,你可能会定义一些反映不同的空间 /时间权衡的算法。当这些变体实现为一个算法的类层次时 ,能够使用策略模式。
3)• 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
4)• 一个类定义了多种行为 , 而且这些行为在这个类的操作中以多个条件语句的形式出现。将相关的条件分支移入它们各自的Strategy类中以取代这些条件语句。

5.结构


6.模式的组成

环境类(Context):用一个ConcreteStrategy对象来配置。维护一个对Strategy对象的引用。可定义一个接口来让Strategy訪问它的数据。
抽象策略类(Strategy):定义全部支持的算法的公共接口。 Context使用这个接口来调用某ConcreteStrategy定义的算法。
详细策略类(ConcreteStrategy):以Strategy接口实现某详细算法。

7.效果

Strategy模式有以下的一些长处:
1) 相关算法系列 Strategy类层次为Context定义了一系列的可供重用的算法或行为。 继承有助于析取出这些算法中的公共功能。
2) 提供了能够替换继承关系的办法: 继承提供了还有一种支持多种算法或行为的方法。你能够直接生成一个Context类的子类,从而给它以不同的行为。但这会将行为硬行编制到 Context中,而将算法的实现与Context的实现混合起来,从而使Context难以理解、难以维护和难以扩展,并且还不能动态地改变算法。最后你得到一堆相关的类 , 它们之间的唯一区别是它们所使用的算法或行为。 将算法封装在独立的Strategy类中使得你能够独立于其Context改变它,使它易于切换、易于理解、易于扩展。
3) 消除了一些if else条件语句 :Strategy模式提供了用条件语句选择所需的行为以外的还有一种选择。当不同的行为堆砌在一个类中时 ,非常难避免使用条件语句来选择合适的行为。将行为封装在一个个独立的Strategy类中消除了这些条件语句。含有很多条件语句的代码通常意味着须要使用Strategy模式。
4) 实现的选择 Strategy模式能够提供同样行为的不同实现。客户能够依据不同一时候间 /空间权衡取舍要求从不同策略中进行选择。

Strategy模式缺点:

1)client必须知道全部的策略类,并自行决定使用哪一个策略类:  本模式有一个潜在的缺点,就是一个客户要选择一个合适的Strategy就必须知道这些Strategy究竟有何不同。此时可能不得不向客户暴露详细的实现问题。因此仅当这些不同行为变体与客户相关的行为时 , 才须要使用Strategy模式。
2 ) Strategy和Context之间的通信开销 :不管各个ConcreteStrategy实现的算法是简单还是复杂, 它们都共享Strategy定义的接口。因此非常可能某些 ConcreteStrategy不会都用到全部通过这个接口传递给它们的信息;简单的 ConcreteStrategy可能不使用当中的不论什么信息!这就意味着有时Context会创建和初始化一些永远不会用到的參数。假设存在这样问题 , 那么将须要在Strategy和Context之间更进行紧密的耦合。
3 )策略模式将造成产生非常多策略类:能够通过使用享元模式在一定程度上降低对象的数量。 添加了对象的数目 Strategy添加了一个应用中的对象的数目。有时你能够将 Strategy实现为可供各Context共享的无状态的对象来降低这一开销。不论什么其余的状态都由 Context维护。Context在每一次对Strategy对象的请求中都将这个状态传递过去。共享的 Strategy不应在各次调用之间维护状态。

8.实现

1)出行旅游:

uml:


代码实现:

<?php
/**
* 策略模式
* 定义一系列的算法,把每个算法封装起来, 而且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化
*
*/ 


/**
* 出行旅游
*
* 
*/
interface TravelStrategy{
	public function travelAlgorithm();
} 


/**
 * 详细策略类(ConcreteStrategy)1:乘坐飞机
 */
class AirPlanelStrategy implements TravelStrategy {
	public function travelAlgorithm(){
		echo "travel by AirPlain", "<BR>\r\n"; 
	}
} 


/**
 * 详细策略类(ConcreteStrategy)2:乘坐火车
 */
class TrainStrategy implements TravelStrategy {
	public function travelAlgorithm(){
		echo "travel by Train", "<BR>\r\n"; 
	}
} 

/**
 * 详细策略类(ConcreteStrategy)3:骑自行车
 */
class BicycleStrategy implements TravelStrategy {
	public function travelAlgorithm(){
		echo "travel by Bicycle", "<BR>\r\n"; 
	}
} 



/**
 * 
 * 环境类(Context):用一个ConcreteStrategy对象来配置。维护一个对Strategy对象的引用。可定义一个接口来让Strategy訪问它的数据。
 * 算法解决类,以提供客户选择使用何种解决方式:
 */
class PersonContext{
	private $_strategy = null;

	public function __construct(TravelStrategy $travel){
		$this->_strategy = $travel;
	}
	/**
	* 旅行
	*/
	public function setTravelStrategy(TravelStrategy $travel){
		$this->_strategy = $travel;
	}
	/**
	* 旅行
	*/
	public function travel(){
		return $this->_strategy ->travelAlgorithm();
	}
} 

// 乘坐火车旅行
$person = new PersonContext(new TrainStrategy());
$person->travel();

// 改骑自行车
$person->setTravelStrategy(new BicycleStrategy());
$person->travel();

?> 


 

2)排序策略:某系统提供了一个用于对数组数据进行操作的类,该类封装了对数组的常见操作,

如查找数组元素、对数组元素进行排序等。现以排序操作为例,使用策略模式设计该数组操作类,

使得client能够动态地更换排序算法,能够依据须要选择冒泡排序或选择排序或插入排序,

也可以灵活地添加新的排序算法。

9.与其它相关模式

1)状态模式

策略模式和其他很多设计模式比較起来是很类似的。策略模式和状态模式最大的差别就是策略模式仅仅是的条件选择仅仅运行一次,而状态模式是随着实例參数(对象实例的状态)的改变不停地更改运行模式。换句话说,策略模式仅仅是在

对象初始化的时候更改运行模式,而状态模式是依据对象实例的周期时间而动态地改变对象实例的运行模式。

能够通过环境类状态的个数来决定是使用策略模式还是状态模式。
策略模式的环境类自己选择一个详细策略类,详细策略类无须关心环境类;而状态模式的环境类因为外在因素须要放进一个详细状态中,
以便通过其方法实现状态的切换,因此环境类和状态类之间存在一种双向的关联关系。
使用策略模式时,client须要知道所选的详细策略是哪一个,而使用状态模式时,client无须关心详细状态,环境类的状态会依据用户的操作自己主动转换。
假设系统中某个类的对象存在多种状态,不同状态下行为有差异,并且这些状态之间能够发生转换时使用状态模式
假设系统中某个类的某一行为存在多种实现方式,并且这些实现方式能够互换时使用策略模式

2)简单工厂的差别:点击打开链接

工厂模式是创建型模式 ,它关注对象创建,提供创建对象的接口. 让对象的创建与详细的使用客户无关。
策略模式是对象行为型模式 ,它关注行为和算法的封装 。它定义一系列的算法,把每个算法封装起来, 而且使它们可相互替换。使得算法可独立于使用它的客户而变化

用我们上面提到旅行的样例:
我们去旅行。策略模式的做法:有几种方案供你选择旅行,选择火车好呢还是骑自行车,全然有客户自行决定去构建旅行方案(比方你自己须要去买火车票,或者机票)。而工厂模式是你决定哪种旅行方案后,不用关注这旅行方案怎么给你创建,也就是说你告诉我方案的名称就能够了,然后由工厂取代你去构建详细方案(工厂取代你去买火车票)。

上面的样例里面client代码:
$person = new PersonContext(new TrainStrategy());
$person->travel();
我们看到客户须要自己去创建详细旅行(new TrainStrategy())实例。传递的是详细实例。
而工厂模式你仅仅要告诉哪种旅行就能够了,不是传递一个详细实例,而是一个标识(旅行方案标识)。

10.总结与分析

1)策略模式是一个比較easy理解和使用的设计模式,策略模式是对算法的封装它把算法的责任和算法本身切割开委派给不同的对象管理。策略模式通常把一个系列的算法封装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是“准备一组算法,并将每个算法封装起来,使得它们能够互换”。
2)在策略模式中,应当由client自己决定在什么情况下使用什么详细策略角色。2)
3)策略模式只封装算法,提供新算法插入到已有系统中以及老算法从系统中“退休”的方便,策略模式并不决定在何时使用何种算法,算法的选择由client来决定。这在一定程度上提高了系统的灵活性,可是client须要理解全部详细策略类之间的差别,以便选择合适的算法,这也是策略模式的缺点之中的一个,在一定程度上添加了client的使用难度。

转载指明原文出处:hguisu   设计模式 ( 十八 ) 策略模式Strategy(对象行为型) http://blog.csdn.net/hguisu/article/details/7558249


posted @ 2015-03-23 16:58  phlsheji  阅读(264)  评论(0编辑  收藏  举报