以Head First设计模式中第一章给出的鸭子例子来讲,此例子中有个鸭子的类,一共有普通鸭、模型鸭和玩具鸭三种。区别是普通鸭可以飞行并发出嘎嘎的叫声;模型鸭不能飞行且不能发声;玩具鸭不能飞行,但可以依靠火箭推进飞行(^..^就是这么吊)且发出吱吱的声音。面对这些有类似的属性但不同的行为的类来说,使用策略模式便有利于类的拓展,其原则是:多用组合,少用继承。简单的思路是:定义算法族,分别封装起来,让它们相互之间可以替换,使算法的变化独立于使用算法的客户。以下就从实例来说明:
首先是鸭子类,那就创建一个鸭子的超类(Duck),鸭子有飞行和鸣叫的行为,所以其下有两个方法用于执行其飞行行为(performFly)和鸣叫行为(performQuack),当然可以添加一些其他方法(display)。
Duck类
/**
* 鸭子的超类
*/
public abstract class Duck {
public Duck() {
// 执行鸭子的特征
display();
}
//鸭子的外观,所有的鸭子外观都不一样,需要实例化的时候自定这只鸭子是长什么样的
public abstract void display();
//执行鸭子的飞行行为
public void performFly(){
}
//执行鸭子的鸣叫行为
public void performFly(){
}
}
可能此处会想到直接创建三种鸭子子类来继承Duck超类,然后重写performFly()和performFly()方法。如果这么做的话要是再出现一种鸭子可以飞行但是发出吱吱的叫声那就会和普通鸭子的飞行行为一样,这样就需要重写写一遍飞行的代码而没有起到最初的代码复用的情况。所以此处考虑到一个设计原则,就是找出应用中可能需要变化的地方把它独立出来,因此采用接口的方式设计飞行行为接口(FlyBehavior)和鸣叫接口(QuackBehavior)
/**
* 鸭子的飞行行为接口,所有的飞行行为类需实现这个接口
*/
public interface FlyBehavior {
//实现并赋予飞行行为
public void fly();
}
/**
* 同样鸭子具有叫的行为
*/
public interface QuackBehavior {
//实现并赋予鸣叫行为
public void quack();
}
既然有了飞行和鸣叫的接口,那就需要实现其具体的行为,因此再创建所有的行为类实现它们,当然可以添加其他的飞行行为和鸣叫行为
可飞行的行为类:
/**
* 可飞行
*/
public class Flyable implements FlyBehavior{
@Override
public void fly() {
// 实现飞行行为
System.out.println("我飞起来了!");
}
}
不可飞行行为类:
/**
* 无法飞行的行为
*/
public class FlyNoWay implements FlyBehavior{
@Override
public void fly() {
// 这只鸭子不能飞行,可能是模型鸭
System.out.println("我飞不起来。");
}
}
靠火箭飞行行为类:
/**
* 依靠火箭飞行
*/
public class FlyRocket implements FlyBehavior{
@Override
public void fly() {
// 依靠火箭推进飞行的鸭子
System.out.println("我可以靠火箭飞行了");
}
}
当然还有叫声的行为
发出嘎嘎叫声:
/**
* 鸭子嘎嘎的鸣叫声
*/
public class Gaga implements QuackBehavior{
@Override
public void quack() {
// 鸭子的叫声是嘎嘎的叫
System.out.println("我会嘎嘎地叫!!");
}
}
发出吱吱叫声
/**
* 鸭子吱吱地叫
*/
public class Zhizhi implements QuackBehavior{
@Override
public void quack() {
// 鸭子鸣叫声为吱吱地叫
System.out.println("我会吱吱地叫!!");
}
}
叫不出声
/**
* 叫不出声的鸭子(木头鸭)
*/
public class Silence implements QuackBehavior{
@Override
public void quack() {
// 有些鸭子叫不出声
System.out.println("我叫不出声!");
}
}
既然飞行行为和鸣叫的行为都有了,现在可以整合鸭子的超类(Duck)了。最初的Duck只有方法而没有具体实现,现在可以其方法了。到此处还只是声明鸭子的飞行行为和鸣叫行为,具体是什么得需要看子类是什么类型的鸭子了。
/**
* 鸭子的超类
*/
public abstract class Duck {
public Duck() {
// 执行鸭子的特征
display();
}
//鸭子具有的飞行行为
FlyBehavior flyBehavior;
//鸭子的鸣叫行为
QuackBehavior quackBehavior;
//鸭子的外观,所有的鸭子外观都不一样,需要实例化的时候自定这只鸭子是长什么样的
public abstract void display();
//执行鸭子的飞行行为
public void performFly(){
flyBehavior.fly();
}
//执行鸭子的鸣叫行为
public void performQuack(){
quackBehavior.quack();
}
}
现在是时候创建三种鸭子了,创建之时初始化其行为便可。
正常鸭(NormalDuck)
/**
* 创建正常的鸭子,继承超类,执行飞行和鸣叫的行为,此类鸭子可以飞,并且发出嘎嘎的叫声
*/
public class NormalDuck extends Duck{
public NormalDuck() {
// 普通鸭构造函数
flyBehavior = new Flyable();
quackBehavior = new Gaga();
}
@Override
public void display() {
// 此类鸭子外观都一致
System.out.println("这是一只小黄鸭!!");
}
}
玩具鸭(ToyDuck)
/**
* 玩具鸭,不会飞行,但会吱吱地叫
*/
public class ToyDuck extends Duck{
public ToyDuck() {
// 玩具鸭构造函数
flyBehavior = new FlyNoWay();
quackBehavior = new Zhizhi();
}
@Override
public void display() {
// 玩具鸭子
System.out.println("这是一只玩具鸭!!");
}
}
模型鸭(ModelDuck)
/**
* 模型鸭,不会飞也不会叫
*/
public class ModelDuck extends Duck{
public ModelDuck() {
// 模型鸭构造函数
flyBehavior = new FlyNoWay();
quackBehavior = new Silence();
}
@Override
public void display() {
// 模型鸭
System.out.println("这是一只模型鸭!!");
}
}
上面说过玩具鸭是可以飞行的,那为什么不直接将其飞行行为设为靠火箭推进飞行呢?这里就可以再次整合一下Duck类,使其可以动态设定行为,通过setFlyBehavior(new FlyRocket())动态设置其飞行行为,此方法也可用于设置鸣叫行为。
/**
* 鸭子的超类
*/
public abstract class Duck {
public Duck() {
// 执行鸭子的特征
display();
}
//鸭子具有的飞行行为
FlyBehavior flyBehavior;
//鸭子的鸣叫行为
QuackBehavior quackBehavior;
//鸭子的外观,所有的鸭子外观都不一样,需要实例化的时候自定这只鸭子是长什么样的
public abstract void display();
//执行鸭子的飞行行为
public void performFly(){
flyBehavior.fly();
}
//执行鸭子的鸣叫行为
public void performQuack(){
quackBehavior.quack();
}
//设置鸭子飞行的属性
public void setFlyBehavior(FlyBehavior fb){
flyBehavior = fb;
}
//设置鸭子的鸣叫属性
public void setQuackBehavior(QuackBehavior qb){
quackBehavior = qb;
}
}
至此所有的工作都已经做完,现在开始测试
/**
* 创建鸭子
* 先创建鸭子的超类和具有的各种行为,然后创建不同类型的鸭子,最后实例化这些鸭子
*/
public class CreateDuck {
public static void main(String[] args) {
//创建普通鸭子,此类鸭子可以飞行,并发出嘎嘎的叫声
Duck normalDuck = new NormalDuck();
normalDuck.performFly();
normalDuck.performQuack();
//创建模型鸭,此类鸭子不能飞行,并且不能发出声音
Duck modelDuck = new ModelDuck();
modelDuck.performFly();
modelDuck.performQuack();
//创建玩具鸭,此类鸭子不能飞行,但依靠火箭推进飞行,并且会发出吱吱的声音
Duck toyDuck = new ToyDuck();
toyDuck.performFly(); //执行玩具鸭的基本属性(不会飞行)
toyDuck.setFlyBehavior(new FlyRocket());//赋予玩具鸭行的飞行属性(火箭推进),至此覆盖了之前的
toyDuck.performFly(); //再次执行时玩具鸭已经拥有火箭推进了,可以飞行了。
toyDuck.performQuack();
}
}
运行结果:
在这里看到实现鸭子的各种行为可以使用策略模式将鸭子的行为封装到一组类中,这样可以很容易拓展和改变,并且在需要之时还能在运行时改变其行为,如将不会飞行的玩具设置成靠火箭推进飞行。