最近半年主要在对一个10余年的商业化系统进行重构。对于商业化系统来讲,普遍存在状态多、链路长、业务逻辑复杂等特点。加上系统设计之初并未考虑到后续业务的发展形态,在承接业务需求时,不断打补丁,存在很多ad hoc方案,系统最终变成了一个”大泥球“,导致的问题是业务迭代效率低,代码变更故障率高。这篇文章主要对如何解决复杂问题谈谈自己的一些思考,供大家参考。
思考框架
首先谈谈什么是复杂问题。复杂问题是一个系统性问题,它的特点是规模大、维度多、关系复杂。在面对大型或复杂问题时,我们需要先找一样思想武器,或者说思考框架,可能简单归结为:分治、知识和抽象。
分治可以把问题分割为规模更小且易于处理的若干子问题,这样就可以运用相似的知识来解决这些子问题,而使用抽象有助于进行推理和判断。分治、知识和抽象的有效性在于它们能够帮助我们在不变的智力条件理解和解决不断增长的问题。
分治 作为解决复杂度及规模问题的有效策略,需要满足两个条件:
- 分割后的各子问题必须足够小,以便一个人就能理解并解决它。
- 需要考虑各子问题如果组装(合并)成一个整体。
知识 可以帮忙我们从先前问题中习得的知识来解决当前问题。比如数据库的查询优化、各语言的优势、设计模式、架构风格等。
抽象 能够简化问题空间,使用问题可能更容易理解和管理,它更具通用性和泛化能力。抽象能够有效解决复杂度和规模增长的问题。但抽象也是最难掌握的技能,正如Leslie Lamport所说:
The hardest part of writing a specification is choosing the proper abstraction. I(Lamport) can teach you how to use TLA+; but I don’t know how to teach you about abstraction. A good engineer knows how to abstract the essence of a system and suppress the unimportant details when specifying and designing it. The art of abstraction is learned only through experience.
– Leslie Lamport
简单问题可以直接解决,无须抽象。但面临复杂问题,我们就需要将问题抽象,映射到一个模型上,在模型内解决问题,再将解决方案转换为现实世界的方案。
上图中,简单问题可以直接解决(灰色箭头);复杂问题通过抽象解决(蓝色箭头),在图中可以看出,它的解决路径更长。它将现实问题“等价”转化为更容易理解的抽象问题,通过对抽象问题的解决,再转化为现实世界的解决方案。
设计思路
说了这么多,那如何运用这个思路解决系统的复杂度的问题呢?简单来说,可以归结为:
系统上采用分层分模块分解业务领域(分治+知识),模块内将相似业务流程抽象为接口(抽象+知识),通过插件化机制分割各业务线(分治+知识)。这是一条纲要性的指导思路,具体的系统实现有空再写一篇详细分解文章。
有些人可能觉得,系统是一开始就设计出来的,这有个前提是架构师具体丰富的经验和知识储备,通常情况下系统是不断演进的。当我们面对复杂问题刚开始会出现模糊性或无从下手的情况。它的根本原因在于对问题元素缺少必要输入和结构化梳理(参考金字塔原理),导致头脑中没有一个清晰的问题框架。所以我们通常所说的采用自顶向下的设计有一个前提就是拥有对问题的大量知识储备,但我们面临一个新问题时通常都缺乏必要的背景知识,这个时候我们需要采用自底向上的方式储备必要知识,再采用自顶向下方式做分治和抽象。并且这个过程不是一促而僦,而是一个循环迭代的过程。