设计模式第一集——策略模式
《Head First——设计模式》真是一本太太太好的书了!!!之前一看设计模式就头疼,这本书浅显易懂越看越想看。
首先,设计模式第一集——策略模式:定义算法族,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
定义不好理解,举一个例子。
假如现在要做一个模拟鸭子游戏,里面有各类鸭子。游戏中有各类鸭子,一边游泳(swim)一边嘎嘎叫(quack)。现在需要对原有的游戏代码进行改进,要求:
1.在新增的游戏中,有些鸭子会飞;
2.鸭子嘎嘎叫的方式有很多不同,比如有的吱吱叫;
3.有一种模型鸭,可以动态的改变自己飞行行为(比如具备火箭动力飞行能力)
针对上面的需求,很容易想到的是设计一个抽象的Duck类,该类里有swim、quack、fly等方法,其它类型的鸭子继承Duck类。这就是我完全不知道设计模式的时候想到的方法,这样设计有很多问题!比如:把fly的方法写到基类中,所有的类型鸭子继承之后都有了fly的能力,但是有些鸭子是不能飞的,就需要把这些鸭子的fly方法覆盖掉,n多个不能飞的鸭子就要覆盖n此,重复代码太多。同理,要求鸭子的嘎嘎叫法也不同,像之前的设计对于很多种鸭子都要重写它们的quack方法。
那要怎么做?
原则一:封装变化
quack和fly的特性是不同鸭子有不同的方式,把他们从Duck的基类中拿出来,单独封装起来,建立一个新类来代表每一行为。这样对quack和fly方法的改变不会影响到其它部分。
那怎么实现多种fly方式和quack方式呢?
原则二:针对接口编程,而不针对实现编程
这个很容易,分别写一个FlyBehavior和QuackBehavior的接口。具体想怎么飞怎么叫,在具体实现中去编吧。在Duck的基类中把之前写死的fly()和quack()方法去掉,改变成委托给FlyBehavior的引用
public class FlyWithWings implments FlyBehavior{
public void fly(){
System.out.println("用翅膀飞!");
}
}
public abstract class Duck{
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public void swim(){……}
public void performFly(){ flyBehavior.fly();}//委托给飞的行为类实现飞的动作
public void performQuack(){quackBehavior.quack();}
}
最后一个需求很简单,我们要动态的改变鸭子的行为,那在Duck中对鸭子的个个行为设立set()方法,当想让这个鸭子有哪种行为时,set一下就可以了。
在最后还要提到一个原则三:多用组合,少用继承.FlyBehavior和QuackBehavior从之前的超类中拿出来,以组合的方式完成动作。
鸭子游戏UML图:
抽象的Duck类:
1 package com.duck; 2 3 public abstract class Duck { 4 FlyBehavior flyBehavior; 5 QuackBehavior quackBehavior; 6 7 public abstract void display(); 8 9 //将飞、呱呱叫的行为委托给行为类 10 public void performfly(){ 11 flyBehavior.fly(); 12 } 13 public void performQuack(){ 14 quackBehavior.quack(); 15 } 16 public void quackBehavior(){ 17 quackBehavior.quack(); 18 } 19 20 public void swim(){ 21 System.out.println("I am swimming"); 22 } 23 //动态设定行为 24 public void setFlyBehavior(FlyBehavior fb){ 25 flyBehavior=fb; 26 } 27 public void setQuackBehavior(QuackBehavior qb){ 28 quackBehavior=qb; 29 } 30 }
FlyBehavior接口:
1 package com.duck; 2 3 public interface FlyBehavior { 4 public void fly(); 5 } 6 7 package com.duck; 8 9 public class FlyNoWay implements FlyBehavior{ 10 public void fly() { 11 } 12 13 } 14 15 package com.duck; 16 17 public class FlyWithWings implements FlyBehavior{ 18 19 @Override 20 public void fly() { 21 System.out.println("I am flying"); 22 } 23 }
QuackBehavior:
1 package com.duck; 2 3 public interface QuackBehavior { 4 public void quack(); 5 } 6 7 package com.duck; 8 9 public class Quack implements QuackBehavior{ 10 11 public void quack() { 12 System.out.println("Quack"); 13 } 14 15 } 16 17 package com.duck; 18 19 public class Squeak implements QuackBehavior{ 20 21 public void quack() { 22 System.out.println("Squeak"); 23 24 } 25 26 }
要实现一个具体的绿头鸭:
现在嘎嘎叫的行为和飞的行为都是单独的类,这样对以后新增的鸭子可以直接组合想要的行为。将需要新的嘎嘎叫和飞行为的时候,写新的类实现实现。若有其他新的行为产生是,再写新的接口用和新的类具体实现。这样在代码维护的时候不用反复修改代码。
1 package com.duck; 2 3 public class MallardDuck extends Duck{ 4 5 public MallardDuck() { 6 //绿头鸭的特点是可以呱呱叫和飞行 7 //因为MallardDuck 继承自 Duck所以它具有quackBehavior和flybehavior实例变量 8 quackBehavior=new Quack();//呱呱叫 9 flyBehavior=new FlyWithWings();//飞 10 } 11 12 @Override 13 public void display() { 14 System.out.println("I am a real Mallard duck"); 15 } 16 }