设计模式(1)-策略模式
引言
最近在读四人帮写的《设计模式》,觉得这本书写得非常用心,逻辑非常严密,对代码复用性和可扩展性的讲解淋漓精致,个人感觉下面几个地方很闪光:
- 业务场景:需求背景并非在一开头就全数给出,而是随着讲解的深入逐步扩展
- 常规设计:给出需求后一开始会按照常规的思路给出的不用设计模式的解决步骤
- 转折与过渡:加入了诙谐幽默的提问方式,指出常规思路为什么不好
- 采用模式的设计:用有趣的标记指出应该如何修改常规解法
- 设计原则总结:每章会总结出一些设计原则,帮助程序员更好的识别和应用模式
但后来发现因为设计模式众多,且它本身还是比较抽象,就算背下整本书,也未必能在一些特定、复杂的场景需求中识别并应用这些模式,加上书中讲述的业务场景还是稍微有点复杂,看完书时印象还很深刻,但隔个一周就有点淡忘了。所以我打算把书中讲到的这些模式记录下来,方便以后查阅。
本章记录的是策略模式,他主要思想是将对象本身与对象的一些需要动态变化的行为进行分离,并通常以接口的形式呈现,最后达到分而治之,降低类间耦合度。
业务背景
需求是实现一群具有不同能力的不同种类的鸭子,有的会游泳、有的会飞、有的会叫。
常规设计
利用继承复用基类Duck中的行为,然后在子类中去覆盖实现新的行为是很容易想到的设计方案。但这样的弊端是需要写很多无用代码,比如这时有个新类诱饵鸭DecoyDuck需要加入,它既不会飞也不会叫,那么覆盖的时候则会把这两个方法变成什么都不做,这样平添了很多冗余无用的代码,容易造成结构的混乱,维护的困难。所以这时我们很容易想到用接口去替换,比如这样:
这样将fly()和quack()从基类中抽离出来,单独以接口形式声明,需要拥有这两种行为的鸭子类才去实现,无用代码消除了。但问题又来了,这样fly()和quack()的行为根本无法复用,因为他们已经不在基类里面了,是以接口形式抽离出来的,因此都是未实现的抽象方法,而如果把他们转变成一个实现了这两方法的基类,让子类再去多继承的确是一种办法,但灵活性很差,因为没法动态变化,而且在Java里面是不允许多继承的,那我们该怎么办呢?
策略模式
一种很精妙的方法就是:先还是将fly和quack这两种行为以接口形式抽离出来,但接下来不同,我们要构建两个行为基类,然后在行为子类中去实现具体的行为方式,另外还需将这两行为植入到鸭子基类Duck中去,让他们以Duck类的成员的形式存在(一种仅有行为的特殊成员)。
不难看出,这种设计达到了主体类(鸭子)与行为(飞行、游泳)的真正分离,他们互不干扰,可以在两个世界实现,但最终他们俩还是需要产生联系,这就是通过在主体类组合两个行为类接口实现的。这样以来,不但解耦了,我们还可以在主体类中提供方法动态地、灵活地改变行为。
结语
本章我们可以总结的设计原则是:
- 封装变化
- 多用组合,少用继承
- 针对接口编程,不针对实现编程