尝试用uml从简单到复杂的程序逻辑组织方式,体现出程序用什么样的方式来面对复杂
1.只有一个类一个方法
先从最简单的只有一个类,一个方法的情况说起,如果只写一个类一个方法写成怎么样算合格了呢?
ClassA 必须有单一职责,可以用一句话说出它是什么或者它做什么,例如:ClassA负责什么,或ClassA提供什么服务之类的主谓结构。可做为A的注释
methodA 同上也必须有单一的职责,可以用一句话说出它做什么,例如:ClassA可以提供methodA服务。一个方法一要有单一的服务。方法要有明确的输入和输出。在java中类属性也可以做为输入和输出。
能做到上面两点,这个类和方法就合格了。
引申下:名字和注释其实是体现类或方法单一职责的好地方。
引出第一个原则:单一职责。编码最基本的原则了,一定要满足。
2.一个类多个方法
如果一个类中出现两个方法,并且有调用关系,如上图,只要ClassA和methodA和methodB都满足单一职责,上图中的代码是没有问题的。
只要类或方法都满足单一职责,一个类中有多个方法是ok的。
3.方法变化,变复杂了,怎么办?
考虑这种情况,如果当中一个方法methodB出现了变化点,methodB部分的需求变的越来越复杂时,维护ClassA就算的复杂起来。
当然就这样维护也是可以的,如果你能在一个类中把这些逻辑组织起来,并且还能保持单一职责,但这样会越来越吃力,
这时就需要把methodB隔离出来了,如下图
这时,如果methodB部分的需求变了,直接修改ClassB就可以了,不用考虑ClassA。现在在前面方法都符合单一职责的基础上,把一个方法的逻辑上升到了一个类的层面。并且分离了出去。
从这看其实组合是为了分离,但也达到了重用的目的,因为别的类中也可以使用ClassB。
从领域逻辑的角度看,是把以前的一个内部逻辑升级到了一个逻辑实体。这个逻辑实体以后可以独立变化了。
在这当中涉及到第二个原则:隔离变化点,隔离变化点的同时一定会产生代码复用的好的副作用。
4.如果同一个逻辑,在系统中不同情景有不需要
例如在情景一中ClassA有时需要B这样做,在情景二中ClassA需要那样做,这就要求类逻辑要可替换。如下图:
这时用到了接口,接口起到了可替换的作用,可替换是因为建立了规约。为了让每个类对外的服务都很明确,大家习惯把每个类都实现相应接口,当然每个类也都有了可被替换的功能。如下图
(策略模式)
本例总结第三个原则:模块可替换
5.如果同一规约下有共同代码?
如果上图中ClassB1和ClassB2中有一些代码是相同的,就有必要使用一个手段来去掉重复代码。在此使用继承,如下图
(模板方法)
引出第四个原则:去掉重复代码。
5.如果继承改用组合
直观感觉是不是可以采用组合呢?如下图:
如果只有methodB_call是两者的不同点,如果把methodB_call采用组合委托出去,则interfaceB和继承体系就没必要存在了,诞生了一个新的逻辑体系methodB_Call,体系更庞大了,比上面采用的继承好还是不好呢?
5.继承和组合对比
此处:先采用继承,或优先考虑继承,减少一个逻辑体系,大家还是一个整体,程序结构简单些。如果有其它模块也需要使用methodB_call这个逻辑,那把它隐藏在继承体系中就不好了,就要分出去。分出去更灵活,重用度更高。分和不分的标准是有没有其它的非本继承体系要重用methodB_call。
继承是有了孩子还没分家,还是主合的,一个继承体系是一个整体;组合是要分家,另一个家庭可以独立生存,独立对外联系,组合的各部分是一个独立体系。
采用组合后,还有一个好的副作用,就是原类的继承体系也可以有,和分出去的没关系,两个家庭可以独立发展。如下图:
其实在同一规约下,保留原来的继承体系,并采用合成来分离不同关注点,让它们可以独立变化。(桥梁)
总结下本文涉及的基本编码原则:
第一个原则:单一职责。
第二个原则:隔离变化点。
第三个原则:模块可替换
第四个原则:去掉重复代码。
注:只代表本文中出现的顺序,不表示重要性高低。
附:本文主要针对服务类来说,针对的是类提过什么服务。和数据封装的关系暂不讨论。
本文中的括号里面内容可以省略不看,后面尝试能不能把简单的编码过程和不同设计模式结合起来。