你的位置:首页 > ASP.net教程

[ASP.net教程]设计模式 之 策略模式


设想有一个游戏,游戏中有各种鸭子,它们可以飞,也可以呱呱叫。这样一个游戏该怎样设计呢?

一、使用继承:

鸭子虽然有不同的种类,但是也有一定的相同之处,所以我们可以从鸭子中提取一个父类Duck,让不同种类的鸭子类继承自父类,将所有鸭子共有的属性和行为放到父类中。

这样做看似没有错误,但实际上存在以下两个方面的错误:

1、子类的可用行为可能远大于我们的期待范围。把所有方法和属性都定义在Duck父类中,再由所有子类去继承这个父类,那么所有的子类就都具备父类的方法和属性。就拿这个游戏的例子来说,如果加入一个方法fly(),那么就算是橡皮鸭(RubberDuck)类也一定会具备fly()的行为。

2、这种方法并不能有效的解决代码重复问题。拿上一条中的fly()方法来说,既然不同的鸭子具有不同的飞行方式,则fly()方法的方法体一定是写在子类中;而不同种类的鸭子的飞行方式也可能是相同的,所以我们就可能不得不在多个鸭子中使用相同的fly()方法,而在另外一些鸭子中使用另一种fly()方法。

二、增加接口:

我们可以把Duck父类中的fly()方法提取出来定义成接口,称为Flyable,其中定义一个抽象方法fly(),让可以飞的Duck子类实现这个接口并实现fly()方法。

使用接口有效的解决了上面的第一个问题,即可以有效的控制各个子类的行为范围,不会出现“子类具有父类的所有方法”的情况了。但是,JAVA中的接口中不具备代码实现功能,所以这样做并没有解决代码重复的问题。

三、解决问题:

我们需要找出应用中可能需要变化的地方,把它们独立出来,不要和那些不需要变化的代码混在一起,即把会变化的部分取出并封装起来,一边以后可以轻易的改动或扩展此部分,而不影响不需要变化的部分。

在上面的两次尝试(使用继承、增加接口)中,我们都只是把共有的属性和行为抽出来封装成父类或接口,但在这里,我们要尽量两次封装,即在业务类上面还有两层封装类。对于一种行为,我们首先将这种行为抽取出来作为一个总的行为接口,然后再在这个接口下面定义多个不同的实现类,最后再在业务类中调用父类完成业务。简单的说,在上面两种尝试中,我们只是定义了两个互不相关的行为,而在这种方法中,我们定义的是一组有关联的行为。

拿我们这个例子中的fly()方法举例,飞,可能是用翅膀飞(FlyWithWings),也可能是坐火箭飞(FlyRocketPower)。因此,我们定义一个总的接口FlyBehavior,在这个接口中定义一个抽象方法fly()。再定义两个实现FlyBehavior接口的类FlyWithWings和FlyRocketPower,这两个类实现FlyBehavior接口,并实现接口中的fly()方法。另外,我们在Duck父类中定义一个FlyBehavior的接口变量,在生成鸭子实体的时候,给其FlyBehavior赋值,以此决定鸭子的飞行方式。我们还可以在Duck父类中定义一个设置飞行方式的方法setFlyBehavior(),来动态的改变鸭子的飞行方式。

以下贴出DEMO代码:

接口FlyBehavior中的代码:

1 public interface FlyBehavior {2   void fly();3 }

FlyBehavior接口的实现类FlyWithWings(用翅膀飞)类中的代码:

1 public class FlyWithWings implements FlyBehavior {2   public void fly() {3     System.out.println("我用翅膀飞!");4   }5 }

FlyBehavior接口的实现类FlyRocketPower(坐火箭飞)类中的代码:

1 public class FlyRocketPower implements FlyBehavior {2   public void fly() {3     System.out.println("我坐火箭飞!");4   }5 }

FlyBehavior接口的实现类FlyNoWay(不会飞)类中的代码:

1 public class FlyNoWay implements FlyBehavior {2   public void fly() {3     System.out.println("我不会飞!");4   }5 }

Duck父类中的代码:

 1 public abstract class Duck { 2   private FlyBehavior flyBehavior; // 飞行行为接口对象 3   // 设置鸭子的飞行行为方式 4   public void setFlyBehavior(FlyBehavior flyBehavior) { 5     this.flyBehavior = flyBehavior; 6   } 7   // 让鸭子飞行 8   public void performFly() { 9     this.flyBehavior.fly();10   }11   // 输出鸭子的样子12   public abstract void display();13 }

Duck的实体类RedHeadDuck(红头鸭)类中的代码:

 1 public class RedHeadDuck extends Duck { 2   public RedHeadDuck() { 3     this.display(); 4     super.setFlyBehavior(new FlyWithWings()); 5   } 6  7   public void display() { 8     System.out.println("我是红头鸭!"); 9   }10 }

Duck的实体类RubberDuck(橡皮鸭)类中的代码:

 1 public class RubberDuck extends Duck { 2   public RubberDuck() { 3     this.display(); 4     super.setFlyBehavior(new FlyRocketPower()); 5   } 6  7   public void display() { 8     System.out.println("我是橡皮鸭!"); 9   }10 }

主函数Main中的代码:

 1 public class Main { 2   public static void main(String args[]) { 3     // 第一只鸭子:红头鸭 4     Duck duck1 = new RedHeadDuck(); 5     duck1.performFly(); 6     // 第二只鸭子:橡皮鸭(由坐火箭飞改变为不会飞) 7     Duck duck2 = new RubberDuck(); 8     duck2.performFly(); 9     duck2.setFlyBehavior(new FlyNoWay());10     duck2.performFly();11   }12 }

四、总结:

本例主要用了策略模式。所谓策略模式,即定义算法族(几组行为),每组行为封装一种行为的不同表现形式,让他们之间可以互相替换。这种模式让算法的变化独立于使用算法的客户。