这是我参与「第五届青训营 」伴学笔记创作活动的第 9 天
关于架构
Created: February 1, 2023 8:06 PM Tags: golang note
软件架构就是软件的基本结构,它是有关软件整体结构与组件的抽象描述,用于指导大型软件系统各个方面的设计。我们常常对架构产生困惑的很大一部分原因在于,对于什么是架构每个人往往都有自己的理解,而这些理解从某个角度来说都是成立的。因此我们在讨论架构是时候,往往需要结合自身对架构这一概念的认识,如果对架构概念理解不一样,那沟通起来自然不顺畅。
背景
对于软件开发而言,架构通常都是最关键的方面之一,架构对软件的开发质量、可维护性和可靠性都有着重要的影响。但什么才是好的架构?这基本上是软件工程中最有争议的一个话题,而且很多时候并没有一个明确的答案。如果我们去询问一些软件工程师“什么是好的软件架构?”,可能会听到这些回答;
好的软件架构是简单而优雅的。(这一普遍的回答,太过含糊,过于主观,并不能作为关于该问题的客观讨论和决策的适当依据。)
好的软件架构增加了可维护性。(这个答案稍好一些,但仍然无助于回答这个问题。如何衡量可维护性并不好回答)
好的软件架构具有高内聚性和松散耦合。(这是一个更好的答案,因为我们可以度量耦合,但耦合仍然是一个不可避免的现象,应该说多少耦合在这种情况下是可以接受的,或者多少内聚才算内聚过多)
好的软件架构正确地结合了已建立的设计模式。(是的,已经建立的实践、范例和模式确实有助于提出更好的设计,但我如何才能知道哪种范例最适合项目呢?或者我应该在多大程度上坚持一个特定的模式呢?)
这些答案要么过于主观,要么仅仅只是提出了一些更客观的工具,如范例、模式和度量标准,而没有任何客观指标来说明应该如何使用这些工具。在“什么是好的架构”这个核心问题上存在着如此多的主观性,因此,关于如何为项目设计好的架构的讨论很容易导致主观的矛盾,而这些矛盾可能永远不会得到客观的解决。
然而,如果我们后退一步,看看软件架构实际上是什么,它扮演着什么角色,使它成为软件开发中不可分割的一部分,也许我们可以指定更具体的、可量化的度量标准,以此作为我们评估各种架构选择和设计的基础。
什么是软件架构
框架与架构
为了理解软件架构,我们首先比较一下架构与框架的区别,框架是和架构比较相似的概念,且两者有较强的关联关系,所以在实际工作中,这两个概念有时我们容易分不清楚。我们可以参考维基百科上框架与架构的定义。
软件框架(Software framework)通常指的是为了实现某个业界标准或完成特定基本任务的软件组件规范,也指为了实现某个软件组件规范时,提供规范所要求之基础功能的软件产品。
其中的关键部分在于:
- 框架是组件规范。例如,MVC就是一种最常见的开发规范,类似的还有MVP、MVVM、J2EE等框架。
- 框架提供基础功能的产品。例如,Spring MVC是MVC的开发框架,除了满足MVC的规范,Spring提供了很多基础功能来帮助我们实现功能,包括注解(@Controller等)、Spring Security、Spring JPA等很多基础功能。
软件架构指软件系统的“基础结构”,创造这些基础结构的准则,以及对这些结构的描述。
单纯从定义的角度来看,框架和架构的区别还是比较明显的,框架关注的是“规范”,架构关注的是“结构”。框架的英文是 Framework,而架构的英文是 Architecture。
架构的目的
一般来说,特定代码库或项目的架构是隐式和显式的规则和约定,指导如何设计每个组件,以及如何与其他组件通信。这些规则可以影响到项目的任何方面,从它所基于的范例或框架,或者它被分解到的抽象层,到命名约定、文件夹和模块的结构,等等。它会影响哪些代码可以知道哪些其他代码元素的存在,或者这些元素应该在何时以何种方式相互通讯。
为了理解软件结构的目的,我们可以想象,如果项目没有软件架构的话会发生什么情况。假设一个项目没有任何一致同意的规则或约定的话,会怎么样?这些规则或约定用于建模和创建各种组件,或者指定它们之间的通讯方式。
如果有许多人要并行处理这个项目,但由于缺乏一致同意的约定和规则,每个人就会使用自己的约定和思维模式来编写自己的元素。如此一来,会导致不同的风格、模型、模式和 API,这不仅使贡献者更难处理另一个贡献者的代码,而且也使不同的元素和组件更难相互通讯,大大增加了开发时间,不管是在初始阶段还是后来的变更和迭代开发。
让我们想象一个具有特定架构的特定代码库。随着时间的推移,项目将会面临一些可能无法预料的变更和迭代,并且,面对任何这样的变化,会发生以下情况之一(或者多种组合):
- 该架构完全符合预期的更改,甚至没有指导性的更改。一切都很好,没什么可看的。
- 该架构没有说明任何预期更改的内容。有时候,开发人员可能会与同事讨论、决定和交流架构中必要的补充,而在其他时候,(例如,可能由于某个截止日期),他们可能只是根据与已建立的架构不一致的模型和指导方针来实现更改,从而导致代码库一致性的下降。
- 开发人员认为架构与预期的更改之间存在冲突,例如,因为它增加了大量的开销,或者由于变更本身非常简单而直接,从而使架构变得非常混乱。他们可能会选择尊重架构,并以更高的成本来实现更改,或者可能会选择打破原有模式,而这反过来又会引起代码库一致性的急剧下降。
在其中两个情况中,很有可能在更改之后,代码库的一致性会降低,并且随着时间的推移,这种不一致性还会增加,以至于看起来像是项目一开始就没有采用架构设计的样子。从实用主义的角度来看,我们现在可以将主要符合第一种情况的架构称为好的架构,而将导致第二种和第三种情况的架构,称为差的架构。
因此,根据我们前面讨论的内容,一个好的架构基本上应该:
- 尽可能多地考虑未来的变化。
- 让这些更改对开发人员来说更容易,而不是更困难。
当然,第一个并不是一个真正可以衡量的指标,但它确实为我们提供了一个切实可行的方向。试着设想未来可能的变化,例如,设想项目的未来阶段,堆栈的某些部分可能会因为外部需求而被替换掉,等等,并思考我们的架构决策和设计将如何面对这些变化。
但是,第二个则带来了更多可量化的指标。从表面上看,“容易”和“困难”似乎都是主观的术语,没有合适的方法来衡量它们。然而,任何代码库的改变最终都是人(程序员)和计算机(键盘)交互的结果,幸运的是,我们已经有一个专门用于测量这种交互难易程度的计算机科学领域,这个领域称为人机交互(Human-Computer Interaction,HCI)。
对于那些不熟悉该领域的人来说,人机交互是一个致力于尽可能量化的方式分析人机交互各个方面的领域。在这些方面中,最重要的是难度(更确切地说,是任何给定交互的难度指数)。对于许多基本的交互,我们能够计算这个指数,事实上我们已经这样做了,这就是为什么窗口会有“最小化”、“最大化”、“关闭”的按钮,如果你正在电脑上阅读本文的话,你会在屏幕的角落看到这些按钮(基本上,角落会导致与难度指数成反比关系的参数大幅增加)。非常方便的是,难度指数也与执行任务所需的时间(一系列人机交互)成线性关系,在我们的用例中,这可以转化为任务本身的字面成本。
这一切意味着什么?简单地说:
💡 好的架构降低了未来更改的成本。如何评估特定架构决策的成本?首先,想象一下可能要进行的一些更改,例如,你可能希望在上面提到的项目的下一阶段添加特性,或者甚至对一些随机选取的方法 / 函数 / 类的签名进行随机更改。然后评估实现这一更改可能包括的一系列交互,从真正基本的交互(例如需要键入多少字符),到开发人员可能需要查看代码的其他部分,再到某些特定概念或函数的可理解性。你可以更精确地估计底层级别的交互难度(使用一些基本的人机交互规则),或者根据它们的难度假定任意的常数,然后大致估计更高级别的交互难度(或许要去掉那些过于抽象的交互),那么你就可以很好地估算出这些潜在变化的难度(以及成本)了。