Chapter13 架构策略和模式

目录

架构模式

模式目录概览

模块模式

Layer 分层模式

一些关于层级的细节

其他模块模式

组件和连接器模式

Broker 经纪人模式

MVC 模型-视图-控制器模式

Pipe-and-Filte 管道-过滤器模式概述

Client-Server 客户端-服务器模式

Peer-to-Peer 点对点模式

Service-Oriented Architecture 面向服务的架构模式

Publish-Subscribe 发布-订阅模式

Shared-Data 共享数据模式

分配模式

Map-Reduce 模式

Multi-tier 多层模式

其他分配模式

策略和模式的关系

模式包括策略

使用策略来增强模式

Broker模式的弱点

使用策略改进代理模式

使用策略共同处理

总结


我并没有失败。我只是找到了一万种行不通的方法。
                                                                               ——托马斯·爱迪生

有许多做设计糟糕的方式,只有少数几种是做得好的。因为在架构设计中取得成功是复杂而具有挑战性的,设计师一直在寻找捕捉和重用经验丰富的架构知识的方法。架构模式和策略是捕捉经过验证的良好设计结构的方式,以便它们可以被重用。

在过去的15年或更长时间里,架构模式引起了软件从业者和理论家的日益关注。架构模式:

  • 是在实践中反复发现的一组设计决策,
  • 具有已知的可重用性质,和
  • 描述了一类架构。

因为模式(根据定义)是在实践中发现的,人们并不是发明它们,而是发现它们。对模式进行目录编制类似于林奈植物学家或动物学家的工作:“发现”模式并描述它们的共同特征。就像植物学家、动物学家或生态学家一样,模式目录编制者努力理解这些特征是如何导致不同的行为和对环境条件的不同响应的。因此,永远不会有完整的模式列表:模式会在对环境条件的反应中自发出现,只要这些条件发生变化,新的模式就会出现。

架构设计很少从第一原则开始。有经验的架构师通常认为创建架构是选择定制组合模式的过程。软件架构师必须决定如何实例化一个模式,如何使其与特定上下文和问题的约束相适应。

在第5-11章中,我们已经看到各种各样的架构策略。这些比模式更简单。策略通常只使用单一结构或计算机制,并旨在解决单一架构力量。因此,与通常将多个设计决策组合成一个包的模式相比,它们在架构师做出设计决策时给予更精确的控制。

策略是设计的“构建块”,从中构建架构模式。策略是原子,模式是分子。大多数模式由(构建自)几种不同的策略。因此,我们说模式打包策略

在本章中,我们将简要地浏览模式的宇宙,涉及一些最重要和最常用的架构模式,然后我们将研究模式和策略之间的关系:展示模式是如何由策略构建的,以及策略如何在您在书籍或网站上找到的模式不能完全满足设计需求时用于定制模式。

架构模式

架构模式建立在以下关系之间:

  • 上下文  A context。世界中一种经常出现的共同情境,引发问题。
  • 问题  A problem。在给定上下文中适当概括的问题。模式描述概述了问题及其变体,并描述了任何互补或相反的力量。问题的描述通常包括必须满足的质量属性。
  • 解决方案  A solution。成功的架构解决方案,适当抽象。解决方案描述了解决问题的架构结构,包括如何平衡工作中的多种力量。解决方案将描述元素之间的静态关系和责任(使用模块结构),或者描述元素之间的运行时行为(制定组件和连接器或分配结构)。模式的解决方案由以下内容确定和描述:
  • 一组元素类型(例如,数据存储库、进程和对象)
  • 一组交互机制或连接器(例如,方法调用、事件或消息总线)
  • 组件的拓扑布局
  • 涵盖拓扑、元素行为和交互机制的一组语义约束

解决方案描述还应明确说明静态和运行时元素配置提供的质量属性。
这种{上下文,问题,解决方案}形式构成了记录模式的模板
复杂系统同时展示多个模式。基于网络的系统可能采用三层客户端-服务器架构模式,但在此模式内部,它可能还使用复制(镜像)、代理、缓存、防火墙、MVC等,每个都可能使用更多的模式和策略。而且,客户端-服务器模式的所有部分可能都使用分层来内部构造其软件模块。

模式目录概览

在本节中,我们列举了一系列有用且广泛使用的模式。这个目录并不旨在是详尽无遗的,事实上,这样的目录是不可能的。相反,它旨在具有代表性。我们展示了运行时元素的模式(例如代理或客户端服务器)和设计时元素的模式(例如层次结构)。对于每个模式,我们列出了上下文、问题和解决方案。作为解决方案的一部分,我们简要描述了每个模式的元素、关系和约束

应用模式并非是非此即彼的选择。目录中给出的模式定义是严格的,但在实践中,当存在良好的设计折衷方案时(牺牲违规成本的一点,但获得偏离带来的一些收益),架构师可能选择以微小的方式违反它们。例如,分层模式明确禁止低层中的软件使用高层中的软件,但在某些情况下(比如为了提高性能),架构可能允许一些特定的例外情况。

模式可以通过它们展示的主导元素类型进行分类:模块模式展示模块,组件和连接器(C&C)模式展示组件和连接器,分配模式展示软件元素(模块、组件、连接器)和非软件元素的组合。大多数已发表的模式都是C&C模式,但也有模块模式和分配模式。我们将从模块模式的鼻祖开始,即分层模式。

模块模式

Layer 分层模式

上下文:所有复杂系统都经历了需要独立开发和演进系统部分的需要。因此,系统的开发人员需要清晰而完整地分离关注点,以便系统的模块可以独立开发和维护。

问题:软件需要以一种能够在各部分之间进行很少交互的方式进行分段。支持可移植性可修改性重用

解决方案:为了实现这种关注点的分离,分层模式将软件分为称为层的单元。每个层是提供一组内聚服务的模块组。层之间的允许使用关系有一些约束:关系必须是单向的。层完全划分了一组软件,每个分区通过公共接口暴露。层按照严格的顺序关系进行交互。如果(A,B)在此关系中,则说层A的实现可以使用层B提供的任何公共设施。在某些情况下,可能需要直接使用非相邻较低层中的模块;通常只允许下一层使用。在这个模式中,软件在较高层中使用非相邻较低层中的模块的情况被称为层桥接 layer bridging。如果发生层桥接的许多实例,系统可能无法实现其严格分层有助于实现的可移植性和可修改性目标。在这种模式中,不允许向上使用。

当然,这一切都不是免费的。有人必须设计和构建层,这通常会给系统增加前期成本和复杂性。此外,如果层次结构设计不正确,它可能会妨碍高层程序员需要的底层抽象。而且,层次结构总是会给系统增加性能开销。如果对最顶层的函数进行调用,这可能需要在被硬件执行之前穿越许多较低的层。这些层中的每一个都会以一些开销的形式添加一些开销,至少以上下文切换的形式。表13.1总结了分层模式的解决方案。


一些关于层级的细节

分层架构是少数几个可以通过相邻性显示组件之间连接的地方之一,而且“上方”和“下方”是有关系的。如果你把图13.1颠倒过来,使C位于顶部,那将代表一个完全不同的设计。使用箭头在框之间表示关系的图表无论方向如何,都保留其语义含义。
分层模式是软件工程中最常用的模式之一,但我经常对有多少人仍然搞错它感到惊讶。
首先,通过查看一堆框,不可能知道是否允许层间桥接。也就是说,一个层是否可以使用任何较低的层,还是只能使用下一个较低的层?解决这个问题是世界上最简单的事情;所有架构师需要做的就是在图表符号的关键信息中包含答案(这是我们建议所有图表都应该做的事情)。例如,请考虑图13.2中呈现的分层模式,该图在下一页呈现。

但我仍然对有多少架构师实际上费心做这件事感到惊讶。如果他们不这样做,他们的分层图就是含糊不清的。
其次,任何一组老旧的箱子堆叠在一起并不构成分层架构。例如,看看图13.3中显示的设计,它使用箭头而不是相邻性来表示框之间的关系。在这里,允许所有东西使用任何东西。这明显不是一个分层架构。
原因是,如果将层A替换为不同的版本,使用它的层C(在这个图中使用它)很可能需要更改。我们不希望我们的虚拟机层在应用层更改时每次都更改。但我仍然对有多少人称一堆箱子为“层”(或认为层和多层体系结构中的层是相同的)感到惊讶。

披着羊皮的“层” ↑

第三,许多声称是分层结构的架构看起来类似于图13.4。这个图表可能意味着A、B或C中的模块可以使用D中的模块,但如果没有一个键来确切告诉我们,它可能意味着任何事情。像这样的“旁路”通常包含常见的实用程序(有时是导入的),例如错误处理程序、通信协议或数据库访问机制。这种图表只在主栈中不允许层之间桥接的情况下才有意义。否则,D可以简单地成为主栈中最底层的层,"旁路"的几何结构就是不必要的。但我仍然对看到这种布局没有解释的频率感到惊讶。

有时,层次结构会被划分为表示模块更细粒度分解的段。有时,当预先存在的单元集,例如导入的模块,共享相同的allowed-to-use关系时,会发生这种情况。当发生这种情况时,必须指定各个段之间的使用规则。有许多可能的使用规则,但它们必须明确说明。在图13.5中,顶层和底层被分割成段。顶层的各段不允许彼此使用,但底层的各段可以。如果你在没有箭头的情况下画同样的图,将很难区分分段层内的不同使用规则。分层图表通常是隐藏歧义的根源,因为图表没有明确指出allowed-to-use关系。

最后,关于分层的最重要一点是,一个层不允许使用在它上面的任何层。当一个模块依赖于它得到的答案时,该模块“使用”另一个模块。但是,一个层允许进行向上调用,只要它不期望从中得到答案。这就是回调的常见错误处理方案的工作原理。层A中的程序调用底层B中的程序,参数包括指向A中错误处理程序的指针,底层在出现错误时应调用该程序。B中的软件调用A中的程序,但完全不关心它的作用。通过不以任何方式依赖A的内容,B免受A变更的影响。


其他模块模式

在特定领域的设计者经常会发布适用于该领域系统的“标准”模块分解。这些标准分解如果以“上下文、问题、解决方案”形式呈现,就构成了模块分解模式。 类似地,在面向对象的领域中,适用于某一类系统的“标准”或已发布的类/对象设计解决方案构成了面向对象模式。

组件和连接器模式

Broker 经纪人模式

上下文:许多系统由分布在多个服务器上的一组服务构建而成。实现这些系统是复杂的,因为需要考虑系统之间的相互操作方式、它们如何连接到彼此以及它们如何交换信息,以及组件服务的可用性。

问题:我们如何构建分布式软件,使得服务使用者无需知道服务提供者的性质和位置,从而轻松地动态更改用户和提供者之间的绑定?

解决方案:经纪人模式通过插入一个中间件称为经纪人,将服务的使用者(客户端)与服务的提供者(服务器)分离开来。当客户端需要服务时,它通过服务接口查询经纪人。经纪人然后将客户端的服务请求转发给服务器服务器处理请求。服务结果从服务器传递回经纪人,经纪人然后将结果(以及任何异常)返回给请求的客户端。通过这种方式,客户端完全不知道服务器的身份、位置和特性。由于这种分离,如果服务器不可用,经纪人可以动态选择替代项。如果服务器替换为不同的(兼容的)服务,同样,经纪人是唯一需要知道此更改的组件,因此客户端不受影响。代理通常作为经纪人的补充引入,以帮助处理与经纪人的交互的细节,如编组和解组消息。

经纪人的缺点是它们增加了复杂性(必须设计和实现经纪人和可能的代理,以及消息传递协议),并在客户端和服务器之间添加了间接层,这将增加它们的通信延迟。调试经纪人可能会很困难,因为它们参与高度动态的环境,导致故障的条件可能难以复制。从安全的角度来看,经纪人将是一个明显的攻击点,因此需要适当加固。另外,如果不小心设计,经纪人可能是一个复杂系统的单点故障。经纪人也可能成为通信的瓶颈。 表13.2总结了经纪人模式的解决方案。

经纪人是这个模式中的关键组件。该模式提供了使用中介者策略(在第7章中描述)的所有可修改性优势(因为经纪人模式使得用另一个替换失败的服务器变得容易),以及性能优势(因为经纪人模式使得将工作分配给最不繁忙的服务器变得容易)。然而,该模式也带来了一些责任。例如,使用经纪人会阻止您进行性能优化,如果您知道服务器的精确位置和特性的话可能会进行这些优化。此外,使用此模式会增加中介的开销,从而增加延迟。经文献中描述的经纪人模式的原始版本由Gamma,Helm,Johnson和Vlissides于1994年记录在图13.6中。

经纪人模式的首个广泛使用的实现是在Common Object Request Broker Architecture (CORBA)中。这个模式在Enterprise Java Beans (EJB)和Microsoft的.NET平台中也有常见的使用,实际上,任何现代化的分布式服务提供者和消费者的平台都会实现某种形式的经纪人。面向服务的架构 (SOA) 方法在很大程度上依赖于经纪人,最常见的形式是企业服务总线。

MVC 模型-视图-控制器模式

上下文:用户界面软件通常是交互式应用程序中最频繁修改的部分。因此,重要的是将对用户界面软件的修改与系统的其余部分分开。用户经常希望从不同的角度查看数据,例如条形图或饼图。这些表示应该都反映数据的当前状态。

问题:如何将用户界面功能与应用程序功能保持分离,同时仍对用户输入或底层应用程序数据的更改作出响应?在底层应用程序数据发生更改时,如何创建、维护和协调用户界面的多个视图?

解决方案:模型-视图-控制器 (MVC) 模式将应用程序功能分为三种组件:

  • 模型  A model,其中包含应用程序的数据
  • 视图  A view,显示底层数据的某个部分并与用户交互
  • 控制器  A controller,介于模型和视图之间,管理状态更改的通知

MVC 不适用于每种情况。设计和实现三种不同类型的组件以及它们各种形式的交互可能会很昂贵,而这种成本可能对相对简单的用户界面不合理。此外,MVC 的抽象与商业用户界面工具包之间的匹配并不完美。视图和控制器将输入和输出分开,但这些功能通常组合成单个小部件。这可能导致架构与用户界面工具包之间的概念不匹配。

表13.3总结了MVC模式的解决方案。

实际上,一个模型可能与多个视图和多个控制器相关联。例如,一组业务数据可以在电子表格中表示为数字列,也可以表示为散点图饼图。每个都是单独的视图,而且此视图可以在模型更改时动态更新(例如,在事务处理系统中显示实时交易)。一个模型可以由不同的控制器更新;例如,地图可以通过鼠标移动、轨迹球移动、键盘点击或语音命令进行缩放和平移;每种不同形式的输入都需要由控制器管理。

MVC组件通过某种形式的通知(例如事件或回调)相互连接。这些通知包含状态更新。需要将模型的变化通知给视图,以便它们可以更新外部事件,例如用户输入,需要通知控制器,它可能进而更新视图和/或模型。通知可以是推送或拉取。

由于这些组件之间松散耦合,因此可以轻松地并行开发和测试它们,并且对其中一个的更改对其他组件的影响很小。MVC组件之间的关系如图13.7所示。

MVC模式在用户界面库中广泛使用,如Java的Swing类、Microsoft的ASP.NET框架、Adobe的Flex软件开发工具包、Nokia的Qt框架等等。因此,在一个单独的应用程序中通常包含许多MVC实例(通常每个用户界面对象一个)

Pipe-and-Filte 管道-过滤器模式概述

背景:许多系统需要将离散数据项的流从输入转换为输出。在实践中,许多类型的转换会重复发生,因此将它们创建为独立的、可重用的部分是可取的。

问题:这些系统需要划分为可重用、松散耦合的组件,具有简单、通用的交互机制。通过这种方式,它们可以灵活地组合在一起。这些通用且松散耦合的组件易于重用,并且由于它们是独立的,可以并行执行。

解决方案:Pipe-and-Filter模式的交互模式以对数据流的连续转换为特征。数据到达过滤器的输入端口,经过转换,然后通过管道传递到下一个过滤器的输出端口。单个过滤器可以从一个或多个端口消耗或产生数据。

有几个与Pipe-and-Filter模式相关的弱点。例如,这个模式通常不适合交互式系统,因为它不允许循环(对于用户反馈很重要)。而且,具有大量独立过滤器可能会增加大量计算开销,因为每个过滤器都作为自己的线程或进程运行。此外,Pipe-and-Filter系统在没有某种检查点/还原功能的情况下可能不适用于长时间运行的计算,因为任何过滤器(或管道)的失败都可能导致整个管道失败。

Pipe-and-Filter模式的解决方案总结如下:

Pipes在通信期间缓冲数据。由于这个属性,过滤器可以异步和并发执行。此外,过滤器通常不知道其上游或下游过滤器的身份。因此,管道和过滤器系统具有整体计算可以被视为过滤器计算的函数组合的特性,使得架构师更容易推理端到端的行为。

数据转换系统通常被构造为管道和过滤器,每个过滤器负责输入数据的整体转换的一部分。每个步骤的独立处理支持重用、并行化和对整体行为的简化推理。通常,这些系统构成信号处理应用程序的前端。这些系统在一组初始过滤器处接收传感器数据;这些过滤器中的每一个都会压缩数据并执行初始处理(如平滑)。下游过滤器进一步减少数据并在从不同传感器导出的数据之间进行合成。最终的过滤器通常将其数据传递给应用程序,例如为建模或可视化工具提供输入。

使用管道和过滤器的其他系统包括使用UNIX管道构建的系统、Apache web服务器的请求处理架构、map-reduce模式(本章后面介绍)、Yahoo! Pipes用于处理RSS feeds、许多工作流引擎以及许多必须处理和分析大量捕获数据的科学计算系统。图13.8显示了一个管道和过滤器系统的UML图。

Client-Server 客户端-服务器模式

背景:存在许多分布式客户端希望访问的共享资源和服务,我们希望控制对这些资源或服务的访问或提供服务的质量。

问题:通过管理一组共享资源和服务,我们可以通过将通用服务分解并在单个位置或少数位置修改它们来促进可修改性和重用。我们希望通过集中控制这些资源和服务的控制来提高可伸缩性和可用性,同时将资源本身分布在多个物理服务器上。

解决方案:客户端通过请求服务器提供的服务进行交互,服务器提供一组服务。一些组件可能既充当客户端又充当服务器。可以有一个中央服务器或多个分布式服务器

客户端-服务器模式的解决方案总结在表13.5中;组件类型包括客户端和服务器;客户端-服务器模式的主要连接器类型是由用于调用服务的请求/回复协议驱动的数据连接器。

客户端-服务器模式的一些缺点包括服务器可能成为性能瓶颈,也可能成为单点故障。此外,在系统构建后更改功能的位置的决策通常复杂且成本高昂。一些使用客户端-服务器模式的常见示例包括:

  • 在本地网络上运行的信息系统,其中客户端是GUI启动的应用程序,而服务器是数据库管理系统
  • 基于Web的应用程序,其中客户端是Web浏览器,而服务器是在电子商务站点上运行的组件。

纯客户端-服务器系统的计算流程是不对称的:客户端通过调用服务器的服务来启动交互。因此,客户端必须知道要调用的服务的身份,客户端启动所有交互。相比之下,服务器在服务请求之前不知道客户端的身份,必须响应启动的客户端请求。

早期的客户端-服务器形式中,服务调用是同步的:服务的请求者等待,或者被阻塞,直到请求的服务完成其操作,可能提供返回结果。然而,客户端-服务器模式的变体可能使用更复杂的连接器协议。例如:

  • Web浏览器在数据请求提供之前不会阻塞。
  • 在某些客户端-服务器模式中,允许服务器启动其客户端上的某些操作。这可能通过允许客户端注册通知程序或在特定时间调用的回调来完成。
  • 在其他系统中,通过请求/回复连接器进行的服务调用被一种“会话”括起,该“会话”界定了客户端-服务器交互集的开始和结束。

客户端-服务器模式将客户端应用程序与它们使用的服务分开。这种模式通过将常见服务提取出来实现系统简化,这些服务是可重用的。由于服务器可以被任意数量的客户端访问,因此很容易向系统添加新客户端。同样,可以复制服务器以支持可伸缩性或可用性。全球网络是一个基于客户端-服务器模式的系统的最著名示例,它允许客户端(Web浏览器)使用超文本传输协议(HTTP)从Internet上的服务器访问信息。HTTP是一种请求/回复协议。 HTTP是无状态的,客户端和服务器之间的连接在每次来自服务器的响应之后终止。图13.9使用非正式符号描述了自动取款机(ATM)银行系统的客户端-服务器视图。

Peer-to-Peer 点对点模式

背景:分布式计算实体,每个实体在启动交互方面都被视为同等重要,并且每个实体都提供其自己的资源,需要合作和协作以向分布式用户社区提供服务。

问题:如何通过共同的协议将一组“相等”的分布式计算实体连接起来,以便它们可以以高可用性和可扩展性组织和共享其服务?

解决方案:在点对点(P2P)模式中,组件直接作为对等体进行交互。所有对等体都是“相等的”,没有对等体或对等体组对系统的健康状态至关重要。点对点通信通常是一种请求/回复交互,没有客户端-服务器模式中的不对称性。也就是说,任何组件原则上都可以通过请求其服务与任何其他组件交互。交互可以由任一方发起,换句话说,在客户端-服务器术语中,每个对等体组件都既是客户端又是服务器。有时,交互只是为了转发数据,而不需要回复。每个对等体提供和使用相似的服务,并使用相同的协议。点对点系统中的连接器涉及双向交互,反映了两个或多个点对点组件之间可能存在的双向通信。

对等体首先连接到点对点网络,然后通过与其他对等体协作请求服务来实现其计算。通常,对等体搜索另一个对等体是通过从一个对等体传播到其连接的对等体,传播次数有限。点对点体系结构可能具有具有索引或路由功能的专门对等节点(称为超级节点),允许常规对等体的搜索达到更多的对等体。

对等体可以随时添加和删除,对整个系统的可扩展性非常高。这为在高度分布式的平台上部署系统提供了灵活性。

通常,多个对等体具有重叠的功能,例如提供对相同数据的访问或提供等效的服务。因此,充当客户端的一个对等体可以与充当服务器的多个对等体协作完成某项任务。如果其中一个多个对等体不可用,其他对等体仍然可以提供服务以完成任务。其结果是提高了整体可用性。还存在性能优势:充当服务器的任何给定对等体组件上的负载减少,可能需要更多的服务器容量和基础设施来支持其责任是分布的。这可以减少用于更新数据和中央服务器存储的其他通信的需求,但以本地存储数据为代价。

点对点模式的缺点与其优点密切相关。因为点对点系统是分散的,管理安全性、数据一致性、数据和服务可用性、备份和恢复都更加复杂。在许多情况下,由于对等体的出现和离开,很难提供点对点系统的保证;相反,架构师最多只能提供满足质量目标的概率,并且这些概率通常随着对等体人口的增加而增加。

下一页的表格 13.6 总结了点对点模式解决方案。

点对点计算经常用于分布式计算应用程序,如文件共享、即时通讯、桌面网格计算、路由和无线自组织网络。点对点系统的示例包括文件共享网络,如BitTorrent和eDonkey,以及即时通讯和VoIP应用程序,如Skype。

Service-Oriented Architecture 面向服务的架构模式

背景:一些服务由服务提供商提供(并描述),由服务消费者使用。服务消费者需要能够理解和使用这些服务,而无需详细了解其实现细节。

问题:如何支持运行在不同平台上、使用不同实现语言编写由不同组织提供并分布在Internet上的分布式组件的互操作性?如何定位服务并将它们组合(以及动态重新组合)成有意义的联盟,同时实现合理的性能、安全性和可用性?

解决方案:面向服务的架构(SOA)模式描述了一组分布式组件,这些组件提供和/或消费服务。在SOA中,服务提供商组件和服务消费者组件可以使用不同的实现语言和平台。服务通常是独立的:服务提供商和服务消费者通常独立部署,而且通常属于不同的系统甚至不同的组织。组件具有描述它们从其他组件请求的服务和它们提供的服务的接口。服务的质量属性可以通过服务级别协议(SLA)进行指定和保证。在某些情况下,这些协议是法律约束的。组件通过向彼此请求服务来实现计算。

此模式中的元素包括服务提供商和服务消费者,实际上可以采用不同形式,从在Web浏览器上运行的JavaScript到在大型机上运行的CICS事务。除了服务提供商和服务消费者组件外,SOA应用程序可能会使用充当中介并提供基础设施服务的专门组件:

  • 服务调用可以由企业服务总线(ESB)进行中介。ESB在服务消费者和服务提供商之间路由消息。此外,ESB可以将消息从一种协议或技术转换为另一种协议或技术,执行各种数据转换(例如,格式、内容、分割、合并),执行安全性检查并管理事务。使用ESB可以促进互操作性、安全性和可修改性。当没有ESB存在时,服务提供商和消费者以点对点方式进行通信。
  • 为了提高服务提供商的独立性,可以在SOA架构中使用服务注册表。注册表是允许服务在运行时注册的组件。这使得服务可以在运行时发现,通过隐藏服务提供商的位置和身份来增加系统的可修改性。注册表甚至可以允许多个相同服务的活动版本。
  • 编排服务器(或编排引擎)协调SOA系统中各种服务消费者和提供者之间的互动。它在特定事件发生时执行脚本(例如,收到采购订单请求)。具有明确定义的涉及与分布式组件或系统的交互的业务流程或工作流程的应用程序通过使用编排服务器来提高可修改性、互操作性和可靠性。许多商业可用的编排服务器支持各种工作流或业务流程语言标准。

SOA中使用的基本连接器类型包括:

  • SOAP:Web服务技术中用于通信的标准协议。服务消费者和提供者通过交换请求/回复XML消息进行互动,通常在HTTP之上
  • 表述状态传输(REST):服务消费者发送非阻塞的HTTP请求。这些请求依赖于四个基本的HTTP命令(POST、GET、PUT、DELETE)来告诉服务提供商创建、检索、更新或删除资源。
  • 异步消息传递,一种“发出并忘记”的信息交换。参与者不必等待收到的确认,因为基础设施假定已成功交付消息。消息传递连接器可以是点对点的或发布-订阅的。在实际应用中,SOA环境可能涉及上述三种连接器的混合使用,以及传统协议和其他通信替代方案(例如,SMTP)。商业产品,如IBM的WebSphere MQ、Microsoft的MSMQ或Apache的ActiveMQ,是提供异步消息传递的基础设施组件。SOAP和REST在第6章中有更详细的描述。

正如你所看到的,SOA模式的设计和实施可能相当复杂(由于动态绑定和相关元数据的使用)。此模式的其他潜在问题包括位于服务和客户端之间的中间件的性能开销以及缺乏性能保证(因为服务是共享的,并且通常不受请求者的控制)。这些弱点与经纪模式共享,这并不奇怪,因为SOA模式共享了经纪的许多设计概念和目标。此外,由于通常不能控制所使用的服务的演变,因此可能不得不忍受高昂且未经计划的维护成本。

表13.7总结了SOA模式。

SOA的主要优点和主要驱动因素是互操作性。因为服务提供商和服务消费者可能运行在不同的平台上,所以面向服务的架构通常集成了各种系统,包括传统的遗留系统。SOA还提供了与互联网上可用的外部服务进行交互所需的元素。特殊的SOA组件,如注册表或ESB,还允许动态重新配置,这在需要替换或添加组件的版本而无需系统中断时非常有用。

图13.11显示了Adventure Builder系统的SOA视图。Adventure Builder允许网站上的客户通过选择活动、住宿和前往目的地的交通方式来组装度假。Adventure Builder系统与外部服务提供商互动,以构建度假计划,并与银行服务互动以处理付款。中央的OPC(订单处理中心)组件协调与内部和外部服务消费者和提供商的交互。请注意,外部提供商可以是传统的大型机系统、Java系统、.NET系统等。这些外部组件的性质是透明的,因为SOAP提供了必要的互操作性。

Publish-Subscribe 发布-订阅模式

背景:存在一些独立的数据生产者和数据消费者,它们需要相互交互。数据生产者和消费者的数量和性质没有预先确定或固定,它们共享的数据也不确定

问题:如何创建集成机制,支持在数据生产者和消费者之间传递消息的能力,以使它们不知道彼此的身份,甚至可能不知道彼此的存在

解决方案:在发布-订阅模式中,组件通过公告的消息或事件进行交互,概括在表13.8中。组件可以订阅一组事件。发布-订阅运行时基础设施的任务是确保每个已发布的事件都传递给订阅该事件的所有订阅者。因此,这些模式中的主要连接器形式是事件总线。发布者组件通过宣布它们将事件放置在总线上;然后连接器将这些事件传递给已注册对这些事件感兴趣的订阅者组件。任何组件都可以既是发布者又是订阅者

发布-订阅模式在发送者和接收者之间引入了一层间接性。这对延迟可伸缩性有负面影响,具体取决于实现方式。通常不希望在系统中使用发布-订阅模式,如果系统有硬实时截止时间要求,因为它会引入消息传递时间的不确定性。

此外,发布-订阅模式的问题在于它提供了较少的消息排序控制,并且不能保证消息的传递(因为发送者无法知道接收者是否在监听)。这可能使发布-订阅模式在共享状态至关重要的复杂交互情况下不合适。

有一些特定的改进版本的发布-订阅模式在常见用途中使用。我们将在本节的后面描述其中的几种。

发布-订阅模式的计算模型最好被视为一组独立的进程或对象,它们对其环境生成的事件做出反应,然后作为它们的事件公告的副作用引发其他组件的反应。在Eclipse平台上实现的发布-订阅模式的示例如图13.12所示。

使用发布-订阅模式的典型示例包括以下几种:

  • 图形用户界面:用户的低级输入操作被视为事件,并被路由到适当的输入处理程序。
  • 基于MVC(模型-视图-控制器)的应用程序:当模型对象的状态发生变化时,视图组件会得到通知。
  • 企业资源规划(ERP)系统:集成了许多组件,每个组件仅对系统事件的子集感兴趣。
  • 可扩展的编程环境:工具通过事件协调。
  • 邮件列表:一组订阅者可以注册对特定主题感兴趣。
  • 社交网络:当个人的网站发生变化时,“朋友”会得到通知。

发布-订阅模式用于向未知的接收者发送事件和消息。由于事件生产者不知道事件接收者的集合,因此生产者的正确性通常不能依赖于这些接收者。因此,可以在不修改生产者的情况下添加新的接收者

使组件不知道彼此的身份会导致系统的易于修改(添加或删除数据的生产者和消费者),但会增加运行时性能的成本,因为发布-订阅基础设施是一种间接性,会增加延迟。此外,如果发布-订阅连接器完全失败​​​​​​​,这将成为整个系统的单点故障

发布-订阅模式可以采用多种形式:

  • 基于列表的发布-订阅:在这种实现中,每个发布者维护一个订阅列表其中包含已注册对接收事件感兴趣的订阅者。这个版本比其他版本的解耦程度低,因此在可修改性方面提供的功能不如其他版本,但在运行时开销方面可能相当高效。此外,如果组件分布式,就没有单点故障
  • 基于广播的发布-订阅:与基于列表的发布-订阅不同,发布者对订阅者的了解更少(或没有)。发布者只是发布事件,然后广播事件。订阅者(或在分布式系统中,代表订阅者的服务)检查每个到达的事件,并确定发布的事件是否感兴趣。如果有大量消息且大多数消息对特定订阅者不感兴趣,此版本有可能非常低效
  • 基于内容的发布-订阅:与前两种变体不同,前两种变体通常被广泛归类为“基于主题”的方式。主题是预定义的事件或消息,组件订阅主题内的所有事件。而内容则更加通用。每个事件与一组属性相关联,并且仅当这些属性与订阅者定义的模式匹配时,事件才会传递给订阅者

实际上,发布-订阅模式通常由某种形式的面向消息的中间件实现,其中中间件作为代理来管理生产者和消费者之间的连接和信息通道。此中间件通常负责消息(或消息协议)的转换,以及消息的路由和有时存储消息。因此,发布-订阅模式继承了代理模式的优点和缺点

Shared-Data 共享数据模式

上下文:用于解决多个计算组件需要共享和操作大量数据。这些数据不仅属于这些组件中的任何一个。

问题:系统如何存储和操作被多个独立组件访问的持久数据?

解决方案:在共享数据模式中,交互主要由多个数据访问器至少一个共享数据存储之间的持久数据交换所主导。交换可以由访问器或数据存储发起。连接器类型是数据读取和写入。与共享数据系统相关联的通用计算模型是数据访问器执行需要来自数据存储的数据的操作,并将结果写入一个或多个数据存储。其他数据访问器可以查看并对该数据进行操作。在纯粹的共享数据系统中,数据访问器仅通过一个或多个共享数据存储进行交互。但是,在实践中,共享数据系统还允许数据访问器之间进行直接交互。共享数据系统的数据存储组件提供对数据的共享访问,支持数据持久性,通过事务管理管理对数据的并发访问,提供容错性,支持访问控制,并处理数据值的分布和缓存。

共享数据模式的特殊化在存储数据的性质方面有所不同,现有的方法包括关系、对象结构、分层和分层结构。

尽管数据共享对于大多数大型复杂系统来说是一项关键任务,但与此模式相关的一些潜在问题。首先,共享数据存储可能会成为性能瓶颈。因此,性能优化一直是数据库研究的一个常见主题。共享数据存储也有可能成为单点故障。此外,共享数据的生产者和消费者可能通过对共享数据结构的了解而紧密耦合。

共享数据模式的解决方案总结在表13.9中。

共享数据模式在各种数据项需要持久化并且有多个访问者时非常有用。使用这种模式的效果是将数据的生产者与消费者解耦;因此,这种模式支持可修改性,因为生产者对消费者没有直接了解。在一个或多个位置汇总数据并以常见方式访问有助于性能调优。与这种模式相关的分析通常集中在数据一致性、性能、安全性、隐私、可用性、可伸缩性以及与现有仓库及其数据的兼容性等质量上。

当系统有多个数据存储时,一个关键的架构问题是数据和计算到数据的映射。使用多个存储可能发生的原因是数据自然上或历史上被分割到可分离的存储中。在其他情况下,数据可能在几个存储中复制,以通过冗余提高性能或可用性。这些选择可能强烈影响上述所提到的质量。

图13.13显示了一个企业访问管理系统的共享数据视图的图表。有三种类型的访问者组件:Windows应用程序、网络应用程序和无头程序(在后台运行且不提供用户界面的程序或脚本)。

分配模式

Map-Reduce 模式

背景:企业急需快速分析它们生成或访问的大量数据,规模可达百万亿字节。示例包括社交网络网站上的互动日志、大规模文档或数据存储库,以及搜索引擎的 <源,目标> 网页链接对。用于分析这些数据的程序应该易于编写,运行高效,并且在硬件故障方面具有弹性。

问题:对于许多具有超大数据集的应用程序,对数据进行排序然后分析分组数据已经足够。Map-Reduce 模式解决的问题是高效地执行大数据集的分布式和并行排序,并为程序员提供指定要执行的分析的简单方法。

解决方案:Map-Reduce 模式需要三个部分:首先,专用基础设施负责在高度并行的计算环境中将软件分配给硬件节点并根据需要对数据进行排序。节点可以是独立的处理器多核芯片中的核心。其次和第三个是程序员编写的两个函数,可预见地称为 mapreduce

map 函数接受一个键(键 1)和一个数据集作为输入。map 函数的目的是过滤和排序数据集。所有繁重的分析都在 reduce 函数中进行。map 函数中的输入键用于过滤数据。数据记录是否需要进一步处理由 map 函数确定。map 函数中的第二个键(键2)也很重要。这是用于排序的键。map 函数的输出包括一个 <键2,值> 对,其中键2是排序值,而值是从输入记录派生的。

通过 map 和基础设施的组合来执行排序。map 输出的每个记录都通过键2哈希到磁盘分区。基础设施在磁盘分区上维护键2的索引文件。这允许以键2顺序检索磁盘分区上的值。

通过创建多个 map 实例来增强 Map-Reduce 的 map 阶段的性能,每个实例处理正在处理的磁盘文件的不同部分。图13.14 显示了 Map-Reduce 的 map 部分如何处理数据。输入文件被分成若干部分,并创建了多个 map 实例来处理每个部分。map 函数根据程序员指定的逻辑将其部分处理成若干分区。

Reduce 函数接收到所有 map 实例按排序顺序发出的所有 <key2, value> 对集合。Reduce 函数执行一些由程序员指定的分析,然后发出分析结果。输出集通常比输入集小得多,因此得名 "reduce"。术语 "load" 有时用来描述最终发出的数据集。图13.14 还显示了 reduce 处理的一个实例(可能有多个实例),称为 Reduce Instance 2。Reduce Instance 2 从各个 map 实例生成的所有 Partition 2 中接收数据。对于大文件,可能会有多次 reduce 迭代,但图13.14 中没有显示出来。

Map-Reduce 的一个经典教学问题是统计文档中单词出现的次数。这个示例可以使用单个 map 函数来完成。文档就是数据集。map 函数将查找文档中的每个单词,并为每个单词输出一个 <word, 1> 对。例如,如果文档以单词 "Having a whole book ..." 开头,那么 map 的第一个结果将是
<Having, 1>
<a , 1>
<who le, 1>
<book, 1>
在实际应用中,"a" 可能会被 map 过滤掉。

map 的伪代码可能如下所示:
map (String key, String value) :
  // key:文档名称
  // value:文档内容
  for each word w in value:
    Emit(w, "1");

reduce 函数将按排序顺序处理该列表,将每个单词的 1 相加以获取计数,并输出结果。

相应的 reduce 函数可能如下所示:
reduce (List <key, value>) :
  // key:一个单词
  // value:一个整数
  int result = 0;
  sort input;
  for each input value:
    for each input pair with the same word:
      result++;
    Emit(word, result);
    result = 0;

更大的数据集会导致一个更加有趣的解决方案。假设我们想要持续分析过去一小时的 Twitter 帖子,以查看当前正在 "热门" 的话题。这类似于统计数百万文档中的单词出现次数。在这种情况下,每个文档(推文)可以分配给其自己的 map 函数实例。(如果您没有成千上万的处理器可用,可以将推文集合分成与处理器农场中的处理器数量匹配的组,并逐批处理集合,一个组接一个组。)或者,我们可以使用字典来提供给我们一个单词列表,然后每个 map 函数可以分配给它自己要在所有推文中查找的单词。

还可以有多个 reduce 实例。通常,这些实例安排成多个阶段进行归约,每个阶段处理比前一个阶段的列表更小(具有较少的 reduce 实例)。最后一个阶段由一个单独的 reduce 函数处理,生成最终的输出。

当然,Map-Reduce 模式并不适用于所有情况。一些可能不适合采用这种模式的考虑因素包括:

  • 如果您没有大型数据集,那么使用 Map-Reduce 的开销是不合理的。
  • 如果您无法将数据集分成大小相似的子集,那么并行性的优势将会丧失。
  • 如果您需要多个 reduce 操作,这将会很复杂。

商业实现的 Map-Reduce 提供了基础设施,负责将函数实例分配给硬件、在硬件故障时进行恢复和重新分配(在高度并行的计算环境中经常发生),以及对沿途产生的大型列表进行排序等实用工具。

表13.10 总结了 Map-Reduce 模式的解决方案。

Map-Reduce确实是一些网络上最熟悉的公司的软件基础之一,包括Google、Facebook、eBay和Yahoo! 这些公司都使用了Map-Reduce模式来处理和分析大规模的数据,这有助于它们有效地管理和提取有价值的信息,以支持其在线服务和业务。这种模式已经成为处理大数据的重要工具,使这些公司能够应对庞大的数据集和复杂的分析需求。

Multi-tier 多层模式

多层模式是一个命令和控制(C&C)模式,或者是一个分配模式,具体取决于用于定义层次结构的标准。层次结构可以根据功能相似的组件来创建,这种情况下它是一个C&C模式。然而,在许多情况下,如果不是大多数情况下,层次结构是根据软件将运行的计算环境来定义的:企业系统中的客户层不会在托管数据库的计算机上运行。这使其成为一个分配模式,将软件元素映射到不同的层次结构,可能是通过应用C&C模式到计算元素而产生的。正因为如此,我们选择将其列为一个分配模式。

背景:在分布式部署中,通常需要将系统的基础设施分为不同的子集。这可能是出于操作或业务原因(例如,基础设施的不同部分可能属于不同的组织)。

问题:如何将系统分割为若干个计算上独立的执行结构,即一组由某些通信媒体连接的软件和硬件组成的结构?这样做是为了提供针对操作需求和资源使用进行优化的特定服务器环境。

解决方案:许多系统的执行结构都组织成一组逻辑组件的集合。每个组件集被称为一个层次结构。将组件分组到层次结构中可以基于各种标准,例如组件的类型、共享相同的执行环境或具有相同的运行时目的。

层次结构的使用可以应用于任何运行时组件的集合(或模式),尽管实际上它在客户端-服务器模式的上下文中最常用。层次结构引入了拓扑约束,限制了哪些组件可以与其他组件通信。具体来说,连接器只能存在于同一层或相邻层中的组件之间。许多Java EE和Microsoft .NET应用程序中的多层模式是从客户端-服务器模式衍生出的层次结构组织的一个示例。

此外,层次结构可能会限制在相邻层次结构之间可以发生的通信类型。例如,某些分层模式要求在一个方向上进行调用返回通信,但在另一个方向上采用基于事件的通知。

多层架构的主要弱点是其成本和复杂性。对于简单的系统,多层架构的好处可能无法证明其前期和持续成本的合理性,包括硬件、软件以及设计和实现的复杂性。

层次结构不是组件,而是组件的逻辑分组。此外,不要混淆层次结构和层次!分层是一种模块的模式(一种实现单元),而层次结构仅适用于运行时实体

表13.11总结了多层模式解决方案的部分。

层次结构使得更容易确保安全性,并以特定的方式优化性能和可用性。它们还增强了系统的可修改性,因为计算上独立的子组需要就交互协议达成一致,从而减少它们之间的耦合。

图13.15使用非正式的符号来描述Consumer Website Java EE应用程序的多层架构。该应用程序是Adventure Builder系统的一部分。许多组件和连接器类型都特定于支持平台,本例中是Java EE。

其他分配模式

除了抽象工厂模式之外,还有一些其他的分配模式,它们用于指导软件组件的分布和分配。

Microsoft发布了"Tiered Distribution"模式,该模式规定了在多层架构中将组件分配到它们将运行的硬件上的特定方式。

IBM的WebSphere手册描述了许多被称为"拓扑结构"的内容,以及选择它们之间的质量属性标准。例如,WebSphere版本6中描述了11种拓扑结构(专用部署模式),包括"单机拓扑结构(独立服务器)"、"反向代理拓扑结构"、"垂直扩展拓扑结构"、"水平扩展拓扑结构"和"带IP喷雾器的水平扩展拓扑结构"等。

还有一些已经发表的工作分配模式,它们通常以常用的团队结构的形式出现。例如,针对全球分布的敏捷项目的模式包括以下几种:

  • 平台:在软件产品线开发中,一个站点负责开发产品线的可重用核心资产,而其他站点负责开发使用这些核心资产的应用程序。
  • 能力中心:工作分配到站点,取决于站点的技术或领域专业知识。例如,用户界面设计由位于具有可用性工程专家的站点完成。
  • 开源:许多独立的贡献者根据技术集成策略开发软件产品。集中控制是最小的,除非独立的贡献者将其代码集成到产品线中。

策略和模式的关系

模式和策略一起构成了软件架构师的主要工具。它们之间有什么关系呢?

模式包括策略

正如我们在本章的引言中所说,策略是设计的“构建模块”,是构建架构模式的基础。策略是原子,而模式是分子。大多数模式由多种不同的策略组成,尽管这些策略可能都有一个共同的目标,比如促进可修改性,但它们通常被选择来促进不同的质量属性。例如,可以选择一种策略,使可用性模式更加安全,或者减轻可修改性模式对性能的影响。

考虑分层模式的示例,这是所有软件架构中最常见的模式(几乎所有非平凡系统都使用分层)。分层模式可以看作是几种策略的综合体,包括增加语义一致性、抽象公共服务、封装、限制通信路径和使用中介。例如:

  • 增加语义一致性。通过确保层的责任都能协同工作,而不过于依赖其他层,实现了增加语义一致性的目标。这样做将那些可能受到变更影响的责任绑定在一起。例如,处理硬件的责任应该分配到硬件层而不是应用层;硬件责任通常与应用责任没有语义一致性。
  • 限制依赖关系。层定义了一个顺序,并且只允许一个层使用其相邻的下层的服务。可能的通信路径数量减少到层数减一。这个限制对层之间的依赖关系有很大的影响,使得更容易限制替换层的副作用。

没有其中任何一个策略,模式可能会无效。例如,如果不使用限制依赖策略,那么任何层中的任何函数都可以调用任何其他层中的任何其他函数,破坏了使分层模式有效的低耦合。如果不使用增加语义一致性策略,那么功能可能会随机分散到各个层中,破坏了关注点的分离,从而破坏了首次使用层的修改便捷性的主要动机。

表13.12显示了Buschmann等人在书籍《面向模式的软件架构第1卷:模式体系》中描述的一些架构模式,以及它们使用的可修改性策略。

使用策略来增强模式

模式被描述为在一般上下文中解决一类问题的解决方案。当选择和应用模式时,其应用的上下文变得非常具体。因此,已记录的模式在特定情况下应用时通常是不足的。

要使模式在特定的架构上下文中发挥作用,我们需要从两个角度来审视它:

  • 模式所做的固有质量属性权衡。模式存在是为了实现特定的质量属性,因此我们需要比较它们促进(以及减弱)的那些属性与我们的需求。
  • 模式未直接关注但仍然会受到影响并在我们的应用中很重要的其他质量属性。

为了特别说明这些关切,以及如何在一般情况下使用策略来增强模式,我们将以代理模式为起点。

代理模式广泛用于分布式系统中,至少可以追溯到其在基于CORBA的系统中的关键作用。代理是任何大规模、动态、面向服务的架构的重要组成部分。

使用此模式,请求从服务器请求某些信息的客户端无需知道服务器的位置或API。客户端只需联系代理(通常通过客户端代理),这在图13.16的UML序列图中得到了说明

Broker模式的弱点

在第13.2节中,我们列举了代理模式的几个弱点。在这里,我们将更详细地研究这些弱点。代理模式在某些质量属性方面存在一些弱点。例如:

  • 可用性  。如果按照图13.6中建议的方式实施代理,则代理是一个单点故障。服务器、代理,甚至客户端的活动性都需要进行监视,并提供修复机制。
  • 性能。客户端(请求信息或服务的客户端)和服务器(提供信息或服务的服务器)之间的间接级别增加了开销,因此增加了延迟。此外,如果不希望直接在客户端和服务器之间进行通信(例如出于安全原因),代理可能成为潜在的性能瓶颈。
  • 可测试性。代理通常在复杂的多进程和多处理器系统中使用。这些系统通常非常动态。请求和响应通常是异步的。所有这些都使得测试和调试这些系统非常困难。但是代理模式的描述没有提供任何测试功能,例如测试接口、状态或活动捕获和回放功能等。
  • 安全性。因为代理模式主要用于系统跨越进程和处理器边界的情况,比如基于Web的系统,所以安全性是一个合理的关注点。但是,如所呈现的代理模式并没有提供任何用于验证或授权客户端或服务器的方法,也没有提供保护客户端和服务器之间通信的方法。

在这些质量属性中,代理模式主要与性能较差(它为系统带来松散耦合的代价已经被广泛记录下来)。它对这个列表中的其他质量属性不太关心;它们在大多数已发布的描述中没有提到。但正如其他弹头所示,它们可能是伴随代理的优点而带来的不可接受的“附带损害”。

使用策略改进代理模式

我们如何使用策略来填补“开箱即用”的代理模式与满足要求的要求之间的差距,以满足要求苛刻的分布式系统?以下是一些选项:

  • 增加可用资源性能策略将导致多个代理,以帮助提高性能和可用性。
  • 保持多个副本策略允许这些代理中的每一个共享状态,以确保它们对客户端请求做出相同的响应。
  • 负载平衡(调度资源策略的应用)将确保一个代理不会超载,而另一个代理闲置。
  • 心跳、异常检测或ping/echo将为复制的代理提供一种通知客户端和相互通知的方式,当其中一个代理处于离线状态时,用于检测故障。

当然,每种策略都会带来权衡。每种策略都会使设计变得更加复杂,这将需要更长时间来实施,成本更高,维护成本也更高。负载平衡引入了间接性,将为每个事务增加延迟,因此会消耗一些旨在增加性能的性能。而且负载平衡器是一个单点故障,因此也必须进行复制,进一步增加了设计成本和复杂性。

使用策略共同处理

策略,如第5-11章所述,是旨在管理单个质量属性响应的设计基元。当然,实际上几乎从不是这样的;每种策略都有其主要效果来管理可修改性或性能或安全性等等,它还有其副作用,其权衡。表面上看,对于架构师来说,情况似乎无望。无论你做什么来改善一个质量属性,都会危及另一个质量属性。我们之所以能够有利用策略,是因为我们可以衡量策略的直接和副作用,当权衡是可以接受的时候,我们就使用策略。这样做,我们在我们感兴趣的质量属性方面获得了一些好处,同时放弃了其他方面的东西(关于不同质量属性的东西,我们希望它的幅度要小得多)。

本节将通过一个示例来说明,该示例显示如何将策略应用于模式可以在一个领域产生负面影响,但添加其他策略可以带来缓解,将您放回可接受的设计空间。重点是展示您可以利用的策略之间的相互作用。正如某些液体组合是有害的,而其他液体组合可以产生像草莓柠檬汽水这样可爱的东西一样,策略可以要么使事情变得更糟,要么将您放在一个愉快的设计空间中。因此,以下是策略混合的演练。

考虑一个需要检测其组件故障的系统。用于检测故障的常见策略是ping/echo。让我们假设架构师已经决定采用ping/echo作为检测系统中故障组件的方法。每种策略都有一个或多个副作用,而ping/echo也不例外。与ping/echo相关的常见考虑因素包括:
• 安全性。如何防止ping flood攻击?
• 性能。如何确保ping/echo的性能开销很小?
• 可修改性。如何将ping/echo添加到现有架构中?

我们可以将架构师迄今为止的推理和决策表示如图13.17所示。

假设架构师确定性能权衡(添加ping/echo到系统的开销)最为严重。用于解决性能副作用的策略是增加可用资源。与增加可用资源相关的考虑因素包括:
• 成本。增加的资源成本更高。
• 性能。如何有效利用增加的资源?
现在,可以将这组设计决策表示如图13.18所示。

现在,架构师选择处理使用增加可用资源的后果,这些资源必须被有效地利用,否则它们只会增加系统的成本和复杂性。可以解决资源有效利用的策略是使用调度策略。与调度策略策略相关的考虑因素包括:

  • 可修改性。如何将调度策略添加到现有架构中?
  • 可修改性。将来如何更改调度策略?

包括调度策略策略的设计决策集可以如图13.19所示。

接下来,架构师选择处理使用调度策略策略的可修改性后果。用于处理调度器添加到系统中的策略是使用中介,它将调度策略的选择与系统的其余部分隔离开来。与使用中介策略相关的一个考虑因素是:

  • 可修改性。如何确保所有通信都通过中介进行?

现在,可以将基于策略的架构设计决策集表示如图13.20所示。

用于处理确保所有通信都通过中介的策略是限制依赖关系。与限制依赖关系策略相关的一个考虑因素是:

  • 性能。如何确保中介的性能开销不过大?

这个设计问题现在已经变得递归!在这一点上(实际上,在我们描述的设计决策树的任何一点上),架构师可能会确定中介的性能开销足够小,不需要进一步的设计决策。

连续应用策略就像在游戏空间中移动,有点像国际象棋:优秀的玩家能够看到他们正在考虑的移动的后果,而非常出色的玩家能够提前数步。在第17章中,我们将看到设计活动被视为“生成和测试”的练习:提出一个设计并测试它以查看是否令人满意。将策略应用于现有设计解决方案,例如模式,是生成随后测试的设计的一种技术。

总结

架构模式

  • 是一组在实践中反复出现的设计决策,
  • 具有已知的属性,允许重复使用,
  • 描述了一类架构。

因为模式(根据定义)在实践中反复出现,所以人们不会发明它们人们会发现它们

策略比模式简单。策略通常只使用单一结构或计算机制,它们旨在解决单一的架构力量。因此,与通常将多个设计决策组合成一个包的模式相比,策略在制定设计决策时为架构师提供更精确的控制。策略是设计的“基本构件”,由此创建了架构模式。策略是原子,模式是分子。

架构模式建立了以下关系:

  • 上下文。在世界上反复出现的常见情况,导致了一个问题。
  • 问题。在给定上下文中产生的问题,经过适当的泛化。
  • 解决方案。问题的成功架构解决方案,经过适当的抽象。

复杂系统同时展示多个模式。

模式可以根据它们显示的主要元素类型进行分类:模块模式显示模块组件和连接器模式显示组件和连接器分配模式显示软件元素(模块,组件,连接器)和非软件元素的组合。大多数已发布的模式都是C&C模式,但也有模块模式和分配模式。本章展示了每种类型的示例。

模式被描述为对一类问题的解决方案,在一般情况下。当选择和应用模式时,其应用上下文变得非常具体。因此,具有文档化的模式在应用于特定情况时存在不足。通过增加策略,我们可以使模式更具体,以适应我们的问题。连续应用策略就像在游戏空间中移动,有点像国际象棋:下一步的后果很重要,而向前看几步对于帮助很有帮助。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值