

大家好,我是人月聊IT。今天继续和大家推荐和导读《软件设计哲学》这本经典书籍。
这本书深入探讨了如何通过高内聚低耦合、分层抽象等设计方法有效降低软件系统的复杂性,适合所有软件开发人员阅读。
核心要点

具体文章内容:

大家好,我是人月聊IT。今天为大家导读《软件设计的哲学》这本书。
我在2017年和2018年时曾读过此书,印象最深的是阮一峰专门写过一篇博客文章推荐这本书。后来,许多开源社区成员对书中的关键章节进行了翻译。该书由人民邮电出版社出版,两位译者都是知名的技术专家,尤其是王海鹏,我在十多年前就阅读过他翻译的关于敏捷方法和需求工程方面的书籍。
从翻译质量来看,这本书的可读性相当出色。在导读之前,我想先说明这本书的受众群体。根据我的阅读体验,这本书适合所有从事软件开发的人员阅读,无论是编码工程师还是系统设计师,都能从中获得启发。
本书的核心主题是探讨软件系统的复杂性问题。
全书围绕一个核心议题展开:如何有效降低或解决软件系统的复杂性。作者首先对复杂性进行了简要定义,即在大型软件系统中,所有导致理解和变更困难的因素均可归为复杂性。
关于软件复杂性的成因,书中提出了两点:
相比之下,模糊性相对更容易解决。 在前期制定开发标准、规范和框架时,包括设计原则的确定,依赖性问题往往难以解决。对于大型系统,我们通常采用分而治之的方法来处理问题,这必然涉及模块和组件的拆分。
然而,拆分后又会面临集成和相互依赖的问题。若拆分过于粗略,则难以应对系统的复杂性;若拆分过于细致,模块间的集成和依赖又会加剧这种复杂性。因此,这成为一个两难的问题,解决起来颇具挑战性。
在概述复杂性之后,本书正式进入软件设计哲学的内容。书中探讨的核心内容主要围绕通用的软件设计方法和设计模式展开,其本质在于如何实现高内聚低耦合,以及如何进行有效的分层与抽象。尽管这些主题在软件工程领域并不陌生,但本书仍提出了一些关键观点。其中首要强调的是模块设计应追求深度而非浅显。
简单来说,深模块是指将所有复杂性隐藏在模块内部实现。模块对外暴露的接口应尽可能少且简单,这就是深模块的设计思路。在此基础上,通用模块应当设计得更深。通用模块通常指技术组件或技术模块,如消息、安全和缓存等常见功能。
通用模块更深的核心原则是:在实现通用模块时,不应引入上层专业模块的任何专业特性。
在这一部分,他专门以编辑器的设计为例进行讲解。这个例子非常典型。在设计编辑器时,核心底层需要处理字符和流。但在实现底层核心功能时,不能引入上层终端、界面或UI相关的特性。因为一个编辑器可能运行在移动应用、桌面电脑或平板电脑上,不能将上层UI界面的特性耦合到底层模块中。
因此,通用模块的设计应更为深入,这是本书关于模块设计的关键要点之一。另一个重要观点是,本书着重探讨了如何在软件设计中实现有效的横向分层与抽象。每一层都应各司其职,这在软件设计过程中至关重要。
因为任何模块的实现都必然涉及横向分层,无论是采用当前主流的微服务架构,还是早期类似Spring MVC的三层架构。无论如何分层,都会涉及底层数据访问、中间业务逻辑处理、上层界面展示,以及接口的实现与暴露。
每一层都应具备明确的职责,不可滥用或混淆。本书强调了一个关键概念——直通方法。这种方法类似于领域建模中的贫血层:尽管存在逻辑层,但其代码仅有一行,仅起到简单的代理和透传作用。真正的逻辑并未体现在逻辑层,而是被置于数据访问层或前端界面展现层,这显然是不合理的。
模块横向分层后,必须明确每一层的职责范围及其具体任务。本书专门用一章讨论“不同层不同抽象”,这是相当重要的内容。后续章节涉及大量编程相关内容,尽管书名是《设计哲学》,但更准确地说,它涵盖了设计哲学与核心编程思想。在编程部分,作者重点阐述了异常处理的原则:各层应处理本层异常,既不能将底层异常暴露给客户端,也不应完全屏蔽异常。
在编写代码时,核心注释的概念至关重要。然而,在讨论注释之前,我们更应关注源代码本身的设计。注释不应局限于单行代码的解释,而应着重阐明整个方法或函数的功能,以及该方法与其他方法或函数之间的关联与依赖关系。这才是撰写注释的关键所在。
在未进行复杂的架构设计和详细设计时,本书提出了一个关键思路:实现功能时应先撰写注释,再编写实现代码。 撰写注释的过程即梳理核心设计思路的过程,本质上是在进行详细设计。这一观点值得大家关注。后续章节还探讨了一致性、软件发展趋势以及性能设计等主题。
因此,这些章节不再进行详细导读。通读全书后,我仍强烈推荐这本书。该书堪称经典,出版至今已有七八年,但其核心思想仍未过时,值得学习。
建议将本书与《人月神话》结合阅读:《人月神话》主要探讨软件复杂性与IT项目管理的关系,而本书则聚焦软件复杂性与设计的关系,阐述如何通过优秀的设计降低软件复杂性。
软件开发的首要任务和次要任务

在《人月神话》这本书中,作者将大型软件项目的管理和软件系统的设计分为首要任务和次要任务。他认为首要任务是将现实中真实的业务需求和用户需求转化为一种抽象的模型。而次要任务则是针对这个模型,将其转化为代码和可执行的程序。
软件行业发展了这么多年,在首要任务上,我们并没有找到一种能够指数级提升效率的方法。对于软件工程这个领域,没有所谓的“银弹”。
同时这本书在“外科手术队伍”这一章节中,作者又专门指出,为了保证整个软件系统的架构设计的概念完整性,需要将核心的工作掌握在一个人手中,类似于做外科手术的主刀医生。
所以,对于《人月神话》在谈软件复杂性的时候,更多的是从业务层面、架构设计层面来谈。首先是业务复杂,这才会导致我们技术实现上的复杂。
软件复杂性=业务*技术*治理
基于这两本经典书籍对软件系统复杂性的分析和阐述,我个人的理解是,我们应该从三个重要维度去理解软件系统的复杂性:业务复杂性、技术复杂性、上线以后的管控治理复杂性。
这三个复杂性刚好覆盖了整个软件全生命周期的过程管理。
对于业务复杂性,我们经常说的思路或解决方案包括业务建模、领域建模、MDA(模型驱动设计)、组件化、模块化、SOA横向分层,包括当前主流的微服务架构设计和微服务的拆分。这是从业务角度我们要去做的事情。
对于技术复杂性,更多的是我们在谈纯技术架构上面,如何支撑整个软件系统的高可用和高并发。对于这一块,我们经常谈到的类似于集群、分布式架构,负载均衡、数据库底层的水平拆分垂直拆分、读写分离、消息中间件、缓存、分布式事务处理等,我把它纳入到技术复杂性。
对于治理复杂性,则是一个系统上线完成后,你怎么样去运维、监控、观测它,以及如何应对软件系统后期的变更。在这个地方更多的解决思路首先是要做好底层的过程支撑,类似于研发过程管理、当前谈的比较多的DevOps的持续集成和持续交付,包括从资源到服务、从服务到应用的整体IT资源服务、应用链路监控,包括类似于混沌工程、面对可观测性的设计,都可以把它纳入到管控治理层面。
所以说,一个大型软件系统的复杂性一定不能从单一维度来看,它往往是业务、技术、管控治理三个维度的相互叠加,而且它不是简单的累加关系,它往往是一种相乘的关系。由于存在这三个维度的复杂性,再一相乘,整个复杂性就出现指数级的增长。
对于任何一个大型的软件系统或软件项目,我们都要从这三个维度去思考复杂性。类似于很多互联网的架构师,他们其实更多的是解决技术复杂性的问题,但到了很多B端的企业,去做大型的B端业务系统,如ERP、CRM、SCM系统的时候,往往做得并不是特别好。其中关键的一个原因就是他们期望通过技术复杂性去解决业务复杂性的问题,这显然是不正确的。
我们做任何的软件大项目或软件架构设计,一定要从业务、技术、管控治理三个方面充分意识到整个大软件系统软件项目的复杂性,这样你才能够把整个软件管理好、设计开发好。
今天的分享就到这里,希望对大家有所启发。再见。