00. 复杂性与设计原则
软件架构的复杂性与设计原则
开发计算机软件是人类历史上最纯粹的创作活动之一,开发软件的最大限制是我们了解所创建系统的能力。随着程序的发展和获得更多功能,它变得复杂,其组件之间具有微妙的依赖性。随着时间的流逝,复杂性不断累积,在任何程序的生命周期中,复杂性都会不可避免地增加。程序越大,工作的人越多,管理复杂性就越困难,程序员在修改系统时将所有相关因素牢记在心中变得越来越难;这会减慢开发速度并导致错误,从而进一步延缓开发速度并增加成本。我们看很多大型系统的本质问题是复杂性问题,数百个甚至更多的微服务相互调用

从微观的视角来看,

好的开发工具可以帮助我们应对复杂性,但是仅凭工具我们并不能完全解决问题。尽管我们尽了最大努力,但复杂度仍会随着时间的推移而增加,但是更简单的设计使我们能够在复杂性压倒性优势之前构建更大,功能更强大的系统。复杂性的应对永远不会是一劳永逸,我们需要不断地推陈出新,是动态、渐进的重塑自己对软件系统的认识,不断认识问题和寻找更优解的持续迭代:
-
互联网行业的软件系统,很难一开始就做出完美的设计,通过一个个功能模块衍生迭代,系统才会逐步成型;对于现存的系统,也很难通过一个大动作,一劳永逸地解决所有问题。系统设计是需要持续投入的工作,通过细节的积累,最终得到一个完善的系统。因此,好的设计是日拱一卒的结果,在日常工作中要重视设计和细节的改进。
-
通过使代码更简单和更清晰(Obvious)来消除复杂性。例如
: 减少特殊场景的处理,或变量命名一致性都能降低系统复杂性。代码能够描述程序的工作流程和结果,却很难描述开发人员的思路,而注释和文档可以。此外,通过注释和文档,开发人员在不阅读实现代码的情况下,就可以理解程序的功能,注释间接促成了代码抽象。好的注释能够帮助解决软件复杂性问题,尤其是认知负担和不可知问题(Unknown Unknowns) 。 -
通过分层或者分模块来封装它,对复杂问题的抽象然后分而治之,以便程序员可以在系统上工作而不会立即暴露其所有复杂性。这种方法称为模块化设计。在模块化设计中,软件系统分为模块,例如面向对象语言的类。这些模块被设计为彼此相对独立,以便程序员可以在一个模块上工作而不必了解其他模块的细节。
- 业化分工和代码复用促成了软件生产率的提升。比如硬件工程师、软件工程师(底层、应用、不同编程语言)可以在无需了解对方技术背景的情况下进行合作开发;同一领域服务可以支撑不同的上层应用逻辑等等。其背后的思想,无非是通过将系统分成若干个水平层、明确每一层的角色和分工,来降低单个层次的复杂性。同时,每个层次只要给相邻层提供一致的接口,可以用不同的方法实现,这就为软件重用提供了支持。分层是解决复杂性问题的重要原则。
- 与分层类似,分模块是从垂直方向来分解系统。分模块最常见的应用场景,是如今广泛流行的微服务。分模块降低了单模块的复杂性,但是也会引入新的复杂性,例如模块与模块的交互,后面的章节会讨论这个问题。这里,我们将第三个原则确定为分模块。
复杂性的衡量维度
- 可维护性
/ 可修改性: - 一致性:
- 可读性
/ 清晰性: - 可测试性: