设计模式之于程序员,是一种诱惑,不管你信不信,反正我信。不懂则必不会用,学习设计模式,在于在某个时候,需要实现某些功能,你会想到,哦,历经千山万水,原来你也在这里。网上资料云集,看一个例子看不懂,就多看几个,自然知其然且知其所以然。
这里我会记录自己学习策略模式的例子,这个例子来自于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();
}
}
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.
其他代码暂略。
策略模式:定义了算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
关于运用实例,请看设计模式之策略模式(二)