什么是策略设计模式?
策略设计模式定义了算法族(行为族),分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
为什么要用策略设计模式?
试想这样一种业务场景。
公司设计了一款与鸭子有关的游戏,游戏中有各种各样的鸭子在水里嬉戏。
现在游戏里一共有两种鸭子,分别是红头鸭与绿头鸭,且具备喊叫与游泳这两种行为。
![duck.png-21.3kB](http://static.zybuluo.com/mikumikulch/ndvvr3zivx6ncokiryj18pzv/duck.png)
每种鸭子的外观都不同,所以 display() 是抽象方法。
/**
* 策略设计模式
* 鸭子超类
*/
public abstract class Duck {
public void quack() {
System.out.println("鸭子叫");
}
public void swim() {
System.out.println("鸭子游泳");
}
public abstract void display();
}
/**
* 绿头鸭
*/
public class MallardDuck extends Duck {
@Override
public void display() {
System.out.println("绿头鸭");
}
}
/**
* 红头鸭
*/
public class RedHeadDuck extends Duck {
@Override
public void display() {
System.out.println("红头鸭");
}
}
随着市场竞争变得越来越大,公司高层经过讨论决定需要给鸭子加上一个会飞的功能。这个需求太简单了,我只要一分钟就可以解决。
/**
* 策略设计模式
* 鸭子超类
*/
public abstract class Duck {
public void quack() {
System.out.println("鸭子叫");
}
public void swim() {
System.out.println("游泳");
}
public void fly() {
System.out.println("飞翔")
}
public abstract void display();
}
新功能上线后的第二天,经理通知我咱们遇到了大麻烦。
原来公司游戏里除了红头、绿头鸭以外还有一种橡皮鸭。现在橡皮鸭在天空中飞来飞去。这该如何是好呢?
我毕竟是一个面向对象程序员,立刻就想到了使用 override 来解决这个问题。
/**
* 橡皮鸭
*/
public class RubberDuck extends Duck {
@Override
public void quack() {
//do nothing
}
@Override
public void fly() {
//do nothing
}
@Override
public void display() {
System.out.println("橡皮鸭");
}
}
问题解决了。
可是,若是将来游戏里出现了诱饵鸭、机械鸭等等其他鸭子,每种鸭子的行为都有所不同,难道每次加入新的鸭子都去检查并覆盖duck中的方法吗?
看来使用继承来为所有的鸭子提供行为会造成很多麻烦,于是我想到了尝试用接口来解决这个问题。
![屏幕快照 2016-11-06 下午2.40.27.png-41.5kB](http://static.zybuluo.com/mikumikulch/6qh59l7jyhttsn1vr4oirg97/%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202016-11-06%20%E4%B8%8B%E5%8D%882.40.27.png)
public abstract class Duck {
// public void quack() {
// System.out.println("鸭子叫");
// }
public void swim() {
System.out.println("鸭子游泳");
}
// public void fly() {
// System.out.println("飞行");
// }
public abstract void display();
}
/**
* 会飞的接口
*/
public interface Flyable {
void fly();
}
/**
* 会叫的接口
*
*/
public interface Quackable {
void quack();
}
/**
* 绿头鸭
*/
public class MallardDuck extends Duck implements Flyable, Quackable {
@Override
public void fly() {
System.out.println("飞行");
}
@Override
public void quack() {
System.out.println("鸭子叫");
}
@Override
public void display() {
System.out.println("绿头鸭");
}
}
/**
* 橡皮鸭
*/
public class RubberDuck extends Duck {
@Override
public void display() {
System.out.println("橡皮鸭");
}
}
将容易产生变化的行为独立抽象成为接口,在需要这些行为的子类对象中分别实现不同的具体行为,这样就避免了每个子类都需要去检查并覆盖父类方法的工作!
然而,经理对这样的做法产生了疑问。由于将变化的行为声明为接口后,需要该行为的子类对象都需要去实现接口中的方法。
若有100支鸭子岂不是要实现100次行为?这无疑加大了工作量甚至会造成项目延期交付!该如何是好呢?
策略设计模式怎么用?
幸运的是,有一个设计原则刚好适用于此种情况:
找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
现在,根据此原则来重新设计这个鸭子游戏。
首先将容易产生变化的部分(quack 与 fly)抽取出来,重构为两组实现了 quackable 与 flyable 的类。(别误会,我不是让你使用多重继承)
接着想办法让鸭子把容易变化的 fly 与 quack委托(delegate)给别人处理。
最后为了提供更强的灵活性,我们需要在运行中也能给鸭子对象灵活的设置不同的行为模式。比如增加一个火箭发射器使橡皮鸭也能飞!
具体应该怎么样实现这样的设计呢?另一个设计原则发挥作用的时候到了
多用组合,少用继承。
![Duck4.png-61kB](http://static.zybuluo.com/mikumikulch/o56vg1gayivbj66zxrjpjd0v/Duck4.png)
抽象变化的部分,将变化的部分独立出来封装成单独的模块。
/**
* 飞行的行为接口
*/
public interface Flyable {
void fly();
}
/**
* 飞行行为类
*/
public class Fly implements Flyable {
@Override
public void fly() {
System.out.println("飞翔");
}
}
/**
* 不能飞行为类
*/
public class FlyNoWay implements Flyable {
@Override
public void fly() {
System.out.println("不能飞");
}
}
/**
* 火箭飞行行为类
*/
public class FlyWithRockets implements Flyable {
@Override
public void fly() {
System.out.println("火箭飞行");
}
}
/**
* 呱呱叫行为接口
*
*/
public interface Quackable {
void quack();
}
/**
* 沉默行为类
*/
public class MuteQuack implements Quackable {
@Override
public void quack() {
System.out.println("无法呱呱叫");
}
}
/**
* 呱呱叫行为类
*/
public class Quack implements Quackable {
@Override
public void quack() {
System.out.println("呱呱叫");
}
}
想办法让鸭子把容易变化的 fly 与 quack 委托(delegate)给别人处理。
/**
* 策略设计模式
* 鸭子超类
*/
public abstract class Duck {
public Flyable flyable;
public Quackable quackable;
public void swim() {
System.out.println("鸭子游泳");
}
public abstract void display();
public void performFly() {
flyable.fly();
}
public void performQuack() {
quackable.quack();
}
public void setQuackable(Quackable quackable) {
this.quackable = quackable;
}
public void setFlyable(Flyable flyable) {
this.flyable = flyable;
}
}
为了提供更强的灵活性,我们需要在运行中也能给鸭子对象灵活的设置不同的行为模式。比如增加一个火箭发射器使橡皮鸭也能飞!
/**
* 绿头鸭
*/
public class MallardDuck extends Duck {
public MallardDuck(Flyable flyable, Quackable quackable) {
this.flyable = flyable;
this.quackable = quackable;
}
@Override
public void display() {
System.out.println("绿头鸭");
}
}
/**
* 红头鸭
*/
public class RedHeadDuck extends Duck {
public RedHeadDuck(Flyable flyable, Quackable quackable) {
this.flyable = flyable;
this.quackable = quackable;
}
@Override
public void display() {
System.out.println("红头鸭");
}
}
/**
* 橡皮鸭
*/
public class RubberDuck extends Duck {
public RubberDuck(Flyable flyable, Quackable quackable) {
this.flyable = flyable;
this.quackable = quackable;
}
@Override
public void display() {
System.out.println("橡皮鸭");
}
}
测试
/**
* 策略设计模式测试类
*/
public class DuckGameTester {
public static void main(String args[]) {
// 创建行为类
Flyable flyNoWay = new FlyNoWay();
Flyable flyWithRockets = new FlyWithRockets();
Flyable fly = new Fly();
Quackable muteQuack = new MuteQuack();
Quackable quack = new Quack();
// 创建各种类型的鸭子对象
MallardDuck mallardDuck = new MallardDuck(fly, quack);
mallardDuck.performFly();
mallardDuck.performQuack();
mallardDuck.setQuackable(muteQuack);
mallardDuck.performQuack();
// 创建橡皮鸭对象
RubberDuck rubberDuck = new RubberDuck(flyNoWay, muteQuack);
rubberDuck.performFly();
rubberDuck.performQuack();
rubberDuck.setFlyable(flyWithRockets);
rubberDuck.performFly();
// 继续创建其他新鸭子对象,只需要将行为委托给对应的行为对象就可以
}
}
通过合理运用前文所描述的2个设计原则,我们将变化部分的代码抽取封装与稳定部分独立开来,接着通过委托的方式使目标对象将变化的功能,委托给了抽取出来的行为(算法族)对象。并且在运行中我们还能随意的改变对象的行为。
这种通过组合的方式建立的系统具有很大的弹性,这就是策略设计模式的运用方式。
网友评论