把模块切割为多个子组件——复杂模块解决之道
1.以业务独立性为边界,把模块切割为多个子组件。
切割为多个子组件是一种极有效地降低业务复杂度的方法。
其实从编程方面看来,切割子组件会增加编码的工作量——我们需要额外写代码让组件之间通讯;但从业务实现的角度来说,切割子组件是值得的——虽然编码的工作量提高了,但(切割之后)每个子业务的复杂性是有限的,比较容易实现——如果你觉得仍然不好实现,那么继续切割,直到业务逻辑单元的粒度达到能够把握的程度。也就是说,虽然无论我们如何切割,系统的业务逻辑的复杂度总量并没有减少,但复杂度却分散到了各个子组件中。我们一次性搬不走万斤大石,但我们可以切成100斤的小块带走。
同时减小业务逻辑单元的粒度也利于重构。千头万绪的代码纠缠在一起,重构就成了大问题。单纯从代码量的角度来说,我们重构一个10余行的方法,比重构一个100行的类要轻松,我们重构一个200行的类,要比重构一个2000行的类要轻松。代码越长,业务逻辑越多,业务逻辑越多,代码越复杂——阅读和理解就越困难。业务逻辑的实现没有人可以一步到位,是否易于重构是衡量代码好坏最重要的标准。
但是,业务逻辑的分散并不总是有着积极意义的。切割组件会导致业务逻辑也被切割,多个有关系的的业务逻辑被零碎地分散到各个子模块中,维护起来非常头痛。因此,许多时候,我们并不希望所有(特别是核心)业务分散,我们更希望能够灵活地决定业务逻辑的分散与集中。由此,我们摒弃了接口,并采用了Document-View模式。
2.接口和通讯
考虑到我们是以降低业务复杂性为目的而切割模块的,因此子组件没必要使用interface。我们切割子组件并不是为了重用,而且子组件要实现的业务差异很大,这种情况下,使用interface反而会增加编码(主要是阅读和调试)的难度,却并没有给我们带来明显的好处。注意,我们需要把注意力集中到业务逻辑上来,不要被技术迷惑——技术是为业务逻辑服务的。
子组件之间的通讯,尽量让容器(窗体)主动通知,即使是有多个组件要通知,宁可采用笨拙的遍历调用各个子组件的方法,也不要用委托。也就是说,能直接调用方法的地方,就不要用委托。因为相对委托来说,调用方法直观、易于维护——委托是粒度更细的接口。再次强调,我们切割子组件,并不是为了减少耦合和重用,而是为了降低业务逻辑的复杂度。
3.使用Document-View模式。
是采用MVC?MVP?还是Document-View?
Document-View是一种管理多组件(通讯)的有效模式。Document-View比MVC/MVP更简洁。
Document-View的Document类似于MVC的Model, 而View类似于把MVC的View和Controller合并起来。
我们把子组件的容器(窗体和组件容器)当作View,然后再建立一个(注意,是一个)Model。
为什么要划分出Model?
首先,我们的Model是以OO的形式构造的,非常方便做业务逻辑处理。
其次,一个复杂的模块,其子组件之间的通讯肯定是非常紧密的。虽然容器可以让子组件之间正常通讯,但业务流程在各个子组件之间跳转,增加了复杂性,并且这种复杂性随子组件之间的相互调用呈指数级增长。比如用户选择了A组件的一个货品,然后必须通知B组件做金额统计,再通知C组件自动更新折扣,最后还要通知D组件缓存。反之,如果B组件修改了货品数量,就必须再通知A、C、D三个组件。
这种相互之间的通讯,依靠容器来主动调用相关方法,会极大地增加组件的接口(方法和属性)——增加阅读和理解的困难。
归根到底,业务逻辑实现的本质是在程序员的推动下使用指定的规则(方法的调用)去改变对象的数据(属性和字段)。
因此当我们构造了一个业务模型之后,公布出调用的方法(用户的推动),然后在内部改变数据。通过数据绑定,我们还能做到属性“即改即变”(模型的属性值更新之后,控件自动显示新值)。
Document-View减少了组件之间相互调用的复杂性,还避免了切割子模块导致业务也被切割得很凌乱的问题,Document-View给了我们一个把核心业务集中的机会。

浙公网安备 33010602011771号