每天一个设计模式(1):策略模式
1.策略模式
策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数。
一.问题的提出
一个模拟鸭子的游戏,游戏中出现各种鸭子,原本的设计是:设计了一个鸭子超类,并让各种鸭子继承此超类。
但是要加新功能:比如会飞的鸭子?
并且产品会不断更新。
两种不好的解决方案:
1.继承基类
如果在基类中加上fly()方法,所有的鸭子都会继承,造成其他鸭子不想要的改变,比如玩具鸭子不想要会飞。
原先设计中,鸭子叫的方法也有问题,各种鸭子叫声不一致,橡皮鸭不会叫。
并且每当有新的鸭子子类出现,都需要检查并可能需要覆盖这些行为。
2.继承接口
如果使用接口来定义行为,子类根据需要实现接口,(比如,只有会飞的鸭子才实现Flyable接口),虽然可以做到行为的定制,但是却造成代码的无法复用,因为接口不提供实现代码。这就意味着,如果需要修改一个行为,那么必须在每一个定义此行为的类中修改它。
二.设计原则
1.找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
2.针对接口编程,而不是针对实现编程。
“针对接口编程”真正的意思是“针对超类型(supertype)编程”,这里所谓的“接口”,更明确地说,变量的声明类型应该是超类型,通常是一个抽象类或者是一个接口,针对接口编程的关键就在多态。
利用多态,程序可以针对超类型编程,执行时会根据实际状况执行到真正的行为,不会被绑死在超类型的行为上。
只要是具体实现此超类型的类所产生的对象,都可以指定给这个超类型的变量。这也意味着,声明类时不用理会以后执行时的真正对象类型。
3.多用组合,少用继承。
“有一个”可能比“是一个”更好。
使用组合建立系统具有很大的弹性,不仅可以将算法族封装成类,更可以“在运行时动态地改变行为”,只要组合的行为对象符合正确的接口标准即可。
三.关于鸭子问题的解决方案
分类变化的部分:将飞行和叫的动作从基类Duck中分离出来。
利用接口代替每个行为:定义FlyBehavior与QuackBehavior行为接口,鸭子类并不实现这些接口,是由我们创造一组其他类专门实现FlyBehavior与QuackBehavior。
这样的设计,可以让各种飞行的动作被其他的对象复用,并且新增行为也不会影响到既有行为。
具体做法:在基类Duck中加入两个实例变量,分别为FlyBehavior flyBehavior与QuackBehavior quackBehavior,注意声明为接口类型,而不是具体的实现类型,每个鸭子对象都会动态地设置这些变量以在运行时引用正确的行为类型。
然后在基类中实现方法,调用相应的变量的方法,比如:
public void performFly()
{
flyBehavior.fly()
}
设定变量可以在子类的构造函数中进行,这样就可以为每个子类设定不同的行为,即变量指向不同的类(该类实现了相应的接口)的对象。
四.UML关系图
图中ICalculator提供同意的方法,
AbstractCalculator是辅助类,提供辅助方法。
五.实现代码
首先统一接口:
public interface ICalculator { public int calculate(String exp); }
辅助类:
1 public abstract class AbstractCalculator { 2 3 public int[] split(String exp,String opt){ 4 String array[] = exp.split(opt); 5 int arrayInt[] = new int[2]; 6 arrayInt[0] = Integer.parseInt(array[0]); 7 arrayInt[1] = Integer.parseInt(array[1]); 8 return arrayInt; 9 } 10 }
三个实现类:
1 public class Plus extends AbstractCalculator implements ICalculator { 2 3 @Override 4 public int calculate(String exp) { 5 int arrayInt[] = split(exp,"\\+"); 6 return arrayInt[0]+arrayInt[1]; 7 } 8 } 9 10 public class Minus extends AbstractCalculator implements ICalculator { 11 12 @Override 13 public int calculate(String exp) { 14 int arrayInt[] = split(exp,"-"); 15 return arrayInt[0]-arrayInt[1]; 16 } 17 18 } 19 20 public class Multiply extends AbstractCalculator implements ICalculator { 21 22 @Override 23 public int calculate(String exp) { 24 int arrayInt[] = split(exp,"\\*"); 25 return arrayInt[0]*arrayInt[1]; 26 } 27 }
简单的测试类:
1 public class StrategyTest { 2 3 public static void main(String[] args) { 4 String exp = "2+8"; 5 ICalculator cal = new Plus(); 6 int result = cal.calculate(exp); 7 System.out.println(result); 8 } 9 }
输出:10
策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。
参考:
《Head Frist 设计模式》
http://www.cnblogs.com/java-my-life/archive/2012/05/10/2491891.html
http://www.cnblogs.com/mengdd/archive/2012/12/26/2834884.html