优先考虑使用策略模式,而不是具体继承 (Rod) 产生的原因: 属于对象的行为模式。处理多于一个算法时候,把算法和使用算法的客户端分开(把行为和环境分割开),从而方便的选择其中一个算法。 设现在要设计一个贩卖各类书籍的电子商务网站的购物车(Shopping Cat)系统。一个最简单的情况就是把所有货品的单价乘上数量,但是实际情况肯定比这要复杂。比如,本网站可能对所有的教材类图书实行每本一元的折扣;对连环画类图书提供每本7%的促销折扣,而对非教材类的计算机图书有3%的折扣;对其余的图书没有折扣。由于有这样复杂的折扣算法,使得价格计算问题需要系统地解决。 使用策略模式可以把行为和环境分割开来。环境类负责维持和查询行为类,各种算法则在具体策略类(ConcreteStrategy)中提供。由于算法和环境独立开来,算法的增减、修改都不会影响环境和客户端。当出现新的促销折扣或现有的折扣政策出现变化时,只需要实现新的策略类,并在客户端登记即可。策略模式相当于"可插入式(Pluggable)的算法"。 程序架构:一个客户类,一个抽象策略类(接口),若干个具体策略类。由客户类决定选择那一个具体类。 定义一系列的算法,把他们一个个封装起来,并且使它们可相互替换。Strategy模式使算法可独立于使用它的客户而变化。 策略模式是对算法的包装,是把使用算法的责任和算法本身分割开,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。用一句话来说,就是:"准备一组算法,并将每一个算法封装起来,使得它们可以互换。" 策略又称做政策(Policy)模式【GOF95】。下面是一个示意性的策略模式结构图: 这个模式涉及到三个角色: · 环境(Context)角色:持有一个Strategy类的引用。 · 抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。 · 具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。 Strategy模式以下列几条原则为基础: 1) 每个对象都是一个具有职责的个体。 2) 这些职责不同的具体实现是通过多态的使用来完成的。 3) 概念上相同的算法具有多个不同的实现,需要进行管理 通过以下步骤,开发人员可以很容易地在软件中实现策略模型: 1)对策略对象定义一个公共接口。 2)编写策略类,该类实现了上面的公共接口。 3)策略对象的类中保存一个对策略对象的引用。 4)略对象的类中,实现对策略对象的set和get方法。 例: 首先,我们建立一个抽象类RepTempRule 定义一些公用变量和方法:
public abstract class RepTempRule{
protected String oldString=""; public void setOldString(String oldString){ this.oldString=oldString; }
protected String newString=""; public String getNewString(){ return newString; }
public abstract void replace() throws Exception;
} |
在RepTempRule中 有一个抽象方法abstract需要继承明确,这个replace里其实是替代的具体方法. 我们现在有两个字符替代方案, 1.将文本中aaa替代成bbb; 2.将文本中aaa替代成ccc;
对应的类分别是RepTempRuleOne RepTempRuleTwo
public class RepTempRuleOne extends RepTempRule{
public void replace() throws Exception{
//replaceFirst是jdk1.4新特性 newString=oldString.replaceFirst("aaa", "bbbb") System.out.println("this is replace one"); }
} |
public class RepTempRuleTwo extends RepTempRule{
public void replace() throws Exception{
newString=oldString.replaceFirst("aaa", "ccc") System.out.println("this is replace Two"); }
} |
第二步:我们要建立一个算法解决类,用来提供客户端可以自由选择算法。
public class RepTempRuleSolve { private RepTempRule strategy; public RepTempRuleSolve(RepTempRule rule){ this.strategy=rule; } public String getNewContext(Site site,String oldString) { return strategy.replace(site,oldString); } public void changeAlgorithm(RepTempRule newAlgorithm) { strategy = newAlgorithm; } } |
调用如下:
public class test{ ...... public void testReplace(){ //使用第一套替代方案 RepTempRuleSolve solver=new RepTempRuleSolve(new RepTempRuleOne()); solver.getNewContext(site,context); //使用第二套 solver=new RepTempRuleSolve(new RepTempRuleTwo()); solver.getNewContext(site,context); } ..... } |
我们达到了在运行期间,可以自由切换算法的目的。 实际整个Strategy的核心部分就是抽象类的使用,使用Strategy模式可以在用户需要变化时,修改量很少,而且快速. Strategy和Factory有一定的类似,Strategy相对简单容易理解,并且可以在运行时刻自由切换。Factory重点是用来创建对象。 Strategy适合下列场合: 1.以不同的格式保存文件; 2.以不同的算法压缩文件; 3.以不同的算法截获图象; 4.以不同的格式输出同样数据的图形,比如曲线 或框图bar等 策略模式的优点和缺点 策略模式有很多优点和缺点。它的优点有: 1. 策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码移到父类里面,从而避免重复的代码。 2. 策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为。如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。但是,这样一来算法或行为的使用者就和算法或行为本身混在一起。决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不可能再独立演化。继承使得动态改变算法或行为变得不可能。 3. 使用策略模式可以避免使用多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后。 策略模式的缺点有: 1. 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。 2. 策略模式造成很多的策略类。有时候可以通过把依赖于环境的状态保存到客户端里面, 其它 策略模式与很多其它的模式都有着广泛的联系。Strategy很容易和Bridge模式相混淆。虽然它们结构很相似,但它们却是为解决不同的问题而设计的。Strategy模式注重于算法的封装,而Bridge模式注重于分离抽象和实现,为一个抽象体系提供不同的实现。Bridge模式与Strategy模式都很好的体现了"Favor composite over inheritance"的观点。 推荐大家读一读《IoC 容器和Dependency Injection 模式》,作者Martin Fowler。网上可以找到中文版的PDF文件。为策略模式的实施提供了一个非常好的方案。 |