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

[ASP.net教程]设计模式(一) 策略模式


摘自<<Head First Design Pattterns>> chapter 1

 

1  飞翔的鸭子

  假设开发一款模拟鸭子的游戏。首先设计一个鸭子母类,里面有鸭子的叫声、游泳和外形三个成员函数,然后在野鸭和红头鸭两个子类中重写继承的外形函数。

  现在更进一步,要求鸭子会飞,应该如何设计程序呢?

2  两种方法

1)  继承

  为 Duck 母类添加 fly() 成员函数,然后各个子类继承 fly()

  

  问题: 并不是所有的鸭子都会飞,比如“煮熟的鸭子”。

  解决: 不会飞的鸭子自母类继承 fly() 后,重写该函数。

  因为鸭子会不会飞的问题,又发现一个bug,并不是所有的鸭子都会叫,比如“大黄鸭”。因此,不同的子类也需要重写 quack()

  此时问题又来了,公司开发的是游戏软件,要求游戏每三个月更新一次,每次更新后鸭子的飞行方式和叫声都可能发生新的变化。那么,岂不是每三个月都要重写 RubberDuck 和 DecoyDuck 子类甚至更多子类的 fly() 和 quack() 函数,有点繁琐。

   于是,有了第二个方法...

2)  接口 (interface)

  视会飞和会叫为一种能力,并将 Flyable 和 Quackable 做成接口 (interface),然后在里面加上相应的函数 fly() 和 quack()

  这样,只有会飞的子类实现该接口 Flyable, 并且具有 fly() 函数。同理只有会叫的子类实现接口 Quackable 且有 quack() 函数。

  问题又来了,飞行是鸭子的一种行为,不同的鸭子,其各自飞行的方式也不相同。

  很显然,每个子类中还得重写 fly() 和 quack()。因此,单纯的使用接口或继承都不是最佳的方法。

  那么,到底什么方法能完美解决鸭子 fly 和 quack 的问题呢?不着急,先看下面的三个设计原则。

3  设计原则

1)  identify what varies and separate them from what stays the same

     把变化的摘出来,和不变的分割开来

2)  program to an interface, not an implementation

 

3)  favor composition over inheritance

    HAS-A better than IS-A

4  策略模式

    现在引出策略模式

    defines a family of algorithms,  encapsulates each one,  and makes them interchangeable.

    (Strategy lets the algorithm vary independently from clients that use it)

1)  client

  Java: 在 Duck 母类中定义 FlyBehavior 和 QuackBehavior 的成员对象 (FlyBehavior flyBehavior)

  C++: 在 Duck 母类中定义 FlyBehavior 和 QuackBehavior 的指针 (FlyBehavior * pflyBehavior)

2)  encapsulated fly behavior

  把飞行封装为接口,里面具体实现不同的飞行方式,即将各种”飞行方式“视为一系列”算法“,添加或者修改算法都在这个接口里面进行。

  这样,不同的鸭子子类,只需通过母类中的成员对象 (flyBehavior) 调用相应的”飞行算法“即可。

3)  encapsulated quack behavior

  Java: 直接使用关键字 interface 便可将 QuackBehavior 定义成接口

  C++: 接口 ≈ 抽象基类,将 QuackBehavior 定义为抽象基类,也即声明 quack() 为纯虚函数, 具体代码形式为 “void quack() = 0”