7 可集成性

第7章 可集成性

融合是生命的基本法则;当我们抗拒融合时,无论是内心还是外在,瓦解都将是必然的结果。因此,我们通过融合领悟到了和谐的概念。

—— 诺曼・卡曾斯(Norman Cousins )

根据韦氏词典的解释,形容词 “integrable” 意为 “能够被集成的”。我们给你一点时间缓口气,消化一下这个深刻的见解。但对于实际的软件系统而言,软件架构师需要关注的不仅仅是让分别开发的组件协同工作;他们还需要考虑预期的以及(在不同程度上)未预期到的未来集成任务的成本技术风险。这些风险可能与进度安排、性能或技术相关。

对集成问题的一种通用的抽象表述是,一个项目需要将一个软件单元C,或者一组单元C₁、C₂、……Cₙ集成到一个系统S中。S可能是一个平台,我们要将 {Cᵢ} 集成到这个平台中;或者它也可能是一个已经包含了 {C₁、C₂、……、Cₙ} 的现有系统,而我们的任务是为集成 {Cₙ₊₁、……Cₘ} 进行设计,并分析其成本和技术风险。

我们假定我们能够对S进行控制,但 {Cᵢ} 可能不在我们的控制范围之内 —— 比如由外部供应商提供,所以我们对每个Cᵢ的了解程度可能会有所不同。我们对Cᵢ的了解越清晰,设计就会越有能力,分析也会越准确。

当然,S并不是一成不变的,它会不断发展演变,而这种演变可能需要重新进行分析。可集成性(就像可修改性等其他质量属性一样)具有挑战性,因为它涉及在我们可利用的信息不完整的情况下为未来做规划。简而言之,有些集成工作会比其他的更简单,因为在架构设计中已经预见到了这些集成并做好了相应的安排;而有些集成工作则会更复杂,因为在架构设计中没有预见到它们。

考虑一个简单的类比:要将一个北美插头(Cᵢ的一个例子)插入北美插座(电力系统S提供的一个接口),这种 “集成” 轻而易举。然而,要将北美插头插入英式插座,就需要一个转换插头。而且带有北美插头的设备可能只能在 110 伏的电压下运行,那么在它能在 220 伏的英式插座中使用之前,还需要进一步的适配。此外,如果该组件设计为在 60 赫兹的频率下运行,而系统提供的频率是 70 赫兹,那么即使插头能够正常插入,该组件也可能无法按预期运行。SCᵢ的设计者所做出的架构决策 —— 比如,是否提供插头转换器或电压适配器,或者是否让组件在不同频率下都能正常运行 —— 将会影响集成的成本和风险。

7.1 评估架构的可集成性

集成难度(即成本和技术风险)可以被看作是 {Cᵢ} 与S的接口规模以及它们之间 “距离” 的函数:

规模指的是 {Cᵢ} 与S之间潜在依赖关系的数量。

距离指的是在每一个依赖关系中解决差异的难度。

依赖关系通常从语法层面进行衡量。例如,如果模块 A 调用组件 B、从 B 继承,或者使用 B,我们就说模块 A 依赖于组件 B。不过,尽管语法层面的依赖关系很重要,并且在未来也依然重要,但依赖关系还可能以任何语法关系都无法检测到的形式出现。两个组件可能会在时间层面产生耦合,或者通过资源产生耦合,因为它们在运行时共享并争夺有限的资源(比如内存、带宽、中央处理器),共同控制一个外部设备,或者存在时间上的依赖关系。又或者它们可能会在语义层面产生耦合,因为它们共享相同协议、文件格式、度量单位、元数据或其他某些方面的知识。区分这些不同类型的依赖关系之所以重要,是因为时间和语义层面的依赖关系往往没有得到充分的理解、明确的认知,也没有被恰当地记录下来。对于一个大型且长期运行的项目来说,缺失的或隐含的知识始终是一种风险,而这些知识缺口必然会增加集成以及集成测试的成本和风险。

想想如今计算领域中服务和微服务的发展趋势。这种方法从根本上来说是为了将组件解耦,以减少它们之间依赖关系的数量和距离。服务之间仅通过已发布的接口相互 “了解”,而且如果该接口是一个合适的抽象,那么对一个服务的修改就不太可能对系统中的其他服务产生连锁反应。组件之间不断增强的解耦是一个已经持续了数十年的行业趋势。面向服务本身仅处理(即减少)了依赖关系在语法层面的问题;它并没有涉及时间或语义层面的问题。那些本应解耦但却对彼此有详细了解并对彼此做出假设的组件,实际上耦合度很高,未来对它们进行修改很可能代价高昂。

为了评估可集成性,“接口” 绝不能仅仅被理解为简单的应用程序编程接口(API)。接口必须描述元素之间所有相关的依赖关系。在尝试理解组件之间的依赖关系时,“距离” 这个概念很有帮助。当组件相互交互时,它们在如何成功协作以完成交互方面的契合度有多高呢?距离可能意味着:

  • 语法距离。协作的元素必须就所共享数据元素的数量和类型达成一致。例如,如果一个元素发送的是整数,而另一个元素期望接收的是浮点数,或者对数据字段中的位的解释不同,这种差异就构成了必须弥合的语法距离。数据类型的差异通常很容易观察和预测。比如,这类类型不匹配的问题可能会被编译器检测到。位掩码的差异虽然本质上类似,但往往更难检测,分析师可能需要依靠文档或仔细检查代码来识别这些差异。
  • 数据语义距离。协作的元素必须就数据语义达成一致;也就是说,即使两个元素共享相同的数据类型,它们对数据值的解释也可能不同。例如,如果一个数据值表示的海拔高度单位是米,而另一个表示的海拔高度单位是英尺,这就构成了必须弥合的数据语义距离。这类不匹配的问题通常很难观察和预测,不过,如果涉及的元素使用了元数据,分析师的工作会稍微轻松一些。如果有接口文档或元数据描述,数据语义方面的不匹配问题可以通过比较这些文档或描述来发现;如果有代码,也可以通过检查代码来发现。
  • 行为语义距离。协作的元素必须就行为达成一致,尤其是在系统的状态和模式方面。例如,在系统启动、关闭或恢复模式下,对一个数据元素的解释可能会有所不同。在某些情况下,这些状态和模式可能会在协议中被明确记录下来。再比如,Cᵢ和Cⱼ可能会对控制做出不同的假设,比如它们都期望对方发起交互。
  • 时间距离。协作的元素必须就时间方面的假设达成一致。时间距离的例子包括以不同的速率运行(比如,一个元素以 10 赫兹的速率发送值,而另一个元素期望以 60 赫兹的速率接收值),或者做出不同的时间假设(比如,一个元素期望事件 A 在事件 B 之后发生,而另一个元素期望事件 A 在事件 B 之后发生,且延迟不超过 50 毫秒)。虽然这可以被看作是行为语义的一个子情况,但它非常重要(而且往往很微妙),所以我们将其单独列出。
  • 资源距离。协作的元素必须就共享资源方面的假设达成一致。资源距离的例子可能涉及设备(比如,一个元素需要独占对某个设备的访问权,而另一个元素期望共享访问权)或计算资源(比如,一个元素需要 12GB 内存才能达到最佳运行状态,另一个元素需要 10GB 内存,但目标中央处理器只有 16GB 物理内存;或者三个元素同时以每秒 3 兆比特的速率生成数据,但通信信道的峰值容量只有每秒 5 兆比特)。同样,这种距离可以被视为与行为距离相关,但应该有意识地对其进行分析。

在编程语言的接口描述中,通常不会提及这些细节。然而,在组织层面,这些未明确说明、隐含的接口常常会给集成任务(以及修改和调试任务)增加时间成本和复杂性。这就是为什么接口是架构层面需要关注的问题,我们将在 第 15 章 中进一步讨论。

从本质上讲,可集成性在于识别并弥合每个潜在依赖关系中元素之间的距离。这是一种为可修改性做规划的形式。我们将在 第 8 章 中再次探讨这个话题。

7.2 可集成性的通用场景

表 7.1 展示了可集成性的通用场景。

表 7.1 可集成性的通用场景

场景部分描述可能的值
来源触发从何而来?来自以下一种或多种情况:
  • 任务 / 系统的利益相关者
  • 组件市场
  • 组件供应商
触发事件触发事件是什么?也就是说,这里所描述的是哪种类型的集成?以下情况之一:
  • 添加新组件
  • 集成现有组件的新版本
  • 以新的方式将现有组件集成在一起
构件系统的哪些部分会参与到这次集成中?以下情况之一:
  • 整个系统
  • 特定的一组组件
  • 组件元数据
  • 组件配置
环境当触发事件出现时,系统处于何种状态?以下情况之一:
  • 开发阶段
  • 集成阶段
  • 部署阶段
  • 运行阶段
响应一个“可集成的”系统将如何对这种触发事件做出反应?以下一种或多种情况:
  • 变更已 {完成、集成、测试、部署}
  • 新配置中的组件能够成功且正确地(在语法和语义层面)交换信息
  • 新配置中的组件能够成功协作
  • 新配置中的组件不会超出任何资源限制
响应度量如何衡量这种响应?以下一种或多种情况:
  • 成本,依据以下一种或多种因素来衡量:
  • 变更的组件数量
  • 代码变更的百分比
  • 变更的代码行数
  • 工作量
  • 资金花费
  • 日历时间
  • 对其他质量属性响应度量的影响(用于考量可接受的权衡情况)

图7.1 展示了一个根据通用场景构建的可集成性示例场景:组件市场中出现了一个新的数据过滤组件。该新组件被集成到系统中,并在1个月内完成部署,所需工作量不超过1个人月

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图7.1 可集成性示例场景

7.3 可集成性策略

可集成性策略的目标是降低添加新组件、重新集成已更改组件以及将多组组件集成在一起以满足系统演进需求的成本和风险,如 图 7.2 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7.2 可集成性策略的目标

这些策略通过减少组件之间潜在依赖关系的数量,或者缩小组件之间预期的 “距离” 来实现上述目标。图 7.3 展示了可集成性策略的概述。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 7.3 可集成性策略

限制依赖关系

封装

封装是所有其他可集成性策略的基础。因此,它很少单独出现,但其作用隐含在本文描述的其他策略之中。

封装为一个元素引入了一个明确的接口,并确保对该元素的所有访问都通过这个接口进行。对元素内部的依赖关系被消除了,因为所有的依赖关系都必须通过该接口。通过减少依赖关系的数量或缩短它们之间的 “距离”,封装降低了一个元素的变更传播到其他元素的可能性。然而,由于接口限制了外部职责与该元素交互的方式(可能通过一个包装器),这些优势会有所削弱。因此,外部职责只能通过暴露的接口与该元素进行直接交互(间接交互,比如对服务质量的依赖,可能不会改变)。

封装还可以隐藏与特定集成任务不相关的接口。一个例子是某个服务使用的库,它可以对所有使用者完全隐藏,并且在进行更改时,这些更改不会传播给使用者。

因此,封装可以减少依赖关系的数量,以及CS之间在语法、数据和行为语义方面的 “距离”。

使用中介

中介用于打破一组组件 Cᵢ 之间,或者 Cᵢ 与系统 S 之间的依赖关系。中介可用于解决不同类型的依赖关系。例如,诸如发布 - 订阅总线、共享数据存储库或动态服务发现之类的中介,通过消除数据生产者和消费者双方了解对方身份的必要性,减少了它们之间的依赖关系。其他中介,如数据转换器和协议翻译器,则用于解决语法和数据语义方面的 “距离” 问题。

要确定特定中介的具体优势,需要了解该中介的实际功能。分析师需要判断中介是否减少了组件与系统之间的依赖关系数量,以及它能解决哪些方面的 “距离” 问题(如果有的话)。

中介通常在集成过程中引入,以解决特定的依赖关系,但也可以将其纳入架构中,以提高针对预期场景的可集成性。在架构中包含一个像发布 - 订阅总线这样的通信中介,然后将传感器与外界的通信路径限制在该总线上,这就是一个使用中介来提高传感器可集成性的例子。

限制通信路径

此策略限制了给定元素可以与之通信的元素集合。在实践中,该策略通过限制元素的可见性(当开发人员看不到某个接口时,他们就无法使用它)和授权(即只允许访问经过授权的元素)来实现。在面向服务的架构(SOA)中可以看到限制通信路径的策略,在这种架构中,不鼓励点对点的请求,而是强制所有请求都通过企业服务总线,以便能够一致地进行路由和预处理。

遵循标准

系统实现中的标准化是实现跨平台和跨供应商的可集成性与互操作性的主要推动因素。标准在其规定的范围方面差异很大。有些标准侧重于定义语法和数据语义。其他标准则包含更丰富的描述,例如那些描述包含行为和时间语义的协议的标准。

标准在其适用范围或采用程度上也有所不同。例如,由被广泛认可的标准制定组织发布的标准,如电气和电子工程师协会(IEEE)、国际标准化组织(ISO)和对象管理组织(OMG)发布的标准,更有可能被广泛采用。一个组织内部的惯例,特别是如果有详细的文档记录并且得到严格执行,也可以提供与 “内部标准” 类似的好处,不过在集成来自该内部标准适用范围之外的组件时,所期望获得的好处就会少一些。

采用标准可以是一种有效的可集成性策略,尽管其有效性取决于该标准所解决的差异方面能够带来的好处,以及未来的组件供应商遵循该标准的可能性。将与系统 S 的通信限制为必须使用该标准,通常会减少潜在的依赖关系数量。根据标准中所定义的内容,它还可能解决语法、数据语义、行为语义和时间方面的 “距离” 问题。

抽象通用服务

当两个元素提供的服务相似但不完全相同时,将这两个特定元素隐藏在一个更通用服务的公共抽象背后可能会很有用。这种抽象可以通过由这两个元素共同实现的公共接口来体现,也可能涉及一个中介,该中介将对抽象服务的请求转换为对隐藏在该抽象背后的元素的更具体请求。这样产生的封装可以向系统中的其他组件隐藏这些元素的细节。从可集成性的角度来看,这意味着未来的组件可以与单个抽象进行集成,而不是分别与每个特定元素进行集成。

当抽象通用服务策略与中介(如包装器或适配器)结合使用时,它还可以规范特定元素之间在语法和语义上的差异。例如,当系统使用来自不同制造商的许多相同类型的传感器时,我们就能看到这种情况,每个传感器都有自己的设备驱动程序、精度或定时特性,但架构为它们提供了一个通用接口。再比如,你的浏览器可以兼容各种广告拦截插件,由于有插件接口,浏览器本身可以完全不必在意你选择了哪种插件。

抽象通用服务使得在处理常见的基础设施问题(如转换、安全机制和日志记录)时能够保持一致性。当这些功能发生变化,或者实现这些功能的组件的新版本发生变化时,可以在较少的地方进行修改。抽象服务通常与一个中介配合使用,该中介可能会进行处理,以隐藏特定元素之间在语法和数据语义上的差异。

适配

发现

发现服务是一个相关地址的目录,每当需要将一种形式的地址转换为另一种形式时,每当目标地址可能是动态绑定的,或者存在多个目标时,它都能派上用场。它是应用程序和服务相互定位的机制。发现服务可用于枚举在不同产品中使用的特定元素的变体。

发现服务中的条目之所以存在,是因为它们已被注册。这种注册可以是静态的,也可以在服务实例化时动态进行。当发现服务中的条目不再相关时,应该将其注销。同样,这可以像在 DNS 服务器中那样静态完成,也可以动态完成。动态注销可以由发现服务本身对其条目进行健康检查来处理,也可以由一个外部软件来执行,该软件知道目录中的某个特定条目何时不再相关。

发现服务中可能包含其本身就是发现服务的条目。同样,发现服务中的条目可能具有其他属性,查询时可以引用这些属性。例如,天气发现服务可能有一个 “预报成本” 的属性;然后你可以向天气发现服务请求一个提供免费预报的服务。

发现策略的作用是减少协作服务之间的依赖关系,这些服务在编写时应该互不了解。这使得服务之间的绑定以及绑定发生的时机都具有灵活性。

定制接口

定制接口是一种在不改变应用程序编程接口(API)或实现的情况下,向现有接口添加功能或隐藏功能的策略。可以在不改变接口的情况下,向其添加诸如转换、缓冲和数据平滑等功能。隐藏功能的一个例子是向不可信用户隐藏特定的函数或参数。这种策略的一个常见动态应用是拦截过滤器,它添加数据验证等功能,以帮助防止 SQL 注入或其他攻击,或者在数据格式之间进行转换。另一个例子是使用面向切面编程的技术,在编译时融入预处理和后处理功能。

定制接口策略允许根据上下文添加或隐藏许多服务所需的功能,并对其进行独立管理。它还使存在语法差异的服务能够在不修改任何一方的情况下实现互操作。

这种策略通常在集成过程中应用;然而,设计一种便于进行接口定制的架构可以提高可集成性。接口定制通常用于在集成过程中解决语法和数据语义方面的 “距离” 问题。它也可以用于解决某些形式的行为语义 “距离” 问题,尽管这样做可能会更复杂(例如,维护一个复杂的状态以适应协议差异),并且也许更准确地说,这应该归类为引入一个中介。

配置行为

配置行为策略由软件组件使用,这些组件按规定的方式实现为可配置的,以便它们能够更轻松地与一系列组件进行交互。组件的行为可以在构建阶段(使用不同的标志重新编译)、系统初始化阶段(读取配置文件或从数据库获取数据)或运行时(在请求中指定协议版本)进行配置。一个简单的例子是配置一个组件,使其在接口上支持标准的不同版本。确保有多种选项可用,会增加系统 S 和未来的组件 C 的假设相匹配的可能性。

在系统 S 的部分功能中构建可配置行为是一种可集成性策略,它使 S 能够支持更广泛的潜在组件 C。这种策略有可能解决语法、数据语义、行为语义和时间方面的 “距离” 问题。

协作

编排

编排是一种使用控制机制来协调和管理特定服务调用的策略,以便这些服务可以相互保持独立。

编排有助于集成一组松散耦合的可重用服务,以创建一个满足新需求的系统。当在架构中以一种支持未来可能集成的服务的方式包含编排时,集成成本会降低。这种策略使未来的集成活动能够专注于与编排机制的集成,而不是与多个组件进行点对点的集成。

工作流引擎通常会使用编排策略。工作流是一组有组织的活动,用于对软件组件进行排序和协调,以完成一个业务流程。它可能由其他工作流组成,而每个工作流本身又可能由聚合的服务组成。工作流模型鼓励重用和灵活性,从而带来更灵活的业务流程。业务流程可以在业务流程管理(BPM)的理念下进行管理,这种理念将流程视为一组需要管理的竞争资产。复杂的编排可以用诸如 BPEL(业务流程执行语言)这样的语言来指定。

编排通过将系统 S 和新组件 {Cᵢ} 之间的依赖关系集中到编排机制上,从而减少了它们之间的依赖关系数量,并完全消除了组件 {Cᵢ} 之间的显式依赖关系。如果编排机制与遵循标准等策略结合使用,它还可以减少语法和数据语义方面的 “距离”。

管理资源

资源管理器是一种特定形式的中介,用于管理对计算资源的访问;它类似于限制通信路径的策略。采用这种策略时,软件组件不允许直接访问某些计算资源(例如,线程或内存块),而是要从资源管理器请求这些资源。资源管理器通常负责以一种保持某些不变量(例如,避免资源耗尽或并发使用)、执行某些公平访问策略或同时满足这两个条件的方式,在多个组件之间分配资源访问权限。资源管理器的例子包括操作系统、数据库中的事务机制、企业系统中线程池的使用,以及在安全关键系统中用于空间和时间分区的 ARINC 653 标准的应用。

管理资源策略通过清楚地暴露资源需求并管理它们的共享使用,来减少系统 S 和组件 C 之间的资源 “距离”。

7.4 基于策略的可集成性调查问卷

基于 7.3 节 中描述的策略,我们可以提出一系列受可集成性策略启发的问题,如 表 7.2 所示。为了全面了解为支持可集成性而做出的架构选择,分析师会提出每个问题,并将答案记录在表格中。这些问题的答案随后可成为进一步工作的重点,例如:研究文档、分析代码或其他工件、对代码进行逆向工程等等。

表 7.2 基于策略的可集成性调查问卷

策略组策略问题支持与否风险设计决策和定位推理和假设
限制依赖关系系统是否通过引入明确的接口,并要求对每个元素的所有访问都必须通过这些接口,来封装每个元素的功能
系统是否广泛使用中介来打破组件之间的依赖关系 —— 例如,消除数据生产者对其消费者的依赖(即让数据生产者无需了解消费者的具体情况)?
系统是否对通用服务进行了抽象,为类似的服务提供了一个通用的、抽象的接口?
系统是否提供了一种方法来限制组件之间的通信路径
在组件之间如何相互交互以及共享信息方面,系统是否遵循相关标准
适配系统是否具备静态(即在编译时)定制接口的能力 —— 也就是说,在不改变组件接口的应用程序编程接口(API)或实现方式的情况下,能够添加或隐藏该接口的功能?
系统是否提供了一种“发现服务”,用于对有关服务的信息进行编目和传播?
系统是否提供了一种在构建阶段、初始化阶段或运行时对组件行为进行配置的方法?
协作系统是否包含一种“编排机制”,用于协调和管理组件的调用,从而使这些组件能够相互独立运行(彼此无需知晓对方的存在)?
系统是否提供了一个用于管理对计算资源访问权限的“资源管理器”?

7.5 模式

前三种模式都围绕着定制接口策略展开,在此将它们作为一组进行介绍:

  • 包装器:包装器是一种封装形式,通过这种方式,某个组件被封装在另一种抽象形式之中。包装器是唯一被允许使用该组件的元素;其他所有软件都需通过包装器来使用该组件的服务。包装器会对其所包装组件的数据或控制信息进行转换。例如,某个组件可能期望使用英制单位的输入,但却处于一个其他所有组件都使用公制单位的系统中。包装器能够:

    • 将组件接口的某个元素转换为另一种元素。
    • 隐藏组件接口的某个元素。
    • 不做任何改动地保留组件基本接口的某个元素。
  • 桥接器:桥接器将一个任意组件的某些 “需求” 假设转换为另一个组件的某些 “提供” 假设。桥接器与包装器的关键区别在于,桥接器独立于任何特定的组件。此外,桥接器必须由某个外部代理显式调用 —— 有可能是由桥接器所连接的组件之一调用,但并非一定如此。最后这一点应表明,桥接器通常是临时存在的,并且特定的转换是在桥接器构建时(例如,桥接器编译时)定义的。在讨论中介器时,这两个区别的重要性将更加清晰。

    与包装器相比,桥接器通常关注的接口转换范围更窄,因为桥接器处理的是特定的假设。桥接器试图处理的假设越多,它所适用的组件就越少。

  • 中介器:中介器兼具桥接器和包装器的特性。桥接器和中介器的主要区别在于,中介器包含一个规划功能,可在运行时确定转换方式,而桥接器则是在构建时就确定这种转换。

    中介器在成为系统架构中的一个显式组件这方面,也与包装器类似。也就是说,从语义层面看较为基础且通常是临时存在的桥接器,可以被视为附带的修复机制,其在设计中的作用可能是隐含的。相比之下,中介器具有足够的语义复杂性和运行时自主性(持久性),能够在软件架构中发挥重要作用。

优点

  • 这三种模式都允许在不强制对元素或其接口进行修改的情况下访问该元素。

权衡

  • 创建上述任何一种模式都需要预先进行开发工作。
  • 所有这些模式在访问元素时都会引入一定的性能开销,不过通常这种开销较小。

面向服务的架构模式

面向服务的架构(SOA)模式描述了一组提供和 / 或使用服务的分布式组件。在 SOA 中,“服务提供者” 组件和 “服务使用者” 组件可以使用不同的实现语言和平台。服务在很大程度上是独立的实体:服务提供者和服务使用者通常是独立部署的,并且往往属于不同的系统,甚至不同的组织。组件具有接口,这些接口描述了它们向其他组件请求的服务以及它们所提供的服务。服务的质量属性可以通过服务级别协议(SLA)来指定和保证,该协议有时可能具有法律约束力。组件通过相互请求服务来执行计算。服务之间的通信通常使用 Web 服务标准,如 Web 服务描述语言(WSDL)或简单对象访问协议(SOAP)来进行。

SOA 模式与微服务架构模式相关(请参阅 第 5 章)。不过,微服务架构被认为是构成单个系统,并由单个组织进行管理,而 SOA 提供的可重用组件被认为是异构的,且由不同的组织进行管理。

优点

  • 服务被设计为可被各种客户端使用,这使得它们更加通用。许多商业组织提供和推广其服务的目标就是被广泛采用。
  • 服务是独立的。访问服务的唯一方式是通过其接口以及网络上的消息。因此,除了通过接口之外,服务与系统的其他部分不会进行交互。
  • 服务可以使用最合适的语言和技术以异构方式实现。

权衡

  • 由于 SOA 的异构性和不同的所有权,它配备了许多诸如 WSDL 和 SOAP 之类的互操作性特性。这增加了系统的复杂性和开销。

动态发现

动态发现应用了 “发现” 策略,以便在运行时发现服务提供者。因此,服务使用者和具体服务之间可以在运行时进行绑定。

使用动态发现功能意味着期望系统能够清晰地公布可与未来组件集成的可用服务,以及每个服务的最少必要信息。可用的具体信息会有所不同,但通常包括在发现和运行时集成过程中可以通过机器搜索的数据(例如,通过字符串匹配来识别接口标准的特定版本)。

优点

  • 这种模式为将服务绑定在一起形成一个协作的整体提供了灵活性。例如,可以在启动或运行时根据服务的价格或可用性来选择服务。

权衡

  • 动态发现的注册和注销必须实现自动化,并且必须获取或开发用于此目的的工具。

7.6 扩展阅读

本章的大部分内容受到了 [Kazman 20a] 的启发,并引用自其中。

关于可集成性这一质量属性的深入讨论,可在 [Hentonnen 07] 中找到。

[MacCormack 06] 和 [Mo 16] 定义了架构层面的耦合度量标准,并提供了实证依据,这些度量标准在衡量设计的可集成性方面可能会很有用。

《设计模式:可复用的面向对象软件元素》一书 [Gamma 94] 定义并区分了桥接模式、包装器模式和适配器模式。

7.7 问题讨论

1. 回想一下你过去进行过的一次集成工作,也许是将某个库或框架集成到你的代码中。找出你必须处理的各种 “距离”,如 7.1 节 中所讨论的那样。在这些 “距离” 中,哪一个最难解决?

2. 为你正在开发的一个系统编写一个具体的可集成性场景(也许是针对你正在考虑集成的某个组件的探索性场景)。

3. 你认为在实际应用中,哪种可集成性策略最容易实现,为什么?哪种最难以实现,又为什么?

4. 许多可集成性策略与可修改性策略类似。如果你让你的系统具有高度的可修改性,这是否就自动意味着它很容易被集成到另一个环境中?

5. 面向服务架构(SOA)的一个常见应用是为电子商务网站添加购物车功能。哪些商业可用的 SOA 平台提供不同的购物车服务?这些购物车的属性是什么?这些属性在运行时能够被发现吗?

6. 编写一个程序,通过谷歌应用商店(Google Play Store)的应用程序编程接口(API)访问该商店,并返回一份天气预报应用程序及其属性的列表。

7. 大致勾勒出一个动态发现服务的设计。这种服务有助于减少哪些类型的 “距离”?


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值