设计模式(一)会飞的鸭子(策略模式)
设计原则:
多用组合(composition),少用继承。
鸭子类
首先我们有一个鸭子类
1 | public abstract class duck{ |
然后具体是哪种鸭子,只要去继承这个鸭子类就好了。
比如 MallardDuck 重写 display()
方法,它就是绿头鸭。
1 | public class MallardDuck extends duck{ |
现在,我们得让鸭子能飞
1 | public abstract class duck{ |
然后你会惊奇地发现,橡皮鸭(RubberDuck)居然飞起来了。这不科学!
可能的解决方案
- 重写:可以重写橡皮鸭(RubberDuck)的 fly 方法,变成什么都不做。但是这样假若我们又添加了诱饵鸭(DecoyDuck),不会 fly 也不会 quack , 我们又要去重写。显然也很麻烦。
- 接口:可以把 fly 抽象成一个 flyable 接口,让会飞的鸭子去实现这接口。但是我们有 48 个 鸭子子类,都要去实现一遍吗?
显然,继承或重写不能解决问题,因为鸭子的行为在子类里不断地改变,并且让所有的子类都有这些行为是不切当的。 抽象出 flyable 接口 和 quackable 接口,让具备这些功能的鸭子子类去实现这些接口,要写很多重复代码,无法复用。
设计原则:
找出应用中可能需要变化的地方,把它们独立出来,不要和那些不需要变化的代码混在一起。
解决方案
既然 fly 和 quack 会随着鸭子的不同而改变,那么不妨把这两个行为抽出来,新建成 鸭子行为 类。然后在鸭子类中包含设定行为的方法。
关键点:
- 新建鸭子行为类
- 在鸭子类中添加设定行为的方法
这样一来,我们 new 一个 绿头鸭(MallardDuck)的时候,就能给它指定特定的 fly 行为 和 quack 行为。
设计原则:
针对接口编程,而不是针对实现编程。
具体实施
定义FlyBehavior
接口,里面有一个 fly()
方法。FlyWithWings
类实现了这个接口,表示用翅膀飞,FlyNoWay
类也实现这个接口,表示不会飞,FlyWithRocket
类也实现这个接口,表示用火箭发动机飞。
同理,定义QuackBehavior
接口,然后有几个不同的实现类。比如Quack()
表示呱呱叫,MuteQuack()
表示不会叫。
现在,鸭子类是这样的
1 | public abstract class duck{ |
现在,橡皮鸭(RubberDuck)类看起来是这样的
1 | public class RubberDuck extends Duck { |
测试类
1 | public static void main(String[] args){ |
至此,我们的鸭子就飞起来了。
所谓组合,就是鸭子类和鸭子行为类的组合。实例化一个鸭子的时候,给它组装上对应的行为。
以上,其实就是设计模式中的 策略模式(Strategy Pattern) 了。策略模式提供了多种实体类和行为类,使用者需要哪种实体和哪种行为,可以自己去组装。
参考:
- 《Head First 设计模式》