7. 可集成性

第7章 可集成性

融合是生活的基本法则;当我们抵制它时,瓦解是我们内心和外部的自然结果。因此,我们得出了通过整合实现和谐的概念。

—Norman Cousins 诺曼·考辛斯

根据韦氏词典,形容词可集成的意思是“能够被整合”。我们会给你一点时间喘口气,吸收这种深刻的见解。但对于实际的软件系统,软件架构师需要关注的不仅仅是让单独开发的组件相互协作;他们还关注将来可预期和(在不同程度上)不可预期的集成任务的成本技术风险。这些风险可能与进度、性能或技术有关。

集成问题的一般抽象表示是,一个项目需要将一个软件单元C或一组单元C1C2,… Cn集成到系统S中。S 可能是一个平台,我们在其中集成 {Ci},或者它可能是一个已经包含 {C1C2, …, Cn} 的现有系统,我们的任务是设计和分析集成 {Cn+1, … Cm} 的成本和技术风险。

我们假设我们可以控制 S,但 {Ci} 可能超出我们的控制范围(例如由外部供应商提供),因此我们对每个 Ci 的理解水平可能会有所不同。我们对*Ci*的理解越清晰,设计的能力和分析就越准确。

当然,S不是静态的,而是会演变的,这种演变可能需要重新分析。可集成性(就像其他质量属性,如可修改性一样)具有挑战性,因为它是关于在我们掌握不完整信息时规划未来。简而言之,一些集成将比其他集成更简单,因为它们已被预期并适用在架构中,而其他集成将更复杂,因为它们尚未实现。

考虑一个简单的类比:将北美插头(Ci的示例)插入北美插座(电气系统S提供的接口),“集成”是微不足道的。但是,将北美插头集成到英国插座中需要适配器。带有北美插头的设备可能只能使用 110 伏电源运行,需要进一步调整才能在英国 220 伏插座中工作。此外,如果组件设计为以 60 Hz 运行,而系统提供 70 Hz,则即使组件插入正常,也可能无法按预期运行。S和*Ci*的创建者做出的架构决策 - 例如,提供插头适配器或电压适配器,或使组件在不同频率下以相同的方式工作 - 将影响集成的成本和风险。

7.1 评估架构的可集成性

集成难度(成本和技术风险)可以被认为是 {Ci} 和 S 接口的大小和“距离”的函数:

大小是 {Ci} 和 S 之间潜在依赖项的数量。

距离是解决每个依赖项差异的难度。

依赖关系通常以语法方式度量。例如,我们说模块 A 依赖于组件 B,要么 A 调用 B,要么 A 继承自 B,或者要么 A 使用 B。但是,虽然句法依赖关系很重要,并且将来将继续很重要,但依赖关系可能以任何句法关系都无法检测到的形式出现。两个组件可以暂时或通过资源耦合,因为它们在运行时共享和竞争有限的资源(例如,内存,带宽,CPU),共享外部设备的控制,或者具有时序依赖性。或者它们可能是语义上耦合的,因为它们共享相同协议、文件格式、度量单位、元数据或其他方面的知识。这些区别很重要的原因是,时间和语义依赖关系通常没有得到很好的理解、明确承认或正确记录。对于一个大型、长期存在的项目来说,缺失或隐含的知识始终是一种风险,而这种知识缺口将不可避免地增加集成和集成测试的成本和风险。

考虑当今计算中服务和微服务的趋势。这种方法从根本上讲是关于解耦组件以减少其依赖项的数量和距离。服务仅通过其已发布的接口“了解”彼此,如果该接口是适当的抽象,则对一个服务的更改不太可能波及到系统中的其他服务。组件解耦的不断增加是整个行业的趋势,已经持续了几十年。面向服务本身仅解决(即减少)依赖关系的语法方面;它不涉及时间或语义方面。假定相互了解并相互假设的解耦组件实际上是紧密耦合的,将来更改它们的成本很高。

出于可集成性的目的,必须将“接口”理解为不仅仅是 API。它们必须表征元素之间的所有的依赖关系。在尝试理解组件之间的依赖关系时,“距离”的概念很有帮助。当组件交互时,它们在如何合作以成功执行交互方面有多一致?距离可能意味着:

  • 句法距离。合作元素必须就共享的数据元素的数量和类型达成一致。例如,如果一个元素发送整数,而另一个元素期望浮点数,或者数据字段中的位可能以不同的方式解释,则这种差异表示必须桥接的语法距离。数据类型的差异通常易于观察和预测。例如,编译器可能会捕获此类类型不匹配。位掩码的差异虽然性质相似,但通常更难检测,分析师可能需要依靠文档或代码审查来识别它们。

  • 数据语义距离。合作元素必须就数据语义达成一致;也就是说,即使两个元素共享相同的数据类型,它们的值也会以不同的方式解释。例如,如果一个数据值表示高度(以米为单位),另一个数据值表示高度(以英尺为单位),则表示必须桥接的数据语义距离。这种不匹配通常很难观察和预测,但如果所涉及的元素使用元数据,则对分析师会有很大帮助。可以通过比较接口文档或元数据描述(如果可用)或通过检查代码(如果可用)来发现数据语义中的不匹配。

  • 行为语义距离。合作元素必须就行为达成一致,特别是在系统的状态和模式方面。例如,在系统启动、关闭或恢复模式下,数据元素的解释可能不同。在某些情况下,可以在协议中显式捕获此类状态和模式。作为另一个例子,CiCj 可能对控制做出不同的假设,例如各自期望对方发起交互。

  • 时间距离。合作元素必须就时间假设达成一致。时间距离的示例包括以不同的速率运行(例如,一个元素以 10 Hz 的速率发出值,另一个元素期望以 60 Hz 的速率发射值)或做出不同的计时假设(例如,一个元素期望事件 A 跟随事件 B,另一个元素期望事件 A 跟随事件 B 的延迟不超过 50 毫秒)。虽然这可能被认为是行为语义的一个子案例,但它是如此重要(而且通常是微妙的),以至于我们需要明确地指出它。

  • 资源距离。合作要素必须就共享资源的假设达成一致。资源距离的示例可能涉及设备(例如,一个元素需要对设备的独占访问,而另一个元素需要共享访问)或计算资源(例如,一个元素需要 12 GB 内存才能以最佳方式运行,另一个元素需要 10 GB,但目标 CPU 只有 16 GB 的物理内存;或者三个元素同时以 3 Mbps 的速度生成数据, 但通信信道提供的峰值容量仅为 5 Mbps)。同样,这个距离可能被视为与行为距离有关,但应该有意识地进行分析。

这些细节通常不会在编程语言接口描述中提及。但是,在组织上下文中,这些未声明的隐式接口通常会增加集成任务(以及修改和调试任务)的时间和复杂性。这就是为什么接口是架构问题的原因,我们将在 第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 之间的语法、数据和行为语义距离。

使用中介

中介用于打破一组组件 Ci 之间或 Ci 与系统 S 之间的依赖关系。中介可用于解决不同类型的依赖。例如,诸如发布-订阅总线、共享数据存储库或动态服务发现之类的中介都通过消除任何一方知道另一方身份的需要来减少数据生产者和使用者之间的依赖关系。其他中介,如数据转换器和协议转换器,则解决语法和数据语义距离的形式。

确定特定中介的具体益处需要了解中介的实际做了什么。分析师需要确定中介是否减少了组件与系统之间的依赖关系数量,以及它处理的距离维度(如果有)。

中介通常在集成过程中引入以解决特定的依赖关系,但它们也可以包含在架构中,以促进相对于预期方案的可集成性。在架构中包含通信中介(如发布-订阅总线),然后限制总线与传感器之间的通信路径,是使用中介以提高传感器可集成性为目标的一个例子。

限制通信路径

这种策略限制了给定元素可以与之通信的元素集。在实践中,这种策略是通过限制元素的可见性(当开发人员看不到界面时,他们无法使用它)和授权(即,限制对仅授权元素的访问)来实现的。限制通信路径策略见于面向服务的架构 (SOA),其中不鼓励点对点请求,而是强制所有请求通过企业服务总线,以便可以一致地完成路由和预处理。

遵守标准

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

标准在适用或采用范围方面同样有所不同。例如,由电气和电子工程师协会 (IEEE)、国际标准化组织 (ISO) 和对象管理组织 (OMG) 等广泛认可的标准制定组织发布的标准更有可能被广泛采用。组织内的约定,特别是如果记录良好并执行,可以提供与“本地标准”类似的好处,尽管在集成组织标准外的组件时,期望的好处较低。

采用标准可能是一种有效的可集成性策略,尽管其有效性仅限于基于标准中解决的差异维度以及未来组件供应商符合标准的可能性。限制与系统 S 的通信以要求使用该标准通常会减少潜在依赖项的数量。根据标准中定义的内容,它还可以解决距离的语法、数据语义、行为语义和时间维度。

Abstract Common Services 抽象公共服务

如果两个元素提供的服务相似但不完全相同,则对于更通用的服务,将两个特定元素隐藏在公共抽象后面可能会很有用。这种抽象可以作为两者实现的公共接口实现,或者它可能涉及一个中介,该中介将对抽象服务的请求转换为对隐藏在抽象后面的元素的更具体的请求。生成的封装对系统中的其他组件隐藏了元素的详细信息。在可集成性方面,这意味着未来的组件可以通过单个抽象集成,而不是与每个特定元素单独集成。

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

抽象通用服务可以在处理常见基础设施问题(例如,翻译、安全机制和日志记录)时保持一致性。当这些功能发生更改时,或者当实现这些功能的组件的新版本发生更改时,可以在较少的位置进行更改。抽象服务通常与中介配对,该中介可以执行处理以隐藏特定元素之间的语法和数据语义差异。

适配

发现

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

发现服务中的条目之所以存在,是因为它们已注册。此注册可以静态发生,也可以在实例化服务时动态发生。发现服务中的条目在不再相关时应取消注册。同样,这可以静态完成,例如使用 DNS 服务器,也可以动态完成。动态取消注册可以由发现服务本身对其条目执行运行状况检查来处理,也可以由知道目录中特定条目何时不再相关的外部软件执行。

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

发现策略通过减少合作服务之间的依赖关系来起作用,这些依赖关系应该在彼此不知情的情况下编写。这样可以灵活地绑定服务之间的绑定,以及绑定发生的时间。

剪裁接口

剪裁接口是一种策略,可以在不更改 API 或实现的情况下向现有接口添加功能或隐藏现有接口中的功能。可以将转换、缓冲和数据平滑等功能添加到接口中,而无需对其进行更改。删除功能的一个示例是对不受信任的用户隐藏特定函数或参数。此策略的常见动态应用是拦截过滤器,这些过滤器添加了数据验证等功能,以帮助防止 SQL 注入或其他攻击,或在数据格式之间进行转换。另一个例子是使用面向方面的编程技术,这些技术在编译时编进了预处理和后处理功能。

剪裁接口策略允许根据上下文添加或隐藏许多服务所需的功能,并独立管理。它还使具有语法差异的服务能够互操作,而无需修改任一服务。

此策略通常在集成过程中应用;但是,设计有助于接口剪裁的架构可以支持可集成性。接口剪裁通常用于在集成过程中解决语法和数据语义距离。它也可以用于解决某些形式的行为语义距离,尽管它可能更复杂(例如,保持复杂的状态以适应协议差异),并且可能更准确地归类为引入中介。

配置行为

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

将可配置行为构建到 S 的某些部分中是一种可集成性策略,它允许 S 支持更广泛的潜在 C。这种策略可以潜在地解决距离的语法、数据语义、行为语义和时间维度。

协调

业务流程

业务流程是一种策略,它使用控制机制来协调和管理特定服务的调用,以便它们可以相互不了解。

业务流程有助于集成一组松散耦合的可重用服务,以创建满足新需求的系统。当业务流程以支持将来可能集成的服务的方式包含在架构中时,集成成本就会降低。此策略允许未来的集成活动侧重于与业务流程机制的集成,而不是与多个组件的点对点集成。

工作流引擎通常使用统筹策略。工作流是一组有组织的活动,这些活动对软件组件进行排序和协调以完成业务流程。它可能由其他工作流组成,每个工作流本身可能包含聚合服务。工作流模型鼓励重用和敏捷性,从而实现更灵活的业务流程。业务流程可以在业务流程管理 (BPM) 的理念下进行管理,该理念将流程视为要管理的一组竞争资产。可以使用 BPEL(业务流程执行语言)等语言指定复杂的业务流程。

业务流程的工作原理是减少系统 S 和新组件 {Ci} 之间的依赖关系数量,并通过将这些依赖关系集中在业务流程机制中来完全消除组件 {Ci} 之间的显式依赖关系。如果业务流程机制与遵守标准等策略结合使用,则还可以缩短语法和数据语义距离。

管理资源

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

管理资源策略的工作原理是缩短系统 S 和组件 C 之间的资源距离,明确公开资源需求并管理其常见用途。

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

基于第7.3节中描述的策略,我们可以创建一组受可集成性策略启发的问题,如表7.2所示。为了大致了解为支持可集成性而做出的架构选择,分析师会询问每个问题并将答案记录在表中。然后,可以将这些问题的答案作为进一步活动的重点:文档调查、代码或其他工件分析、代码逆向工程等。

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

策略组策略问题支持与否风险设计决策和定位理由和假设
限制依赖关系系统是否通过引入显式接口并要求对元素的所有访问都通过这些接口来为每个元素封装功能
系统是否广泛地使用中介来打破组件之间的依赖关系,例如,删除数据生产者对其使用者的了解?
系统是否抽象公共服务,为类似服务提供通用的抽象接口?
系统是否提供在组件之间限制通信路径的方法?
系统在组件如何交互和相互共享信息方面是否遵守标准
适配系统是否提供静态(即在编译时)剪裁接口的能力,即在不更改其 API 或实现的情况下添加或隐藏组件接口的功能的能力?
系统是否提供发现服务,编目和传播有关服务的信息?
系统是否提供在生成、初始化或运行时配置组件行为的方法?
协作系统是否包含协调和管理组件调用的业务流程机制,以便它们彼此不知情?
系统是否提供管理对计算资源的访问的资源管理器

7.5 模式

前三种模式都以剪裁接口策略为中心,此处将其描述为一组:

  • 包装器。包装器是一种封装形式,其中某些组件被封装在替代抽象中。包装器是唯一允许使用该组件的元素;所有其他软件都通过包装器使用该组件的服务。包装器转换它包装的组件的数据或控件信息。例如,组件可能期望使用英制度量的输入,但发现自己处于所有其他组件生成公制度量的系统中。包装器可以:

    • 将组件接口的元素转换为替代元素

    • 隐藏组件接口的元素

    • 保留组件基本接口的元素而不进行更改

  • 桥接。桥接将一个任意组件的一些“需要”假设转换为另一个组件的一些“提供”假设。桥接和包装器之间的主要区别在于桥接独立于任何特定组件。此外,桥接必须由某些外部代理显式调用,可能但不一定由桥接跨越的组件之一显式调用。最后一点应该传达这样一种观点,即桥接通常是瞬态的,并且特定的转换是在桥接构建时定义的(例如,桥接编译时)。这两种区别的重要性将在中介的讨论中明确说明。

    与包装器相比,桥接通常专注于更窄的接口转换范围,因为桥接解决了特定的假设。桥接尝试解决的假设越多,它适用的组件就越少。

  • 中介。中介表现出桥接和包装器的特性。桥接和中介之间的主要区别在于,中介包含一个规划功能,导致运行时确定转换,而桥接在桥接构建时建立此转换。

    中介也类似于包装器,因为它成为系统架构中的显式组件。也就是说,语义上原始的、通常是瞬态的桥接可以被认为是偶然的修复机制,其在设计中的作用可以保持隐含。相比之下,中介具有足够的语义复杂性和运行时自治(持久性),可以在软件架构中发挥最好的作用。

好处:

  • All three patterns allow access to an element without forcing a change to the element or its interface.

    所有三种模式都允许在无需强制更改元素或其接口的情况下访问元素。

权衡:

  • 创建任何模式都需要前期开发工作。

  • 所有模式都会在访问元素时引入一些性能开销,尽管此开销通常很小。

面向服务的架构模式

面向服务架构 (SOA) 模式描述了提供和/或使用服务的分布式组件的集合。在 SOA 中,服务提供者组件和服务使用者组件可以使用不同的实现语言和平台。服务在很大程度上是独立的实体:服务提供者和服务消费者通常独立部署,并且通常属于不同的系统甚至不同的组织。组件具有描述它们从其他组件请求的服务以及它们提供的服务的接口。服务的质量属性可以通过服务级别协议 (SLA) 来指定和保证,该协议有时可能具有法律约束力。组件通过相互请求服务来执行其计算。服务之间的通信通常通过使用 Web 服务标准(如 WSDL(Web 服务描述语言)或 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. 编写一个程序,通过其API访问Google Play商店,并返回天气预报应用程序及其属性的列表。

7. 绘制动态发现服务的设计草图。此服务有助于缓解哪些类型的距离?


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/522618.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

warning: #188-D: enumerated type mixed with another type

警告解释:枚举类型混合了其它的数据类型; 解决方法: 1:检查代码,是不是存在混用;; 2:结构体初始化为 0 报warning,不能将结构体的第一个变量,使用枚举类型&am…

DDoS和CC攻击的原理

目前最常见的网络攻击方式就是CC攻击和DDoS攻击这两种,很多互联网企业服务器遭到攻击后接入我们德迅云安全高防时会问到,什么是CC攻击,什么又是DDoS攻击,这两个有什么区别的,其实清楚它们的攻击原理,也就知…

Java零基础 - 数组的定义和声明

哈喽,各位小伙伴们,你们好呀,我是喵手。 今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。 我是一名后…

【Super数据结构】数据结构入门first step!了解些概念和时空复杂度计算!

🏠关于此专栏:Super数据结构专栏将使用C/C语言介绍顺序表、链表、栈、队列等数据结构,每篇博文会使用尽可能多的代码片段图片的方式。 🐎博主首页:Jammingpro 🚪归属专栏:Super数据结构 &#x…

C++ 篇 数组

数组是含有多个数据项的数据结构,并且这些数据项都具有相同的数据类型。这些数据项称为数组的元素,我们可以根据元素在数组中的位置来选取元素。 最简单的数组就是一维数组。数组元素在内存中是依次排列的,如下图所示: 声明一个…

穿越牛熊,股市的春天还有多远?

2023年,资本市场的严冬令无数投资者和机构投资者都感受到了前所未有的压力。VC/PE、公募基金、股权投资类公司等机构,在这一年里业绩普遍不佳,寒意弥漫。VC/PE机构的营业收入普遍呈现负增长,公募基金更是历史上首次连续两年亏损&a…

LLM 加速技巧:Muti Query Attention

MQA 是 19 年提出的一种新的 Attention 机制,其能够在保证模型效果的同时加快 decoder 生成 token 的速度。在大语言模型时代被广泛使用,很多LLM都采用了MQA,如Falcon、PaLM、StarCoder等。 在介绍MQA 之前,我们先回顾一下传统的…

穷人想赚钱该怎么选打工VS创业?2024年如何把握新机遇?

在贫穷的困境中,打工与创业似乎成为了两条截然不同的道路,摆在每一个渴望改变命运的人面前。然而,这并非简单的选择题,而是一场关于勇气、智慧与机遇的较量。打工,对于许多人来说,是稳定且相对安全的收入来…

遗传算法理解与代码实战(二)- demo(python+deap)

前文介绍了遗传算法,并且手动python代码进行了实践,但是在遇到复杂的问题时(遗传算法理解与代码实战(三)会介绍),手写代码很麻烦,所以需要借助专门的遗传算法库来实现,这…

社区医院智慧管理:Java+SpringBoot新实践

✍✍计算机编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡ Java实战 |…

酷炫!向数字世界 AGI 迈进!让智能体直接控制键盘、鼠标,与一切软件交互

信息革命催生了数字世界,这个世界为大模型提供了海量数据,同时也为通用人工智能(AGI)的实现提供了可能。在迈向数字世界的 AGI 的过程中,北京智源人工智能研究院、新加坡南洋理工大学和北京大学联合提出了一种名为 Gen…

数据结构——lesson7二叉树 堆的介绍与实现

前言💞💞 啦啦啦~这里是土土数据结构学习笔记🥳🥳 💥个人主页:大耳朵土土垚的博客 💥 所属专栏:数据结构学习笔记 💥对于数据结构顺序表链表有疑问的都可以在上面数据结…