Java 设计模式之策略模式

来源:互联网 发布:mac哪里设置环境变量 编辑:程序博客网 时间:2024/05/22 07:59

要想写出可维护、可复用、可扩展的应用程序,就必须掌握一些设计模式的思想.设计模式重在设计,是一种思想层面的东西,需要细细的去领悟。今天学习了一下设计模式的第一个模式——策略模式,现在把它整理出来。

策略模式:定义了算法族,分别封装起来,让他们之间可以互相转换,此模式让算法的变化独立于使用算法的用户。初次去看这么一个定义,估计很多人跟我一样,直接晕掉或者崩溃掉。完全不理解,没关系,我们由一个例子来逐步分析策略模式。

这是一个关于鸭子类的游戏,在这个程序中,会有各种各样的鸭子在水里游泳,呱呱叫等。我们来看代码实现.

<span style="font-family:Courier New;font-size:14px;">/* *  鸭子的抽象类 我们定义了一个鸭子超类,他拥有鸭子共同特征的方法,比如呱呱叫,并且拥有一个抽象的方法,因为鸭子有各种各样不同的外观 */public abstract class Duck {//鸭子呱呱叫public void quack() {System.out.println("呱呱...");}//游泳行为public void swim() {System.out.println("我可以在水里游泳");}//鸭子外观的抽象方法 public abstract void display();}</span>

<span style="font-family:Courier New;font-size:14px;">/* *  外观是绿头的鸭子 继承自超类 Duck那么它也就拥有了超类中的行为,实现超类的抽象方法,以展示自己不同于超类的特征 */class MallardDuck extends Duck{@Overridepublic void display() {System.out.println("我是绿头鸭哦");}}</span>

<span style="font-family:Courier New;font-size:14px;">/* *  外观是红头的鸭子 */class ReadheadDuck extends Duck{@Overridepublic void display() {System.out.println("我是红头鸭哦");}}</span>

<span style="font-family:Courier New;font-size:14px;">public class DuckTest {public static void main(String[] args) {MallardDuck md = new MallardDuck();md.display();md.quack();md.swim();}}</span>
这个程序已经写好了,不过好像还没有用到设计模式相关的东西,是的,让我们一步一步的引入策略模式。

现在我们有一个需求,让会飞的鸭子来超越其他的竞争者。如果让我们去实现这样的功能,可能会觉得很简单啊,我们在超类Duck中,写一个飞行类的方法,这样他的子类不就都拥有了飞行行为。恩,确实是这样,但是有一个问题,并不是所有的鸭子都具有飞行行为,比如在这个游戏中会有一些木有鸭子,他们是不会飞甚至不会叫的。那么刚才的做法就会使得不会飞的鸭子也有了飞行的行为,显然是不合适的。

那么我们是不是可以利用接口去达到刚才的需求呢。我们把飞行的行为以及呱呱叫的行为从超类中抽取出来,放到Flyable和Quackable接口中,这样就可以让拥有这些行为的类去实现这些接口.我们来看代码:

<span style="font-family:Courier New;font-size:14px;">/* *  鸭子的抽象类 */public abstract class Duck {//鸭子外观的抽象方法 public abstract void display();public void swim() {System.out.println("游啊游..");}}/* *  外观是绿头的鸭子 */class MallardDuck extends Duck implements Quackable,Flyable{@Overridepublic void display() {System.out.println("我是绿头鸭哦");}@Overridepublic void fly() {System.out.println("我正在飞哦..");}@Overridepublic void quack() {System.out.println("呱呱叫..");}}/* *  木头鸭 不会飞也不会叫 外观是木头样的颜色 假定因为可以在水上漂 就相当于会游泳 */class WoodDuck extends Duck{@Overridepublic void display() {System.out.println("我的外观颜色是木般的颜色");}}/* * 飞行的接口 */interface Flyable { void fly();}/* * 呱呱叫行为的接口 */interface Quackable { void quack();}public class DuckTest {public static void main(String[] args) {MallardDuck md = new MallardDuck();md.display();md.swim();md.quack();md.fly();WoodDuck wd = new WoodDuck();wd.swim();wd.display();}</span>}

不错,这样是解决了刚才的问题,可以有个问题,那么就是代码的复用性,假设我们有100个鸭子,都有同样的飞行行为,那么这同样的代码我们要在每个子类中写一遍,相当于100遍,即便粘贴复制,也要好久吧.如果要修改的话就更麻烦了。所以从代码复用的角度来看,似乎又不太可行。设计中有一个原则就是:找出应用中可能需要变化的地方,将它们独立出来,与那些不需要变化的代码分离。

从上面的程序分析可知,需要变化的地方是飞行和呱呱叫的行为,因为不同的鸭子具有不同的飞行行为或者是否会呱呱叫。所以根据设计原则,我们应该将他们抽取出来,建立一组新的类来代表每个行为。这里可能有人会有疑问,行为怎么可以成为一个类呢?因为飞行也会有属性,比如飞行的速度,飞行的高度等。或者我们将这个行为类想象成一个技能学习中心,父类中的行为是从这里学习得来的,子类也是。但如果我们要想在鸭子子类中可以动态的去改变飞行的行为,该怎么做?有这样的一个设计原则:针对接口编程,而不是针对实现编程。所以我们用FlyBehavior和QuackBehavior接口来代表每个行为,然后行为的每个实现类都去实现其中的接口。这种做法跟以往不同,我们以往做法是行为来自Duck超类的具体实现,或是继承某个接口并由子去实现,这都是依赖于“实现”,这种做法不灵活。现在我们来看代码实现:

<span style="font-family:Courier New;font-size:14px;">/* *  鸭子的抽象类 */public abstract class Duck {//行为接口的变量QuackBehavior quackBehavior;FlyBehavior flyBehavior;//鸭子外观的抽象方法 public abstract void display();public void performFly() {//委托给行为类flyBehavior.fly();}public void performQuack() {quackBehavior.quack();}public void swim() {System.out.println("游啊游..");}}/* * 飞行的接口 */interface FlyBehavior{ public void fly();}//飞行的具体实现类class FlyWithWing implements FlyBehavior {public void fly() {System.out.println("我会飞哦");};}class FlyNoWay implements FlyBehavior {@Overridepublic void fly() {System.out.println("我不会飞啊");}}/* * 呱呱叫行为的接口 */interface QuackBehavior { public void quack();}class Quack implements QuackBehavior {@Overridepublic void quack() {System.out.println("呱呱叫");}}class MuteQuack implements QuackBehavior {@Overridepublic void quack() {System.out.println("我不会叫啊");}}/* *  外观是绿头的鸭子 */class MallardDuck extends Duck {public MallardDuck() {flyBehavior = new FlyWithWing();quackBehavior = new Quack();}@Overridepublic void display() {System.out.println("我是绿头鸭哦");}}/* *  木头鸭 不会飞也不会叫 外观是木头样的颜色 假定因为可以在水上漂 就相当于会游泳 */class WoodDuck extends Duck{public WoodDuck() {flyBehavior = new FlyNoWay();quackBehavior = new MuteQuack();}@Overridepublic void display() {System.out.println("我的外观颜色是木般的颜色");}}public class DuckTest {public static void main(String[] args) {//向上转型Duck md = new MallardDuck();md.display();md.performFly();md.performQuack();Duck wd = new WoodDuck();wd.display();wd.performFly();wd.performQuack();}}</span>
虽然代码量并没有减少,但是可扩展性可维护性是增加的。现在我们虽然在main方法里调用的是同一个方法,但是具体的结果却不同。我们还可以在Duck类里设定一个改变飞行行为或其他行为的方法,这样子类就可以动态的去设定行为了。

设计模式是一种思想,既然是思想上的东西,就需要我们不断的思考,领悟,在实践当中不断的加以运用与验证。虽然写了这篇博客,但并不代表我已经彻底掌握,后面还需要不断的理解、思考并将这些思想运用到实践当中去。

ps:如要转载,请注明出处。另推荐Head First 设计模式这本书,对于初学者来说是一本不错的书,博客中的例子也是引用书里面的。

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             


0 0
原创粉丝点击