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方法。
- Java设计模式(一) 策略模式
- java设计模式一策略模式
- java设计模式:一、策略模式
- java设计模式(一)-策略模式
- 设计模式一 策略模式
- 设计模式(一) 策略模式
- 设计模式一:策略模式
- Java设计模式 -- 策略模式
- java设计模式-----策略模式
- java 设计模式-策略模式
- java设计模式--策略模式
- java设计模式-策略模式
- java设计模式-策略模式
- java设计模式--策略模式
- java设计模式---策略模式
- java设计模式---策略模式
- java设计模式---策略模式
- 【Java设计模式】策略模式
- 电机控制项目-重要笔记
- IP核在modelsim里面的仿真
- POJ 3126 Prime Path bfs求最短路
- 《Effective Java》读书笔记
- Glide框架
- java设计模式:一、策略模式
- android中pcm数据的播放(AudioTrack)
- Shell的种类
- linux 查看一个文件或一个文件夹大小
- openssl、x509、crt、cer、key、csr、ssl、tls 这些都是什么鬼?
- USACO——Closing the farm
- 关于ImageLoader must be init with configuration before using问题解决
- 26. Remove Duplicates from Sorted Array
- 关于Servlet的几个小问题