策略模式的应用实例 - 鸭子开发
假设一个鸭子实例,要求各种各样的鸭子会游泳、会呱呱叫。按照OO技术的开发原则,我们首先会让鸭子实例的超类Duck实现这些方法,已进行代码的重复利用:
DUCK |
quack() swim() display() //鸭子的其他方法 |
RedDuck //继承Duck超类 |
display(){ //我是红鸭子 } |
DarkDuck //继承Duck超类 |
display(){ //我是黑鸭子 } |
DUCK |
quack() swim() display() fly() //鸭子的其他方法 |
RedDuck //继承Duck超类 |
display(){ //我是红鸭子 } fly(){ //我会飞 } |
DarkDuck //继承Duck超类 |
display(){ //我是黑鸭子 } fly(){ //什么都不做 } |
看来继承并不能很好的解决问题了,那我们试试接口好啦。我把fly()和quark()方法从超类中分别提取出来做成接口,让具有其行为的鸭子才实现此接口。可是这样仍然没有解决代码无法复用的问题,造成了大量的代码重复。
这样看来,并不是所有的鸭子都具有同样的行为,所以继承不是合适的解决方法;但是采用接口又无法实现代码复用。现在就来看看策略设计模式的灵通吧。
策略模式的定义
策略模式定义了算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
设计原则一
把会变化的部分取出并封装起来,以便日后可以轻易的改动或扩充此部分,而不影响其他不变部分。
现在我们要把fly()和squack()方法提取出来,并且封装起来。
设计原则二
针对接口编程,而不是针对实现编程。
刚才我们的做法是:行为来自超类的实现,或者行为来自子类的实现。实现,实现,瞧吧,我们被实现绑架啦。与之前不同的是,可变行为的实现不再由鸭子类实现,而是交由专门的实现类实现。
真正编程接口的含义
更确切点,我们说针对超类型编程,此处的超类型接口不只是interface,还包括abstract class,此处的关键是利用多态。也就是说,变量的定义应为超类型形式,即接口或抽象类,这样任何实现此接口或抽象类的子类均可赋值给此变量,声明类时无需理会真正的对象类型。
封装行为的大局观
1.定义鸭子超类,实现公用方法
Duck //定义鸭子超类 |
FlyBehavior flybehavior QuackBehavior quackbehavior |
swim() fly() squack() display() setFlybehavior() setQuackbehavior() |
interface fly |
fly() |
Fly1 //实现Fly接口 |
fly(){//实现fly} |
Fly2 //实现Fly接口 |
fly(){//什么都不做} |
interface Squack |
quack() |
Squack1 //实现Squack接口 |
quack(){//咕咕叫} |
Squack2 //实现Squack接口 |
quack(){//呱呱叫} |
setFlybehavior()和setQuackbehavior()方法可以动态的设定两实例对象,实时改变鸭子子类的行为,实现了代码的复用和可变与不变的分离。这就是策略模式的精髓所在。