2.3 软件架构的基本概念
软件架构定义了软件系统的基本结构、整体技术概念和设计决策,是整个系统开发的基础。因此,它可以被视为一个可持续地简化复杂和大规模软件开发的建设计划。软件架构不指定详细设计,而是描述了从外部对系统提出的需求到最终构建的完整系统(例如以程序文件的形式)的解决方案的建设性路径。在此过程中,应尽可能记录设计决策的原因,因为特定架构的选择对质量属性以及诸如可维护性、可扩展性和性能等非功能特性有重大影响。 尽管软件架构很重要,但它仍然是一个年轻的学科。因此,本章对软件架构的基本概念进行了一般性介绍。首先,我们基于“构建模块”和“接口”这两个术语来定义对“软件架构”一词的理解,这两个术语也将被引入。这为我们描述软件架构的用途和能产生的好处提供了基础。然后,用描述软件架构的概念来完善这一内容。
2.3.1 什么是软件架构 ?
对于软件架构,没有一个统一的、被普遍接受的定义。作为对软件架构众多定义的介绍,这里有一个虽独特但恰当的定义:
软件架构是一个变革的框架。
卡内基梅隆大学的软件工程研究所(SEI)在为此专门创建的网站上收集了超过 150 个软件架构的定义[SEI Def]。在这些已制定的定义中,可以越来越多地确定一个我们认可的共识。这也反映在 IEEE 标准 42010:2011《软件密集型系统架构描述的推荐实践》所提供的定义中:
<系统> 系统在其环境中的基本概念或属性,体现在其元素、关系以及设计和演化的原则中。
在本章范围内,我们不为这个核心术语引入一个全新的定义,而是以这个标准为导向,但在一些地方进行补充,例如使用“接口”这个术语以及提及开发组织,因为我们认为这些对于软件架构是至关重要的。
软件架构定义了系统组织及其结构分解为构建模块和接口的基本原则和规则,以及它们彼此之间和与周围环境的关系。因此,它为整个软件生命周期、开发人员以及软件的运营者定义了指导方针,涵盖从分析到设计、实现,再到运营和增强。
对软件架构的这种理解包括两个重要方面。首先是建设性方面,它指定了软件密集型系统的结构及其分解为构建模块和接口,以及它们彼此之间和周围环境的关系。然而,该定义还包括关于流程的第二个方面。软件架构还影响开发人员和系统生命周期,因此指定了必须遵守的原则和规则。
这也意味着软件架构目标也可以是长期目标,超越项目目标及其时间范围,这通常与开发项目的持续时间相关。因此,软件架构也可以代表并包括对整个系统生命周期的投资,这可能只有在相关开发项目完成后才能摊销。
2.3.2构建模块、接口和配置
现在我们来看看软件架构的系统构建方面。将介绍“构建模块”和“接口”这两个术语以及这些元素之间的关系。 “接口”和“构建模块”是基本的工程术语。它们在信息技术中也很常见,但尽管几乎每天都在使用,对于什么是接口或构建模块,并没有一个共同的、精确的理解。 当电气工程师、机械工程师和计算机科学家同时参与一个系统开发项目时,就可以看出并非每个人对接口和构建模块的理解都是相同的。例如,如果您试图与所有相关方就待开发的机床的接口达成一致,您很快就会发现对于什么是接口以及什么不是接口存在完全不同的概念。所以在这个阶段,我们希望将“接口”一词定义如下:
接口代表了系统或其构建模块的一个定义明确的访问点。在这种情况下,接口描述了这个访问点的特征(例如,属性、数据和功能)。目的是尽可能精确地定义这些特征,包括所有必要的方面,如语法、数据结构、功能行为、错误行为、非功能特征、接口使用日志、技术、协议、访问修饰符、文件格式、条件/约束和语义。
接口的这个定义清楚地表明,接口的全面规范可能极其费力。像 Java 或 C#这样的编程语言包含接口概念,通常可以用它们定义语法(接口的名称)、提供的方法及其参数和返回值。接口的其他方面,例如其功能行为,必须记录在额外的文档中。
我们经常在程序中发现不充分的接口描述。例如,如果您查看 Java 中的 Collection 接口,它似乎准备得非常好并且有详细的文档。另一方面,对于将元素插入 Collection 的 Insert 操作的性能(例如,上限和下限,或插入一个元素允许的平均时间)没有说明。
然而,对于此接口的使用,这个特性可能非常相关,特别是在处理大量元素的处理器密集型任务中。在决定是否应该使用 Java Collection ,还是必须找到替代解决方案时,这些特性至关重要。
在这种特殊情况下,程序员因此必须了解接口的具体实现并选择适当的一个。在这种情况下,他可以选择 ArrayList 或 LinkedList ,它们具有不同的性能特征。因此,尽管这是其定义的任务,但接口并未完全封装实现。
这个例子说明通常不可能创建完整的接口描述。更多时候是由架构师来决定在接口的哪些方面必须进行描述,哪些在有疑问的情况下可以忽略。然而,在可能的情况下,您仍应尝试创建完整的接口描述,并包含特定项目背景下的相关特征。
现在我们可以探讨“构建模块”这个术语。“构建模块”这个术语经常被用作“组件”的同义词。然而,“构建模块”是一个通用术语,而“组件”是构建模块的一种特殊形式。在这方面,我们有意不使用“组件”这个术语,因为它常常被理解为其他含义。有些人认为组件主要是指 UML 组件,而另一些人则将组件与编程结构(如包或 JavaBeans)联系起来。
我们使用通俗的术语“构建模块”,从编程语言、建模方法和设计方法中使用的众多术语中抽象出软件架构的组件元素。在我们的上下文中,构建模块是特殊编程结构或描述元素的抽象。
构建模块是构建软件架构静态结构的核心基本元素。它包括最终代表源代码抽象的所有软件或实现工件。这从小型构建模块(如函数或类),到中型构建模块(如包或库),再到大型构建模块(如子系统、层或框架)都有。因此,构建模块可以以不同的方式表现出来。图 2 - 4 展示了一些特定类型构建模块的示例。这里需要注意的是,构建模块本身可以由其他构建模块组成。

图2-4构建模块的例子
“构建模块”这个术语因此是软件架构中最重要的术语之一。然而,为了划定界限,它需要明确的标准。什么是构建模块,什么不是?我们对构建模块的定义包括下面指定的三个基本特征,因此广泛涵盖了相关文献([Szy 98],[D'SW98],[RQ + 12])中的主要定义。
构建模块提供它在合同意义上保证的接口。然而,这种保证仅在相应配置范围内提供其所需的接口时适用。(提供和所需接口) 通过提供和所需的接口,构建模块封装了这些接口的实现。因此,它可以被提供并在适当情况下也需要相同接口的其他构建模块所替换。(封装和可互换性) 构建模块也是软件密集型系统分层(分解)的单位。换句话说,一个构建模块可以使用其他(子)构建模块及其相互关系的适当配置来实现。在这种情况下,我们也说这个(超级)构建模块封装了(子)模块。构建模块还可以将外部接口委托给内部接口,反之亦然。这就是构建模块之间关系的定义方式。(配置和分层(分解))

图2-5The构建模块和接口之间的关系
请注意:由于可能的副作用,在替换构建模块时,也必须考虑所需的接口。构建模块是一个可重用的组件。关于构建模块的周围环境和构建模块所需接口的存在等进一步的假设应保持在最低限度,并明确记录。
“构建模块”这个术语现在已经解释过了,构建模块和接口之间可能的关系也已经定义。构建模块的基本特征也已确定如下:提供和所需的接口、封装和可互换性、配置和分层(分解)。图 2 - 5 说明了这些术语及其相互关系。
如图 2 - 6 所示,我们可以区分构建模块的不同视图:
在黑箱视图中,我们只看到构建模块提供和所需的接口。这是构建模块用户看到的视图。此视图遵循信息隐藏原则——换句话说,它隐藏了构建模块的(私有)内部细节。例如,黑箱视图可以使用 UML 组件图来描述。
灰箱视图显示了构建模块所需的哪些其他的、大多是技术接口——例如,配置接口或所需和使用的运行时环境接口[BW97]。灰箱视图可以使用 UML 部署图来描述。
白箱视图(也称为玻璃箱视图)提供了构建模块内部细节的视图——换句话说,它展示了其分解为子构建模块的配置或不同类型的实现。此视图还显示了其提供和所需接口到构建模块内部工作的委托。这是构建模块的实现者看到的视图。例如,白箱视图可以使用 UML 组合结构图来描述。

图2-6Black盒子,灰色盒子,盒和白盒视图
架构及其构建模块的分层(分解)在黑箱、灰箱和白箱之间的相互作用中变得尤为清晰。如图 2 - 7 所示,构建模块 A 的黑箱视图可以在其下方的白箱视图中进行分层分解。在这个白箱视图中,构建模块 A 被分解为组件元素 B1、B2 和 B3。 请注意:元素 b1、b2 和 b3 不是构建模块,而是使用构建模块实例的占位符(在 UML 中也称为“部件”)。我们也将这些占位符称为构建模块实例,或者如果没有重要区别,简称为构建模块。对于子构建模块实例 b1 到 b3,在构建模块 A 的配置中也有构建模块实例的占位符。然而,并非所有的构建模块实例都可以被占位符 b1、b2 和 b3 使用,因为占位符具有特定的类型。占位符与变量类似,它们具有值(= 构建模块实例)和类型(= 构建模块)。因此,占位符“b1:构建模块 B1”只能使用构建模块 B1 的实例。 由于此构建模块实例在配置中定义了构建模块类型,因此此构建模块也有一个黑箱视图。 这里需要注意的是,该图在架构描述的上下文中仅显示了一个层次结构。因此,一个构建模块的实例可以在其他构建模块的多个配置中使用,在它们的白箱视图中可见。构建模块也可能在不同的层次级别上使用。在图 2 - 7 中,构建模块 B1 作为构建模块实例出现在白箱视图的配置中,既是 A 的子构建模块实例,也是 B2 的子构建模块实例。B1 可能是一个 XML 解析器构建模块,在多个不同级别和广泛的不同构建模块中使用。

图2-7Hierarchical (de)与黑盒和白盒视图组成
还应当注意的是,这种分层分解不仅适用于构建模块,也适用于它们的接口。换句话说,如果一个构建模块在灰箱中有一个到另一个构建模块的接口,那么在黑箱和白箱级别也存在相应的接口,并且自然必须相应地实现——例如,通过将接口委托给子构建模块。

图2-8Who定义了接口和接口协议好吗?
接口用于连接构建模块。无论构建模块是提供接口还是需要接口,两个构建模块都必须遵守接口协议。这在接口本身中定义。但是谁来定义接口呢?这里有不同的可能性:
标准接口
此接口由外部第三方定义。提供和请求的构建模块都要遵循它。
提供接口
在这种情况下,接口由提供它的构建模块定义。除了标准接口,这是最常用的接口类型。
所需接口
在这种情况下,接口由需要它的构建模块定义。这种配置常在框架中发现。通过这些类型的接口,您可以将具有特定功能的构建模块合并到程序结构中。
独立的接口
在这种情况下,提供接口的构建模块和需要接口的构建模块都定义了自己的接口。这增加了构建模块的解耦性,它们可以独立开发和测试。然而,这意味着接口随着时间的推移可能不保持相同,因此必须在它们之间放置一个适配器。
每种接口定义类型都有其特定的特点,因此也有优点和缺点。接口定义类型的最终变体增加了构建模块的解耦性,但需要以增加开发工作量和延长时间为代价。然而,这种变体仍然可以被视为一种合理的长期解决方案——例如,在集成任务的情况下。 另一方面,这种变体也可以临时使用,以在开发过程中确保最大的并发性,同时统一不一致的开发。然而,在这种情况下,您需要为架构的重构和重新设计预留额外的时间,以便能够移除适配器,并达成新的通用接口协议。否则,临时解决方案将无意中成为永久解决方案。
2.3.3描述软件架构的概念
无论架构是明确形成还是隐性形成,如果没有被记录下来,其作用都是有限的。只有经过适当记录的架构才能持续地被交流、讨论和进一步发展。 软件架构不仅要与其他架构师讨论。软件架构的所有方面都要向不同利益代表(利益相关者)展示、与他们讨论,并共同进一步发展。例如,客户和用户也可以参与影响他们的架构决策。开发人员也应该参与讨论,特别是对于与最终实现相关的架构方面的交流和讨论。 根据 IEEE 标准 42010:2011,即《软件密集型系统架构描述的推荐实践》[IEEE 42010:2011],软件架构描述包含一系列用于描绘软件架构的工件。相应的标准定义了架构描述的概念模型。图 2 - 9 展示了概念模型中我们特别感兴趣的部分。

图2-9根据IEEE 42010:2011的概念模型的核心要素
在模型的这段摘录中,一个系统受其环境的影响,反之亦然。每个系统都有一个架构。根据 IEEE 标准,这个架构由一个单一的架构描述来描述。乍一看,这似乎是一种限制。然而,如上所述,该标准将架构描述定义为由一系列工件组成。这意味着一个架构是由一系列描述来记录的。这一点在标准中本可以(也应该)表述得更清楚。
一个系统也有许多利益相关者,而利益相关者又有许多关注点。架构描述解决了利益相关者的关注点,并在相关的基本原理中用它们来证明所做的架构决策是合理的。 在这方面,该标准涵盖了许多软件架构方法的一个广泛的核心概念,即:架构描述包括架构的视图。视图的概念如图 2 - 10 所示。根据他的观点,利益相关者对架构有不同的看法。这些观点是由每个利益相关者自己的特定关注点所驱动的。

图2-10Views预测的软件架构。
IEEE 标准描述了多个“视图”,包括“功能视图”、“物理视图”和“技术视图”,这些通常被称为单独的架构或架构级别——例如,“功能架构”或“功能级别”。
架构观点 架构观点是用于构建、解释、使用和分析一种类型的架构视图的一组约定。观点包括模型种类、观点语言和符号、建模方法和分析技术,以框定一组特定的关注点。观点的示例有:操作、系统、技术、逻辑、部署、流程、信息。 架构视图 在 AD 中的架构视图从一个或多个利益相关者的角度表达了所关注系统的架构,以解决特定的关注点,使用其观点所建立的约定。一个架构视图由一个或多个架构模型组成。
在其他架构方法中——例如,Kruchten 的 4 + 1 架构模型[Kru95],视图是由不同图表中的不同描述元素所驱动的。例如,在 Kruchten 的逻辑视图中,重点是构建模块及其关系,而流程视图描述了流程以及它们之间的信息交换。 此外,相关文献经常提到许多不同的架构或架构级别。这些包括业务流程架构、IT 架构、功能架构级别、技术架构、技术基础设施架构、部署架构级别和许多其他术语(例如参见[EH + 08],[Sie03])。 Siedersleben 的“血型”[SD00]在这种术语混乱中提供了一种令人耳目一新的原始区分。以应用架构层(血型 A)和技术架构层(血型 T)为例。就像血型一样,架构层的混合是不可取的。在 Zachmann 框架[O'RF + 03]中可以找到另一种类型的区分,其中架构级别根据角色和视角在两个维度上进行区分。 总之,可以说架构、架构层和视图这些术语在描述架构时使用得过于频繁。在此背景下,我们为软件架构的描述定义了一个简单的概念模型(见图 2 - 11)。

图2-11软件架构描述的概念模型
根据这个定义,一个架构描述由一系列的架构级别组成。一个架构级别将多个视图组合在一起,形成一个有意义的描述元素。视图包括文本和图表,它们又被存储在模型中。
例如,一个功能架构级别可以同时包含一个由一系列静态图表组成的静态视图(参见 4.3.5 节中的构建模块视图),以及一个由一系列行为图表组成的动态视图(参见 4.3.6 节中的运行时视图)。然而,额外的自由文本描述对于记录和理解架构至关重要。此外,已经提到的架构决策的基本原理构成了这段文本的一部分。有些级别完全由自由文本组成。
2.3.4 架构描述与架构级别
正如已经解释的那样,架构在不同的级别上进行描述。这些级别选择和组织为相关的设计方法提供了一个初始参考点。架构级别经常出现在不同的抽象层次上——例如,高层次的“面向服务的分层架构”风格,或者更具体的包括功能实体和服务的功能架构级别。
对于架构方法及其相关的描述方法,有大量不同的方法和标准。其中一些——如 TOGAF®、RM-ODP 和 Zachman 框架——在第 4 章中介绍。然而,如图 2 - 12 所示,有一个基本原则是所有这些方法所共有的。所有这些架构方法中的方法论都是基于将相关的描述和细化方法在两个维度上进行分离。在视角维度上,处理不同的架构领域(如数据、流程、服务和程序组织)。在抽象程度维度上,这些不同的架构领域被逐步详细地处理和描述。一旦您理解了这种方法,在这些架构框架中导航就会相对容易。
例如,图 2 - 12 展示了将架构描述细分为四个架构级别:架构风格、技术基础设施、功能应用架构级别(也称为 A 架构)和技术架构级别(T 架构)。如下所示,这四个级别在两个维度上有所不同——换句话说,在抽象级别和视角(功能与技术)方面。

图2-12架构描述中的不同层次
正如级别之间的箭头所示,架构级别可以单独处理,尽管它们相互影响,因此相互依赖。更具体的功能和技术架构级别自然涉及到架构风格和技术基础设施的要求。在一个方向上,功能和技术架构级别的具体设计影响更高架构级别的要求。在另一个维度中也可以找到类似的关系。例如,技术架构为持久性或事务管理提供了特定的概念,这些反过来又必须包含在功能架构级别中。在另一个方向上,功能架构级别提供了所需持久性概念方面的输入。
这里的架构风格是系统的核心架构隐喻。例如:“我们的软件系统构建为三层架构,在表示层使用模型 - 视图 - 控制器,在数据管理层使用对象 - 关系映射。”另一方面,技术基础设施定义了架构的网络配置。例如:“我们有一个带有网络和应用容器以及关系数据库的简单客户端。”
功能和技术架构级别的基于视图的描述发生在更详细的架构级别(也参见 [SD00])。在功能架构级别,为实现功能需求设计适当的应用构建模块及其关系。例如,可以在此为通用保险产品模型创建设计,以便在应用中映射不同的保险产品。
相比之下,在技术架构级别,基于非功能需求为相关方面设计并记录跨学科的解决方案构建模块。例如,可能需要对所有功能实体进行版本控制,并在技术架构中为此开发解决方案。然后,应用架构级别的功能实体可以使用来自技术架构级别的这个通用解决方案。
2.3.5 软件架构与环境之间的交互
如图 2 - 12 所示,系统的架构并非在真空中创建,而是受到周围环境的影响,反之亦然。实际软件架构周围的元素通常也被称为架构——例如,业务流程架构。在这里我们希望避免使用“架构”这个术语,以防止其过度使用。相反,我们越来越多地使用“环境”或“景观”这些术语。图 2 - 13 展示了软件架构背景下的关键周边区域以及其中涉及的角色: • 项目环境和项目管理 项目环境和项目管理提供了各种约束和项目目标,在与架构相关时必须予以考虑。例如,预算和开发方法可能会影响软件架构,反之亦然。在系统的周边环境中,通常有许多应用程序和项目。这种现有的(并且不断变化的)项目和应用程序景观通常对正在开发的软件架构有重大影响。如果额外的项目或应用程序启动或突然终止,这可能对要为其开发软件架构的系统的接口产生巨大影响。 • 产品管理和需求工程 产品管理和需求工程对系统提出功能需求,特别是非功能需求、质量目标和约束,所有这些在项目过程中都可能发生变化,从而影响架构。另一方面,架构也可以识别哪些需求与其他需求或项目约束产生利益冲突。因此,架构也可以为需求的变更产生推动力。 • 执行平台和运营 一般来说,执行平台和运营组织已经在组织内存在。新系统应尽可能使用现有系统,这在架构设计期间应予以考虑。另一方面,架构也可能产生新的平台和操作需求。 图2-13Software架构受其环境的影响,反之亦然。

图2-13Software架构受其环境的影响,反之亦然。
• 工具和开发环境 架构最终必须得以实现。这需要合适的工具和开发组织。还必须为所选的编程语言、框架和技术提供适当的开发环境。同样,架构本身也可能对工具和开发组织提出新的要求。例如,根据所选的技术,可能需要扩展测试基础设施。
2.3.6软件架构的质量和价值
但是,什么时候开发或提供的软件架构才是一个好的软件架构呢?[BCK03]将一个好的架构定义为在魔法矩形(即成本、时间、功能和质量)的背景下,同时考虑到生命周期,能够使项目和系统实现其目标的架构。与所有质量属性一样,架构的质量是主观的,取决于评估它的人[ISO/IEC 25010]。 换句话说,软件架构只是在一定程度上适用于指定的目标、约束、需求和未来的挑战。软件架构的外部感知质量只能在特定质量目标的背景下进行评估。所需的质量特征来自当前和未来的目标、约束和需求,因此对于所讨论的各个软件系统是特定的。 然而,当前和未来的目标、约束和功能需求(特别是非功能需求)往往没有得到充分和完整的描述。在这方面,值得看一看 ISO 标准 25010 [ISO/IEC 25010],其中已经定义了高级质量特征:
• 功能的适用性 • 可靠性 • 可用性 • 性能效率 • 安全 • 可维护性 • 兼容性 • 可移植性 这些质量属性是为软件架构和软件系统推导其他质量特征的良好起点。此外,还可以使用 FURPS 属性(另见第 2.4 节和第 5 章)检查现有的目标、约束和需求以及从中派生的所需质量特征的完整性。
除了明确制定的目标、约束和需求(从中得出所需的质量特征)之外,每个项目还具有各种隐含的目标、约束和需求。在检查所需质量特征的完整性时,这些隐含(且仍然隐藏)的质量特征必须在可能的情况下完全明确。例如,可能有一个隐含的条件,即要使用特定的数据库管理系统,因为它是公司的标准系统,已经支付了许可证费用,并且数据库的操作得到了保证。关于上市时间的不明确约束也可能导致在增量方法支持方面的隐含架构要求。这些隐含的目标、约束和需求必须明确,并且必须从中得出必须实现的质量特征。这是确保它们在设计的架构中得到考虑和实现的唯一方法。
除了用户直接感知到的外部质量外,用户无法直接看到的质量也起作用。例如,在现代车辆的产品设计中,为了简化未来贵金属或稀土的回收,会设计和安装组件。这些质量特征对车主来说并不明显,但可能间接地体现在车辆的价格上。
对于用户来说并非立即感知到的质量目标也与软件架构固有地定义在一起,与特定的软件密集型系统无关。如果在系统开发过程中将软件架构定义为核心价值,那么就会隐含一系列质量目标。例如,软件架构必须易于理解、透明、具有最新的文档并且正确实现。架构必须支持轻松的开发和结构化的项目组织。它还应该确保系统操作简单、经济、可扩展和可维护。出于质量和经济原因,都希望最大程度地重用现有的构建模块。
Last updated