通过以上设计,fly()行为以及quack()行为已经和Duck.java没有什么关系,可以充分得到复用。而且我们很容易增加新的行为,既不影响现有的行为,也不影响Duck.java。但是,大家可能有个疑问,是在面向对象中行为不是体现为方法吗?为什么现在被定义形成类(例如Squeak.java)?在OO中,类代表的"东西"一般是既有状态(实例变量)又有方法。只是在本例中碰巧"东西"是个行为。既使是行为,也有属性及方法,例如飞行行为,也需要一些属性记录飞行的状态,如飞行高度、速度等。

  2)整合变化的内容和不变的内容

  Duck.java将fly()以及quack()的行为委托给行为类处理。

  不变的内容:

publicabstractclassDuck{
//将行为类声明为接口类型,降低对行为实现类型的依赖
FlyBehaviorflyBehavior;
QuackBehaviorquackBehavior;

publicvoidperformFly(){
//不自行处理fly()行为,而是委拖给引用flyBehavior所指向的行为对象
flyBehavior.fly();
}

publicvoidperformQuack(){
quackBehavior.quack();
}

publicvoidswim(){
System.out.println("Allducksfloat,evendecoys.");
}

publicabstractvoiddisplay();
}

Duck.java不关心如何进行fly()以及quack(),这些细节交由具体的行为类完成。

publicclassMallardDuckextendsDuck{
publicMallardDuck(){
flyBehavior=newFlyWithWings();
quackBehavior=newQuack();
}

publicvoiddisplay(){
System.out.println("Greenhead.");
}
}

测试类:

publicclassDuckTest{
publicstaticvoidmain(String[]args){
Duckduck=newMallardDuck();
duck.performFly();
duck.performQuack();
}
}

  在Duck.java子类型MallardDuck.java的构造方法中,直接实例化行为类型,在编译的时侯便指定具体行为类型。当然,我们可以:

  1)我们可以通过工厂模式或其它模式进一步解藕(可参考后续模式讲解);

  2)或做到在运行时动态地改变行为。

  3)动态设定行为

  在父类Duck.java中增加设定行为类型的setter方法,接受行为类型对象的参数传入。为了降藕,行为参数被声明为接口类型。这样,既便在运行时,也可以通过调用这二个方法以改变行为。

publicabstractclassDuck{
//在刚才Duck.java中加入以下二个方法。
publicvoidsetFlyBehavior(FlyBehaviorflyBehavior){
this.flyBehavior=flyBehavior;
}

publicvoidsetQuackBehavior(QuackBehaviorquackBehavior){
this.quackBehavior=quackBehavior;
}

//其它方法同,省略...
}

  测试类:

publicclassDuckTest{
publicstaticvoidmain(String[]args){
Duckduck=newMallardDuck();
duck.performFly();
duck.performQuack();
duck.setFlyBehavior(newFlyNoWay());
duck.performFly();
}
}

  如果,我们要加上火箭助力的飞行行为,只需再新建FlyBehavior.java接口的实现类型。而子类型可通过调用setQuackBehavior(...)方法动态改变。至此,在Duck.java增加新的行为给我们代码所带来的困绕已不复存在。

  该是总结的时侯了,让我们从代码的水中浮出来,做一只在水面上自由游动的鸭子吧:

  3.解决方案

  MallardDuck继承Duck抽象类;->不变的内容

  FlyWithWings实现FlyBehavior接口;->变化的内容,行为或算法

  在Duck.java提供setter方法以装配关系;->动态设定行为

  以上是策略模式的实现三步曲。接下来,让我们透过步骤看本质:

  1)初始,我们通过继承实现行为的重用,导致了代码的维护问题。->继承,isa

  2)接着,我们将行为剥离成单独的类型并声明为不变内容的实例变量并通过->组合,hasa

  setter方法以装配关系;

  继承,可以实现静态代码的复用;组合,可以实现代码的弹性维护;使用组合代替继承,可以使代码更好地适应软件开发完后的需求变化。

  策略模式的本质:少用继承,多用组合。