java设计模式:一、策略模式

来源:互联网 发布:画logo的软件 编辑:程序博客网 时间:2024/06/05 04:44

策略模式

我的设计模式的学习,是通过《Head First设计模式》这本书来学习的。在学习的过程中,自己再总结一遍。如果在这儿有什么错误或者不同的见解,希望指出。

鸭子应用

先模拟一个应用,根据这个应用的实现方式来引出模式。
简单构思一个鸭子的应用。鸭子会叫,鸭子会游泳。不同的鸭子,长得不同。假设现在存在外观是黄色的黄头鸭和外观是红色的红头鸭。那么这个鸭子类可以定义为如下:

/** * 定义抽象类鸭子。 **/public abstract class Duck {  public abstract String display();  public void quack() {    System.out.println(display() + "呱呱叫...");  }  public void swim() {    System.out.println(display() + "游泳...");  }}/** * 黄头鸭。 **/public class YellowHeadDuck extends Duck {  @Override  public String display() {    return "黄头鸭";  }}/** * 红头鸭。 **/public class RedHeadDuck extends Duck {  @Override  public String display() {    return "红头鸭";  }}

上面的这个方式,很简单,设计也没有问题。测试一下吧。

public class TestDuck {  public static void main(String[] args) {    Duck duck = new YellowHeadDuck();    duck.quack();    duck.swim();  }}输出结果:黄头鸭呱呱叫...黄头鸭游泳...

似乎还顺利。但是随着后面需求的增加,现在又多了一种鸭子:橡皮鸭。对于多出来的这个鸭子,大多开发工程师都会想,继续继承Duck类不就可以了嘛,但是,有问题。下面演示一下:

public class RubberDuck extends Duck {  @Override  public String display() {    return "橡皮鸭";  }}

测试橡皮鸭,会输出”橡皮鸭呱呱叫”、”橡皮鸭游泳”。橡皮鸭呱呱叫是不符合逻辑的,因为橡皮鸭是模拟叫声,并非真正叫。这算是一个bug。要解决这个bug也很简单,就是让橡皮鸭来重写quack方法。

  public void quack(){    System.out.println("模拟鸭子呱呱叫...");  }

这样就搞定了。但是,有没有发现什么问题呢?现在只加了一种鸭子,比如现在又增加一种模型鸭。它只是一个模型,不会叫。继续继承Duck,然后继续覆盖quack方法。那如果继续增加鸭子呢。不同的鸭子可能叫法不同。
这个问题,暂时放一下。再增加一个新的需求。让鸭子具有飞的行为。
方法1:将飞的行为添加在抽象鸭子类中,让每一个鸭子具有飞行行为。
这种方式是不对的,因为橡皮鸭和模型鸭是不会飞的,如果这样加,必须要让这两个鸭子去覆盖飞行方法。鸭子多了,可能会忘记哪个需要覆盖,哪个不需要覆盖。
方式2:在抽象鸭类中增加抽象方法,让具体鸭子来实现飞的行为。
这种方式是可以考虑的。增加一个抽象方法,就需要让每一个继承了它的子类去实现这个方法。好的,代码如下:

/** * 抽象鸭子 **/public abstract class Duck {  public abstract void fly();  //...其他}/** * 黄头鸭 **/public class YellowHeadDuck extends Duck {  @Override  public String display() {    return "黄头鸭";  }  @Override  public void fly() {    System.out.println("黄头鸭正常的飞...");  }}/** * 红头鸭 **/public class RedHeadDuck extends Duck {  @Override  public String display() {    return "红头鸭";  }  @Override  public void fly() {    System.out.println("红头鸭正常飞...");  }}/** * 橡皮鸭 **/public class RubberDuck extends Duck {  @Override  public String display() {    return "橡皮鸭";  }  @Override  public void fly() {    //不会飞,什么也不做。  }  public void quack(){    System.out.println("模拟鸭子呱呱叫...");  }}/** * 模型鸭 **/public class ModelDuck extends Duck {  @Override  public String display() {    return "模型鸭";  }  @Override  public void fly() {    //不会飞,什么也不做。  }  public void quack(){    //不会叫,什么也不做。  }}

这样其实也有问题,橡皮鸭和模型鸭不具备飞的能力,为什么还要让他具有飞的方法呢?如果不把fly()定义在抽象类中,让每一个具有飞的行为的鸭子单独来增加fly方法,又会有两个问题会发生:
1. 2个鸭子要增加2个方法。如果不仅仅有黄头鸭,红头鸭,还有绿头鸭、褐头鸭等等各种鸭子20个,需要增加20个同样的方法。
2. 如何调用鸭子的fly方法。强制转换。举个例子:

public class BeforeFly {  public void before(Duck duck) {    System.out.println("在鸭子飞之前,做点事...");    duck.fly();    /**     * 你本想这样调用,但是很遗憾,Duck并没有这样的方法。你需要把Duck类转换为具体的类。     */    /*    if (duck instanceof YellowHeadDuck) {      (YellowHeadDuck)duck.fly();    } else if (duck instanceof RedHeadDuck) {      (RedHeadDuck)duck.fly();    }    */    //上面这样合适吗?肯定不合适。  }}

上面都是一些反例,你肯定也感受到了。如果要增加一种行为,而这种行为,每种鸭子实现方式不同,就不能把这种行为定义在公共抽象类里。所以,策略模式诞生了。

使用模式

设计原则:
找出应用中可能要变化的内容,把它们独立出来,不要和那些不变的内容混在一起。
多用组合,少用继承
这个鸭子模型中,其他都是一样的,只有fly方法和quack方法有点不同。那么,我把这两个不同的行为独立出来,重新来设计这个鸭子应用。

独立飞的行为

/** * 飞的行为接口 **/public interface FlyAction {  void fly();}/** * 正常飞 **/ public class NormalFlyAction implements FlyAction {  @Override  public void fly() {    System.out.println("正常的飞...");  }}/** * 不会飞 **/ public class NotFlyAction implements FlyAction {  @Override  public void fly() {    System.out.println("不会飞...");  }}

独立叫的行为

/** * 叫的行为 */public interface QuackAction {  void quack();}/** * 正常叫 */public class NormalQuack implements QuackAction {  @Override  public void quack() {    System.out.println("呱呱叫...");  }}/** * 模仿叫 */public class ImitateQuackAction implements QuackAction {  @Override  public void quack() {    System.out.println("模仿呱呱叫...");  }}/** * 不会叫 */public class NotQuackAction implements QuackAction {  @Override  public void quack() {    System.out.println("不会叫...");  }}

重构鸭子类

public abstract class Duck {  QuackAction quackAction;  FlyAction flyAction;  public abstract String display();  public void swim(){    System.out.println(display() + "游泳...");  }  public void quack(){    quackAction.quack();  }  public void fly(){    flyAction.fly();  }}public class YellowHeadDuck extends Duck {  public YellowHeadDuck() {    this.quackAction = new NormalQuack();    this.flyAction = new NormalFlyAction();  }  @Override  public String display() {    return "黄头鸭";  }}public class RubberDuck extends Duck {  public RubberDuck() {    this.quackAction = new ImitateQuackAction();    this.flyAction = new NotFlyAction();  }  @Override  public String display() {    return "橡皮鸭";  }}public class ModelDuck extends Duck {  public ModelDuck() {    this.quackAction = new NotQuackAction();    this.flyAction = new NotFlyAction();  }  @Override  public String display() {    return "模型鸭";  }}

然后测试:

public class TestDuck {  public static void main(String[] args) {    Duck yellowHeadDuck = new YellowHeadDuck();    Duck rubberDuck = new RubberDuck();    Duck modelDuck = new ModelDuck();    yellowHeadDuck.fly();    yellowHeadDuck.quack();    rubberDuck.fly();    rubberDuck.quack();    modelDuck.fly();    modelDuck.quack();  }}输出:黄头鸭正常的飞...黄头鸭呱呱叫...橡皮鸭不会飞...橡皮鸭模仿呱呱叫...模型鸭不会飞...模型鸭不会叫...

使用策略模式,如果不管后面再增加什么样的鸭子,都不需要再去维护飞的行为和叫的行为。即使已经存在的鸭子要改变某个行为,也只增加一个新的行为类即可,而不需要再去修改代码。
上面的代码,其实存在一个小小的问题。就是在每一个鸭子模型中去new对应的行为,这样的做法让代码高耦合了。不建议这样使用。改变方式可以参考spring的bean实现原理。即提供get/set方法。

原创粉丝点击