今天是6.17,京东618的前夕。
来京东800多天了,感觉到了初级程序员的‘痒’年了,自己很迷茫,但是又不知道该如果走出当下这种困境。突然想起一句话:当你迷茫到不知道你能做什么的时候,那你就该看书了。
今天开始学习《java与模式》这本书。
第一章介绍了UML的使用,我们直接跳过。
面向对象的设计原则
- 开闭原则
- 里氏替换原则
- 依赖倒转原则
- 接口隔离原则
- 组合/聚合复用原则
- 迪米特法则
开闭原则
开闭原则讲的是:一个软件实体应当对扩展开放,对修改关闭。这个原则说的是,在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展。换言之,应当可以在不必修改代码的情况下改变这个模块的行为。
这句话听上去好像矛盾,但是实际上面向对象的设计原则和设计模式中很具体的说明如何在设计上做到这一点。
所有的软件都有一个共同的性质,即对它们的需求都会随实际的推移而发生变化。在面临新的需求的时候,系统的设计必须是稳定的。满足开-闭原则的设计可以给一个软件系统无可比拟的优越性。
怎么做到开-闭原则
这里用书上的一个例子来说明;咋看起来,不能修改而可以扩展似乎是自相矛盾的。怎么可以同时又不修改但是可以扩展呢?看看当年孙悟空大闹天宫的时候一个场景。
玉帝招安美猴王
当年大闹天宫的时候美猴王便是玉帝天庭的新挑战。美猴王说:“皇帝轮流做,明年到我家,只教他搬出去,将天宫让与我!”对于这个挑战,太白金星给玉皇大帝提出的建议“降一道圣旨,把他宣来上界,给他分配一官半职,一是不劳师动众,二则可以把他收为仙”。
换言之,不劳师动众,不破坏天规就是“闭”,收为神仙就是“开”,招安法则就是玉帝的“开-闭”原则。
招安法则的关键就是不允许改变现在的天庭秩序,但是可以把妖猴纳入现有的秩序中,从而扩展了这一秩序。面向对象的语言来讲,不允许改变的是系统的抽象层,可以更改的是系统的实现层。比如你现在有一个接口是对“交通工具”的抽象。叫CommunicateService。在蒸汽机的时代,这个Service的实现就有“火车”,“轮船”,内燃机时代你就得新增一个实现类,叫汽油车。新能源时代就叫电动车....一直下去可以扩展到无止境。
里氏替换原则
里氏替换原则原则中说,任何基类出现的地方,子类一定可以出现。
里氏替换原则是对“开-闭”原则的补充。正如前面所说,实现“开-闭”原则的关键是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏替换原则是对实现抽象化的具体步骤的规范。
依赖倒转原则
依赖倒转原则讲的是要依赖于抽象,不要依赖于实现。
看上去依赖倒转原则和开闭原则有很大的相似之处,实际上,他们之间的关系是目标和手段的关系。开闭是目标,而达到这一目标的手段是依赖倒转原则。
聚合复用原则
聚合讲的是要尽量使用聚合,而不是继承关系达到复用的目的。
迪米特法则
迪米特法则讲的是,一个软件实体应当与尽可能少的其他实体发生互相作用。
当一个系统面临扩展的时候,其中的一些模块,它们的修改压力要比其他模块大一些,最后的可能是这些模块需要修改或者不需要修改。但是不论哪一种情况,如果这些模块是相对孤立的,那么它们就不会将修改的压力传递给其他模块。
接口隔离原则
接口隔离原则讲的是,应当我客户端提供尽可能小的单独的接口,而不要提供大的总接口。
显然,接口隔离原则和广义的迪米特法则都是对一个软件实体与其他软件实体的通信的限制。广义的迪米特法则要求尽可能限制通信的宽度和深度,接口隔离原则限制的是通信的宽度,也就是说要求通信应该尽可能的窄。
显然,遵循接口隔离原则和迪米特法则,会让一个软件系统在扩展功能的时候,不会将修改的压力传递到其他的对象。
一个重构做法的讨论
“将条件转译语句改写成多态性”是一条广为流传的代码重构做法。意思是将一个进行多次条件转译的业务逻辑封装到不同的具体子类去。从而是用多态性代替条件转译语句。下面就从“开闭”原则分析这一代码重构做法的适用性。
寻找可变性的线索
首先,这一代码重构的做法是实现“开闭”原则的一条重要线索,因为条件判断语句特别是大段大段的代码转移语句往往意味着可变性。将这种可变性用多台替换,意味着吧这种变化封装了,从而带来系统在这种变化发生时的“开闭”原则。
但是,这种做法本身并不能保证实现开闭,我们应该以开闭为指导,事实上,这一做法有明显缺点:
- 任何语言都有if..else... 这本身并不是错的,如果需要我们完全可以选择
- 使用多态性替代条件语句以为着有更多的类被创建了。如果有三个方法,每个方法有三个if分支,那么会创建9个类。
什么情况下使用这种重构做法
如果一个条件语句中确实封装了某种业务逻辑,那么就可以使用这种多态性封装起来。符合开闭原则。
如果一个if分支中没有涉及任何的业务逻辑,或者基本不会有变化的可能,那么就没必要这么做了。