第二篇:策略模式

来源:互联网 发布:华为笔试java 编辑:程序博客网 时间:2024/06/03 19:56

场景:你的公司最近要做一个飞机模型展览项目,该项目可以向客户展示各种各样的飞机,项目第一期可以确定的是这些飞机都有不同的外观,都可以发出声波,都可以飞行;好了,现在我们就知道这些,让我们开干吧!
(你该不会去纠结模型展览的飞机为什么会发声波还会飞行吧?...)
首先,用OO思维,我们可以先总结出一个父类:

/**所有飞机模型的父类*/public abstract class Flyable {/**发声波...*/protected void voice(){System.out.println("嗡...嗡...嗡...");}protected void fly(){System.out.println("我先直线加速...");System.out.println("我再拉起操纵杆...");System.out.println("然后...飞呀飞呀 我的骄傲放纵...");}/**子类必须自己去绘制自己的外观*/protected abstract void facade();}

接下来让我们创造两架飞机:

/**一架喷气机*/class GasPlane extends Flyable{@Overrideprotected void facade() {System.out.println("我是一家绿绿的喷气机");}}

/**一架客运机*/class GuestPlane extends Flyable{@Overrideprotected void facade() {System.out.println("白白的长长的...");}}

好啦,现在看起来一切都那么的完美,两架飞机只需要简单的绘制自己的外观,至于飞行和声波的功能,父类都给我们啦!瞧,代码多省事儿!

过了一个礼拜...新的需求还需要加入一架直升机,你马上开始写代码!

/**一架直升机*/class CopterPlane extends Flyable{@Overrideprotected void facade() {System.out.println("很炫的迷彩...");}/**注意,这里必须覆盖父类的飞行方法,因为直升机是螺旋桨飞行,不是跑道提速...*/@Overrideprotected void fly() {System.out.println("我先启动引擎");System.out.println("然后我再让我的小翅膀转呀转...");System.out.println("飞呀飞呀...");}}


好吧,这里覆盖了父类的方法,但看起来好像也没事儿...但是,我们要记住一点,当你的大多数子类都要去完全覆盖父类的方法时,你就要警觉了,你的设计很可能有问题!比较父类的方法它更多的是要达到统一复用的目的!


又过了几天...需求还要加入一个木头做的模型飞机!”

...刷刷刷...代码写好了...”

class WoodPlane extends Flyable{@Overrideprotected void facade() {System.out.println("红色的木...");}}

这个代码有问题吗??当然有!木头做的模型机是不能飞的!但是它通过继承了Flyable,却拥有了飞行的能力!OK,那赶紧改代码吧!

class WoodPlane extends Flyable{@Overrideprotected void facade() {System.out.println("红色的木...");}/**注意:这里覆盖父类的飞行方法,并且什么都不做,因为木头模型飞机不能飞! * 我们不去关注木头飞机不能发出声波这个问题,如果关注这个点,后面我将要写更多的类...*/@Overrideprotected void fly() {}}

好了,是时候说点什么了!你看出这个设计有什么问题了吗???没有的话我来给你讲讲吧!

首先,父类拥有飞行方法,所有的子类理论上都应该能飞行,但是,我们看到,直升机完全覆盖父类的方法,虽然在使用这个类的客户端看来它应该是像父类所描述的那样,在跑道加速,然后拉起操纵杆,然后起飞...但它实际上却完全不是这样走的。然后,我们的WoodPlane模型飞机看起来是能够飞的,但它实际上却直接覆盖父类方法,从而使自己失去飞行能力,这在使用这个类的人来是很诡异的;

好吧,那我们怎么来改进这个程序的设计呢?要不,既然飞行方法会随着子类的变化而一直变,有的会飞有的还不会飞,那我们就设计一个接口,如果会飞的就实现这个接口,否则就不实现,这样对客户端来说不就一目了然了吗?

注:接口就是用来规划和描述一个对象应该会具有怎样的能力!面向对象设计原则,依赖抽象(接口或抽象类),不依赖具体实现;

如果用接口设计的话,又有什么问题呢?好吧,首先,我们得把父类Flyable的fly()方法去掉,这样所有子类都失去了飞行的能力,然后让我们的“喷气机”,“客运机”,“直升机” 分别去实现我们的接口,而“木头模型机”因为不具备飞行的能力,所以不去实现该接口;但是!问题来了,我们的喷气机辉和客运机它们的飞行方法都是一致的...或者说后期我们还要加入各种各样的飞机,它们的飞行方法都是一致的,但是呢!他们却都要去实现飞行方法,造成我们的代码不能复用!多处复制黏贴!!!

那我们有什么办法既能将飞行方法分离出来,又能让代码复用呢???当当当当!策略模式闪亮登场!

策略模式核心:将会变化的部分抽取出来,这些部分我们可以看成是一个行为,比如飞行就是一个行为 ,抽取出来后形成一个个的策略对象,所有需要的能力都委托给策略对象去执行!

在这里,我们会变得地方是fly()飞行方法,那么OK,进行代码改造吧!

首先,增加一个策略接口,它表示具有飞行这个行为,再造几个拥有不同飞行方式的策略类;

/**飞行行为接口*/interface FlyBehavioer{void fly();}/**拥有直线跑道加速...这种飞行能力的类*/class LineFlyBehavioer implements FlyBehavioer{@Overridepublic void fly() {System.out.println("我先直线加速....");System.out.println("我再拉起操纵杆...");System.out.println("然后...飞呀飞呀  我的骄傲放纵...");}}/**拥有螺旋桨盘旋飞行...这种飞行能力的类*/class CircleFlyBehavioer implements FlyBehavioer{@Overridepublic void fly() {System.out.println("我先启动引擎");System.out.println("然后我再让我的小翅膀转呀转...");System.out.println("飞呀飞呀...");}}/**不会飞的...所以是一个空实现,什么都不做*/class NonFlyBehavioer implements FlyBehavioer{@Overridepublic void fly() {}}

然后,改造我们的Flyable父类

public abstract class Flyable {//依赖于抽象protected FlyBehavioer flyBehavioer;//默认是不具备飞行能力的public Flyable() {this(new NonFlyBehavioer());}public Flyable( FlyBehavioer flyBehavioer ) {this.flyBehavioer = flyBehavioer;}protected void voice(){System.out.println("嗡...嗡...嗡...");}/**注意,这里委托给策略类去进行具体的飞行实现*/protected void toFly(){flyBehavioer.fly();}protected abstract void facade();/**可以在运行时动态改变飞行行为*/public void setFlyBehavioer( FlyBehavioer flyBehavioer ){this.flyBehavioer = flyBehavioer;}}

然后对我们的“喷气机”,“客运机”,"直升机","木头模型机" 进行改造:

/**一架喷气机*/class GasPlane extends Flyable{public GasPlane() {//初始化具备直线飞行能力super( new LineFlyBehavioer() );}@Overrideprotected void facade() {System.out.println("我是一家绿绿的喷气机");}}/**一架客运机*/class GuestPlane extends Flyable{public GuestPlane() {//初始化具备直线飞行能力super( new LineFlyBehavioer() );}@Overrideprotected void facade() {System.out.println("白白的长长的...");}}/**一架直升机*/class CopterPlane extends Flyable{public CopterPlane() {//初始化具备盘旋飞行能力super( new CircleFlyBehavioer() );}@Overrideprotected void facade() {System.out.println("很炫的迷彩...");}}//一架木头模型机class WoodPlane extends Flyable{@Overrideprotected void facade() {System.out.println("红色的木...");}}


终于到了测试我们代码的时候了!

public static void main(String[] args) {GasPlane gp = new GasPlane();gp.toFly();System.out.println("****************************");GuestPlane gtp = new GuestPlane();gtp.toFly();System.out.println("****************************");CopterPlane cp = new CopterPlane();cp.toFly();System.out.println("****************************");WoodPlane wp = new WoodPlane();wp.toFly();//让我们动态给木头飞机配上飞行的能力吧!System.out.println("****************************");wp.setFlyBehavioer(new CircleFlyBehavioer());wp.toFly();}

看下我们的输出结果!

我先直线加速....
我再拉起操纵杆...
然后...飞呀飞呀  我的骄傲放纵...
****************************
我先直线加速....
我再拉起操纵杆...
然后...飞呀飞呀  我的骄傲放纵...
****************************
我先启动引擎
然后我再让我的小翅膀转呀转...
飞呀飞呀...
****************************
为了看效果...我还是打印一下吧!我是飞不了的!!!
****************************
我先启动引擎
然后我再让我的小翅膀转呀转...
飞呀飞呀...



Ok,改造完成,让我们看看发生了什么变化!

我们增加了接口,增加了几个接口实现,然后所有飞机的飞行行为都委托给接口实现类去进行展示,这样有什么好处呢? 后期我们想要不同的飞行能力,只需要增加新的实现类就可以做到了!我们的”飞行“这个变化维度彻底与飞机解耦且可以进行代码复用,这对我们后期的扩展有非常大的帮助,我们想要改变飞行行为,压根就不需要去碰飞机实体了,因为你只需要执行setFlyBehavioer()就可以做到!虽然我们前期看似增加了代码量,但对于后期的维护与扩展来说,都是值得的!

最后,我们来看一下运用策略模式的类图;如果有疑问之处,给我留言喔!





2 0