Head First 设计模式收获
找出应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混淆到一起。
这个说起来很简单,但是实施起来会比较难。或者换种方法说,可能一开始设计的时候,一些代码是不需要独立出来的,但是随着时间的流失和业务的优化修改,这部分代码的扩展导致系统越来越臃肿,越来越难以修改。例如书中例子,鸭子会飞,但是万一需求增加了,增加了一个模型鸭子,模型鸭子继承了鸭子父类,那他也不得不继承会飞这个特性。但是其实他并不会飞。其实这种情况,在一开始代码设计的时候,可能很难考虑到。只能尽量做到代码独立,没有一种完美的独立方法,或者说过于独立的代码,会导致开发过程中的复杂。所以只能在两者之间寻找一个平衡,这就很考验设计者的业务水平能力了。
针对接口编程,而不是针对实现编程。
业务尽量依赖抽象和接口,不要依赖具体类
以上两个可以看成是同概念的规则,一个是从接口定义上面规定,一个是从业务代码上规定。
多用组合,少用继承。
例如,在我做过的游戏中,玩家有状态控制器,血量控制器等等控制器,其实这就是一种组合。那么继承是什么呢,我把这些状态控制器和血量控制器的方法都放到玩家身上,当需要的时候,直接调用就好了。那继承会有什么不好的地方呢,主要有四点,分别是:
- 代码在子类中重复
如果有一个MissionPlayer,继承了IPlayer,那Player中的方法,MissionPlayer也要全部复制一次。
- 很难知道玩家的所有行为
如果现在Iplayer中加了另外一个控制器的方法,那所有继承了IPlayer的子类都要重写实现一次方法。
- 运行时的行为不容易改变
假如Player根据场景不同,状态逻辑控制不同,那就要写一堆if
- 改变会牵一发动全身,造成其他子类不必要的改变
假如Player某个通用方法修改了,Player的子类本来调用了这个通用方法,但是修改后的通用方法子类不适用,那子类还是要更改。
那用组合呢,这四大问题基本上可以根绝,或者产生的问题不会有这么多这么麻烦。玩家和各种控制器独立开来,既有利于玩家和控制器解耦,玩家在不同状态、不同场景下可以随便更换控制器,在控制器发生修改的时候,也不会影响到玩家及其子类的修改。其实这上面三条的规则,都是为了减到代码之间的耦合,方面后续需求的扩展以及优化,尽量让代码之间的关系做到有一个(has-a),而不是是一个(is-a)。
对扩展开放,对修改关闭
例如观察者模式,可以在一定程度上,不改变现有的代码,只增加观察者,就可以增加新的功能。但是如果本来的业务修改改的是主体里面的业务逻辑的话,那无可避免的要对现在的业务代码进行修改。换种方式来说,可扩展到什么程度这个度,需要好好把控,如果要求完美的对扩展开放,在每个地方都要采用开放关闭原则,那对业务的框架设计的难度必然大大提高,也是一种时间浪费。所以,如果定夺这个度,还是要取决于个人的能力以及对业务未来扩展的预估。设计模式是为了减少重复工作的,不是来秀技能的,不要把简单的东西硬套设计模式复杂化。
最少知识原则
尽量避免方法之间的耦合调用,例如System.out.println()
,如果是现有功能的设计者的话,应该设计成System.println()
和System.errorPrintln()
,减少用户需要知道的具体细节。