设计模式之策略模式
一、模式介绍
1.1 模式意图
定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独 立于使用它的客户而变化。
1.2 动机
有许多算法可对一个正文流进行分行。将这些算法硬编进使用它们的类中是不可取的,原因:
- 需要换行功能的客户程序如果直接包含换行算法代码的话将会变得复杂,这使得客户程 序庞大并且难以维护, 尤其当其需要支持多种换行算法时问题会更加严重。
- 不同的时候需要不同的算法,我们不想支持我们并不使用的换行算法。
- 当换行功能是客户程序的一个难以分割的成分时 ,增加新的换行算法或改变现有算法将 十分困难。
我们可以定义一些类来封装不同的换行算法,从而避免这些问题。一个以这种方法封装的算法称为一个策略。
1.3 在什么情况下使用策略模式
- 许多相关的类仅仅是行为有异。“策略”提供了一种用多个行为中的一个行为来配置一 个类的方法。
- 需要使用一个算法的不同变体。
- 算法使用客户不应该知道的数据。可使用策略模式以避免暴露复杂的、与算法相关的数据结构。
- 一个类定义了多种行为 , 并且这些行为在这个类的操作中以多个条件语句的形式出现。 将相关的条件分支移入它们各自的策略类中以代替这些条件语句。
二、案例演示
现在客户要咱做一个模拟鸭子的游戏:FunnyDuck。 游戏中会出现很多鸭子,一边游泳戏水,一边呱呱叫。
你一开始的设计可能是这样的:
所有的鸭子都会游泳,也都会呱呱叫,所以由超类来负责处理这部分代码。每一种鸭子的外观不一样,所以display()方法是抽象的,由具体的鸭子子类来实现。
一切都很顺利,游戏成功上线。。。
但是不久,很多的类似游戏都出来啦,主管们决定,此模拟器要加上会飞的鸭子来将竞争者抛开。
这不是so easy的是吗,于是你满怀信心的向主管表示,一周内上线。
你心想:我只需要在Duck 类中加入 fly() 方法,然后所有的鸭子都会继承 fly() , 是时候展示我的OO才华了。
可怕的问题发生了,你会发现,橡皮鸭居然在天上飞!
我们忽略了一个问题:并非每个鸭子都会飞(橡皮鸭)。在超类中增加行为直接影响到了所有的子类。
怎么办? 针对每一个鸭子的实现类,做特殊处理,比如,橡皮鸭fly方法覆盖超类的,什么也不做。。
显然这不是一个好的办法,因为子类可能有很多个,也有可能有的鸭子不会叫。
利用接口如何? 可以吧fly() 接口提取出来,放到 Flyable 接口中,只有会飞的鸭子才实现此接口。
但是这样一来会产生很多的重复代码,比如你48个鸭子的飞行行为需要改变一下。。
现在我们知道继承并不能很好的解决问题了,还记得我们之前学习的设计模式的原则吗?
组合优先于继承。
封装变化,把那有可能变化的部分独立出来。
针对这个场景,我们可以把咕咕叫的行为 和 飞行的行为 抽离出来。来看看修改后的类图吧:
代码示例:
/**
* 飞行行为接口
*/
public interface FlyBehavior {
void fly();
}
public class FlyNoWay implements FlyBehavior {
public void fly() {
System.out.println("I can't fly!");
}
}
public class FlyWithWinds implements FlyBehavior {
public void fly() {
System.out.println("I'm flying!");
}
}
/**
* 呱呱叫行为接口
*/
public interface QuackBehavior {
void quack();
}
public class Quack implements QuackBehavior {
public void quack() {
System.out.println("Quack");
}
}
public class Squeak implements QuackBehavior {
public void quack() {
System.out.println("Squeak");
}
}
public class MuteQuack implements QuackBehavior {
public void quack() {
System.out.println("< Silence >");
}
}
/**
* 抽象的鸭子类,组合了 FlyBehavior 和 QuackBehavior,由这两个行为接口来完成对应的行为
*/
public abstract class Duck {
public FlyBehavior flyBehavior;
public QuackBehavior quackBehavior;
public Duck() {
}
public abstract void display();
public void swim(){
System.out.println("All ducks swim, even decoys!");
}
public void performFly(){
flyBehavior.fly();
}
public void performQuack(){
quackBehavior.quack();
}
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
}
// 具体的鸭子
public class ModeDuck extends Duck {
public ModeDuck() {
flyBehavior = new FlyNoWay(); // 一开始,模型鸭不会飞
quackBehavior = new Quack();
}
public void display() {
System.out.println("I'm a mode Duck");
}
}
// 客户端调用
public class Main {
public static void main(String[] args) {
Duck modeDuck = new ModeDuck();
modeDuck.display();
modeDuck.performFly();
modeDuck.setFlyBehavior(new FlyWithWinds());
modeDuck.performFly();
}
}
如果要增加新的飞行行为怎么办?