设计模式之策略模式
软件工程师都会学习设计模式,设计模式可以说是前人智慧与经验的结晶,虽然不是哪里都用得到,但是在合适的地方使用合适的设计模式,能够带来巨大的收益。我通过学习《head first 设计模式》,逐渐掌握这些设计方法,本篇是对于策略模式的一点心得。
策略模式是对算法的包装,是把使用算法的类和算法本身分割开来,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。这样能够很灵活地操作这些方法,通过修改不同子类中的算法就可以达到对应的效果。举个例子,现在有一个Bird类,我们需要的是各种鸟的实例来操作它们。假设Bird这个类有两个个行为,fly和swim,而且show是都有的行为,但是每个子类的show又不一样,那么这么写:
abstract class Bird{ void fly(){System.out.println("fly...");}void swim(){System.out.println("swim...");}
abstract void show();
}
看起来没问题,现在业务需要的子类大雁、翠鸟等都妥妥的满足了。然而由于业务扩大,现在需要鸟类实现一个捕食方法,由于觉得所有鸟类都会捕食,而且捕食对象不相同,于是添加了prey的抽象方法,在子类中重载这个方法。后来,业务继续扩大,公司又添加了鸡这个子类,但是这个鸡并不需要捕食,这样一来,就出问题了。强行重载prey方法,然后在里面什么也不做,这显然不是一个明智的方法,那么把prey方法拿走,另外写一个接口Iprey,需要捕食的类就去实现这个接口怎么样,这样子的话,每个子类都要写一个自己的捕食方法,假如捕食方法有变动,所有需要捕食的子类都要改代码,这样也不是个事儿。
在这里的设计原则有:1、尽量把代码变和不变的部分分割开来;2、针对接口编程,而不是针对实现编程;这里的接口的真正意义是超类型,针对接口编程的真正意义在于多态,利用多态,程序可以针对超类型编程,执行时可以根据实际情况执行到真正的行为,而不会绑死在超类型的行为上。
于是我们在Iprey接口的基础上,设计了两个类来实现这个接口,其中一个类PreyBehavior,另一个类NoPrey,分别在两个类里有捕食方法和不捕食的方法:
public interface Iprey{ void prey(); } class PreyBehavior{ void prey(){ System.out.println("prey..."); } } class NoPrey{ void prey(){ System.out.println("No prey..."); } }
现在就可以整合一下Bird的行为了:
public abstract class Bird { Iprey preyBehavior; void fly() { System.out.println("fly..."); } void swim() { System.out.println("swim..."); } void performPrey(){ preyBehavior.prey(); } abstract void show(); }
我们在Bird类里面呢加了个performPrey方法,里面有一个preyBehavior对象,这么做可以让子类选择怎样实现prey方法:
public class Duck extends Bird{ public Duck(){ preyBehavior = new NoPrey(); } @Override void show() { System.out.println("duck..."); } }
当然,我们可以在Bird类里面加上setPreyBehavior方法来动态控制Prey的动作,这个不提。