设计模式之于程序员,是一种诱惑,不管你信不信,反正我信。不懂则必不会用,学习设计模式,在于在某个时候,需要实现某些功能,你会想到,哦,历经千山万水,原来你也在这里。网上资料云集,看一个例子看不懂,就多看几个,自然知其然且知其所以然。
这里我会记录自己学习策略模式的例子,这个例子来自于Head First设计模式;并分享一个实例,来自于实际应用。
Head First设计模式是从一个鸭子模拟游戏开始讲述策略模式的,游戏中会出现各种鸭子,一边游泳,一边呱呱叫。
- 第一次设计是这样的,设计一个超类Duck,有呱呱叫行为quack,游泳行为swim,这两个行为在超类实现,被所有子类继承;另显示鸭子自己外观的方法display设计为抽象方法,由子类自己实现;这里有两个子类绿头鸭MallarDuck和红头鸭HedHeadDuck,类图如下:
- 第二次的时候,需求有点小变更,需要让鸭子能飞。想都不用想,在超类里面加上一个fly方法就够了不是么!
现在的问题是,一只橡皮鸭,居然也会飞(它不应该会飞)!因为在超类中增加了fly,所以所有的子类都具备了fly行为。既然这样不行,那么可以在橡皮鸭RubberDuck中覆盖超类的fly不久就可以了哇,就像覆盖quack呱呱叫方法一样!如果以后想加入诱饵鸭,它是一只木头假鸭,不会飞不会叫,同样需要覆盖quack和fly方法。
- 那么问题来了,因为鸭子的行为在子类里不断地变化,并且让所有的子类都有这些行为是不恰当的,这就衍生了第一个设计原则:找出应用中可能需要变化之处,把它们独立出来,不和那些不需要变化的部分混合在一起。为了分开变化和不变化的部分,需要准备两组类,让它们完全远离Duck类,一个是fly相关的,一个是quack相关的,每一组类将实现各自的动作。而我们知道Duck类的fly和quack会随着鸭子的不同而变化,所以把它们抽取出来,建立一组新类来代表每个行为。我们希望设计有弹性,因为从一开始,鸭子的行为没有弹性,才让我们走上了这条不归路,我们希望能够指定行为到鸭子的实例,好比我们产生了一个绿头鸭,并指定特定的类型的飞行行为给它,因为这样,我们就可以在运行时动态地改变绿头鸭的飞行行为。所以衍生了第二个设计原则:针对接口编程,不针对实现编程。针对接口编程的真正意思是针对超类型编程。
这里是简单针对接口编程和针对实现编程的例子:
public abstract class Animal { protected abstract void makeSound(); } class Dog extends Animal { @Override protected void makeSound() { bark(); } void bark() { System.out.println("汪汪叫"); } } class Cat extends Animal { @Override protected void makeSound() { meow(); } void meow() { System.out.println("喵喵叫"); } }
这里是测试类:
class MainTest { public static void main(String[] args) { // 针对实现编程 Dog dog = new Dog(); dog.bark(); // 针对接口编程 Animal animal = new Cat(); animal.makeSound(); } }
- 现在我们实现鸭子的行为,类图如下:
- 现在我们整合鸭子的行为,Duck变成了这样:
- 现在整合代码
package src.designpattern.strategy.ex1; /** * 鸭子超类 * * @author wusj * @date 2015-08-07 */ public abstract class Duck { FlyBehaviour flyBehaviour; QuackBehaviour quackBehaviour; abstract void display(); /** * 所有的鸭子都会有用,漂浮起来,包括诱饵鸭 */ public void swim() { System.out.println("All ducks float, even decoys."); } public void performQuack() { quackBehaviour.quack(); } public void performFly() { flyBehaviour.fly(); } }
package src.designpattern.strategy.ex1; /** * 用翅膀飞 * * @author wusj * @date 2015-08-07 */ public class FlyWithWings implements FlyBehaviour { public void fly() { System.out.println("I'm flying."); } }
package src.designpattern.strategy.ex1; /** * 不会飞 * * @author wusj * @date 2015-08-07 */ public class FlyNoWay implements FlyBehaviour { public void fly() { System.out.println("I can't fly."); } }
package src.designpattern.strategy.ex1; /** * 嘎嘎叫 * * @author wusj * @date 2015-08-07 */ public class Quack implements QuackBehaviour { public void quack() { System.out.println("Quack."); } }
package src.designpattern.strategy.ex1; /** * 不会叫 * * @author wusj * @date 2015-08-07 */ public class MuteQuack implements QuackBehaviour { public void quack() { System.out.println("Silence."); } }
package src.designpattern.strategy.ex1; /** * 绿头鸭 * @author wusj * @date 2015-08-07 */ public class MallarDuck extends Duck { /** * 绿头鸭会呱呱叫,会飞 */ public MallarDuck() { quackBehaviour = new Quack(); flyBehaviour = new FlyWithWings(); } @Override void display() { System.out.println("I'm a real Mallard duck."); } }
测试类代码
class MiniDuckSimulator { public static void main(String[] args) { Duck mallard = new MallarDuck(); mallard.performFly(); mallard.performQuack(); } } // 测试结果 // I'm flying. // Quack.
其他代码暂略。
策略模式:定义了算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
关于运用实例,请看java设计模式之策略模式应用:订单手续费计算