策略模式 (Strategy Pattern)
定义:定义了算法簇,分别封装起来,让它们之间可以相互替换,此模式让算法的变法独立于使用算法的客户。
使用《Head First Design Pattern》 书中Duck的例子分析下上述定义
Duck(abstract class) – 使用算法(飞行FlyBehavior,鸣叫QuackBehavior)
public interfaceFlyBehavior {
void fly();
}
public interfaceQuackBehavior {
void quack();
}
基于这两个接口定义一组类,构成了算法簇,飞行方式、鸣叫方式都分别有很多种。
public classFlyWithWing implements FlyBehavior {
@Override
public void fly() {
System.out.println("I fly with the wing.");
}
}
这就是算法簇中的一个算法并封装(类FlyWithWing封装了Fly算法簇的一种算法)起来,定义了一种飞行方式Fly with Wing 用翅膀飞。
既然是算法簇,当然还以可以有其它的算法。例:
public classFlyNoWay implementsFlyBehavior {
@Override
public void fly() {
System.out.println("I can't fly.");
}
}
FlyNoWay也是Fly算法簇的一个算法。
/*
* Duck 鸭子类,FlyBehavior算法簇的客户
* FlyBehavior簇中添加和删除算法(例如:FlyNoWay、FlyWithWing...)
* 都对Duck类无影响,无需修改客户Duck代码
* CauZ Duck 使用 has-a(组合FlyBehavior)而不是 is-a
*/
public abstractclassDuck {
private FlyBehavior flyBehavior;
public voidsetFlyBehavior(FlyBehavior flyBehavior){
this.flyBehavior=flyBehavior;
}
//other code here
}
//测试方法
public static void main(String...args){
FlyBehaviorflyWithWing = newFlyWithWing();
DuckredHeadDuck = newRedHeadDuck();
redHeadDuck.setFlyBehavior(flyWithWing);
redHeadDuck.display();
redHeadDuck.performFly();
System.out.println("-----------------------------");
//切换redHeadDuck飞行行为
redHeadDuck.setFlyBehavior(flyNoWay);
redHeadDuck.display();
redHeadDuck.performFly();
}
我们可以通过setFlyBehavior在运行时设定飞行行为,可以是来着Fly算法簇中的任意一个算法(都实现了FlyBehavior接口)。
duck.setFlyBehavior(flyBehaviorMagic);//实现了算法切换
但是我们的客户Duck不知晓(代码层次)它所关联的具体Fly行为是什么,算法的切换亦是不会影响到客户Duck。
没有使用策略模式的Duck
/**
* 这是所有鸭子的父类,其中包含了鸭子的一些基本行为
* 如:display、quack、fly
*/
public abstract class Duck {
/**
* 抽象的display方法,子类分别提供自己的实现。
*/
public abstract void display();
public void quack(){
//假设大多鸭子都zhizhi的叫,这是为什么没有设置为抽象方法的原因
System.out.println("zhizhi");
}
public void fly(){
System.out.println("I am flying");
}
}
/*
* 分析一:以上基本行为几乎是所有鸭子都具有的,所以抽象成鸭子超类 Duck。
* 这样Concrete类就可以继承Duck,实现不同的鸭子
* 问题: 但是,对于每个行为不同的鸭子可能不一样,叫声不同、飞行方式不同。
* 基于以上问题:为什么不把quack和fly设置为抽象呢?不然Concrete类就可能继承到错误的行为(如果不覆盖super类方法)。
* 抽象方法和接口是一种设计行为,不利于代码重用,对于Duck来说这样的设计除了应用多态这一点就一无是处了。没有体现出继承的优点。
* 继承无法排除特例,例如塑料鸭子不会飞,但是却错误的继承了fly方法,这是必须在塑料鸭子PlasticDuck中覆盖fly方法,明确什么事情都不做。
*
*
* 分析二:关于扩展升级
* Duck 添加新的行为,比如:现在加入layEggs()//产蛋行为
* 我们就要在Duck加入 layEggs方法,但是公鸭子很塑料鸭子不会啊,这不是为难鸭子么?
* 加入这个方法,就必须手动覆盖掉其他N多非活生生母鸭子的layEggs行为,天知道这样会不会把手指累折了。
*/
/*
* 设计模式:无非就是减少依赖、封装变化、提高重用性,最终开发人员得益于开发效率提高,程序保证了健壮性。
* 策略模式侧重对变化的封装。
*/