设计模式之策略模式

来源:互联网 发布:光纤网络监控方案 编辑:程序博客网 时间:2024/06/01 09:56

《head first设计模式》一书中说:时时刻刻思考着模式如何仰赖基础与原则。
基础:抽象、封装、多态、继承
原则:封装变化、多用组合,少用继承、针对接口编程,不针对实现编程

策略模式:定义算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

先从简单的模拟鸭子应用做起:现在假设有一套模拟鸭子的游戏SimUDuck,游戏中会出现各种鸭子,一边游泳,一边呱呱叫。
首先,设计出一个鸭子超类(Superclass),让各种鸭子继承此超类。

public  abstract class Duck{    //所有的鸭子都会呱呱叫也会游泳,所以由超类负责处理这部分的实现     public void quack(){        //实现鸭子叫    }    public void swim(){        //实现游泳    }    //鸭子的外观不同,所以由子类实现    public abstract void display();}

绿头鸭:

public class MallardDuck extends Duck{    @Override    public void display(){        //外观是绿头    }}

红头鸭:

public class RedheadDuck extends Duck{    @Override    public void display(){        //外观是红头    }}

假如:现在让鸭子能飞

public  abstract class Duck{    //所有的鸭子都会呱呱叫也会游泳,所以由超类负责处理这部分的实现     public void quack(){        //实现鸭子叫    }    public void swim(){        //实现游泳    }    //所有子类都会继承fly()    public void fly(){        //实现飞    }    //鸭子的外观不同,所以由子类实现    public abstract void display();}

问题出现了:如果现在有一个橡皮鸭,可是橡皮鸭并不会飞。这就是说:并非所有Duck的子类都会飞,对代码所做的局部修改,影响层面可不只是局部。

怎么办?解决方案有几种,咱们一个个讨论。
一、使用继承,在子类中将父类方法fly()覆盖且方法体中不包含任何内容。
二、利用接口,将fly()从超类中取出来,放进一个Flyable接口中,这么一来,只有会飞的鸭子才实现此接口。

第一种方案不可行,因为并非所有的鸭子都会飞(fly()),重复代码会变得很多。
第二种方案也不可行,首先是代码不能重用,而且很可能,在会飞的鸭子中,飞行的动作还有多种的变化。

所以,使用继承并不能很好地解决问题,因为鸭子的行为在子类里不断地改变,并且让所有的子类都有这些行为是不恰当的。Flyable接口可以让会飞的鸭子实现飞的功能,可是接口无法达到代码的复用。这意味着:无论何时你需要修改某个行为,你必须往下追踪并在每一个定义此行为的类中修改它,一不小心,可能 造成新的错误。

幸运的是,有一个设计原则,恰好适用于此情况。
找出应用中可能需要变化之处,把它们独立出来 ,不要和那些不需要变化 的代码混在一起。

分开变化和不会变化 的部分。我们知道Duck类内的fly()和quack()会随着鸭子的不同而改变,为了要把这两个行为从Duck类中分开,我们将把它们从Duck类中取出来,建立一组新类来代表每个行为。

设计鸭子的行为。
如何实现飞行和呱呱叫的行为的类呢?我们希望一切有弹性,还想能够“指定”行为到鸭子的实例。比方说,我们想要产生一个新的绿头鸭实例,并指定特定“类型”的飞行行为给它。我们应该在鸭子类中包含设定行为的方法,这样就可以在运行时动态地改变绿头鸭的飞行行为。

想要实现目标,需要借助第二个设计原则:针对接口编程,而不是针对实现编程。 (读者可以自行查阅针对接口编程)

我们利用接口代表每个行为,比方说,FlyBehavior与QuackBehavior,而行为的每个实现都将实现其中的一个接口。这样Duck类中不会写死行为的实现(换句话说,特定的具体行为编写在实现了FlyBehavior与QuakcBehavior的类中)。

实现鸭子的行为。在此,我们有两个接口,FlyBehavior和QuackBehavior,还有它们对应的类,负责实现具体的行为。

整合鸭子的行为。
关键在于,鸭子现在会将飞行和呱呱叫的行为“委托”别人处理,而不是使用定义在Duck类内的呱呱叫和飞行方法。
做法如下:
1、在Duck类中加入两个实例变量,分别为”flyBehavior”与”quackBehavior”,声明为接口类型(不是具体的实现类);将Duck类中的fly()和quack()删除,因为这些行为已经搬到接口的实现类中了;添加performFly()和performQuack()。

2、实现performQuack():

public class Duck{    QuackBehavior quackBehavior;//每只鸭子都会引用实现QuackBehavior接口的对象    public void performQuack(){        quackBehavior.quack();//交给quackBehavior处理    }}

3、设定flyBehavior和quackBehavior的实例变量

public class MallardDuck extends Duck{    //MallardDuck继承了Duck类,相应地拥有flyBehavior和quackBehavior实例变量    public MallarDuck(){        quackBehavior = new Quack();//这里        flyBehavior = new FlyWithWings();    }}

其中Quack是QuackBehavior的具体实现类,FlyWithWings也一样。

!!此处没有完全实现针对接口编程,在后面的内容中,会写这一部分。

0 0