某种程度上,策略模式不值得一提,因为学习和理解它,其难度系数为0。不就是一个类层次的问题吗。
所以,我们添加一点内容:方法对象化――将方法封装为类型。
另外,从重构分支结构的角度看,策略模式与[1.3.2工厂方法模式(3.3)]和[3.2状态模式(5.8)]是三胞胎。
1.父类定义接口,子类给出实现
外出旅游时,可以在多种不同的出行方式中选择,如坐汽车/火车/飞机、骑自行车或步行。海关对不同的商品按照不同的税率征收关税……做事情的方式即策略或政策。在源代码中,一个方法体常常被称为一个算法,故封装做事情方式的一个方法体,被称为一个具体的策略。
策略模式以类层次定义和封装算法集:父类定义接口,子类给出实现。【策略模式(Strategy Pattern):定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换,策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。】
如【编程导论・11 排序】介绍了各种各样的排序算法:选择、插入和交换排序等。为了方便地测试各种算法,抽象类algorithm.sorting.IntSort封装了抽象方法int[] sort(int[] arr),各种排序算法被设计成IntSort的子类。当然,IntSort除了包括策略,还可以提供了工具方法:如public static void swap(int[] arr ,int one, int two)――交换数组的两个元素。
从封装算法集的角度看,策略模式就是设计一个类层次。因而,学习和理解它,其难度系数为0。策略模式仅仅简单地使用了多态技术。正因为策略模式仅仅简单地使用了多态技术,因而策略模式成为其他模式常用的一个基本技术。也可以说,策略模式没有技术含量。
2. 将策略从环境类中分离出来
还是以排序为例,
package method.strategy;
public class Context{
public void test(String s,int i,int j,int[] array){
if(s == "选择") m1(i,j);
if(s == "插入") m2(array);
if(s == "交换") m3(array,i);
}
private void m1(int i,int j){}
private void m2(int[] array){}
private void m3(int[] array,int i){}
}
如果按照分支结构测试各种算法,在可能增加分支的场合该代码不遵循OCP。显然的解决方案是,设计MyStrategy的abstract方法
m(int i,int j,int[] array)封装各种算法m1、m2、m3。在实际编程中,原来的m1()、m2()、m3()
可能需要各种不同的参数。因此设计抽象方法m()时,在保证返回值类型确定的前提下,m()必须能够保证所有具体的策略类能够获得它需要的所有数据,因而在定义m()的接口时,它的参数列表是具体的策略类所需数据的最大集合。最大集合意味着宁可多不可少――某些具体策略类可以不使用参数列表提供的数据,但是不可使得某些具体策略类缺乏需要的数据。当然,仅就排序而言,参数情况简单。
package method.strategy;
public interface MyStrategy{
public abstract int[] sort(int[] arr);
}// MyStrategy的子类型,略
注意,我们由于拥有依赖注入工具tips.IoC,因而代码
package method.strategy;
import tips.IoC;
public class Context{
private MyStrategy s;
//依赖注入
public void setStrategy(MyStrategy s){
this.s=s;
}
public static void test(int[] arr){
MyStrategy st =(MyStrategy)IoC.getObject("插入");
int[] array = st.sort(arr);
}
}
如果要画出策略模式的结构图/UML图,仅需要画出了Context与MyStrategy两者。
学习设计模式时,绝大多数情况下,我会忽略抽象类型如MyStrategy的子类型。一方面,子类型是必须有的,代码大家自己写;另一方面,通过IoC工具,Context不需要知道MyStrategy的子类型,因而,不画出各子类型不影响读者对该模式的理解。
策略模式的基本内容,就这么一点。
3.