# 3.6架构模式

\
模式在软件的设计和开发中是一个重要的工具。在软件开发的许多领域都存在模式——例如，设计模式、架构模式、分析模式、软件组织模式和教学模式。\
架构模式的分类是按照弗兰克·布施曼（Frank Buschmann）的四类系统进行的。其基本概念是以模式所解决的问题作为分类的基础。

#### 3.6.1 适应性系统

此类别中的模式支持应用程序的扩展以及它们对不断发展的技术和不断变化的功能需求的适应。

**3.6.1.1依赖注入**

在面向对象设计中，由于需要创建一个抽象接口的具体实例，经常会出现问题。\
• 谁管理所使用实例的生命周期?\
• 谁决定在运行时最终实例化哪个具体的类?

为此，此模式提供了一个独立的构建块，即：装配器。

装配器在运行时确定如何解决上述问题。装配器将依赖对象的特定实例的引用传递过去。它可以被视为一种“通用工厂”。

它首先检查 ServiceUser 对于必要的依赖项（Service），并通过元信息生成或确定提供所需服务的 ServiceImplementation。然后，它将此服务实现“注入”到 ServiceUser 中，从而将类与其依赖项解耦。

已有的用于依赖注入的 Java 实现有:\
• JEE 6:上下文和依赖注入(JSR - 299)\
• Spring框架

<figure><img src="https://3692050972-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FggUOxqxsCZOTHblhkbzD%2Fuploads%2FMK3GchXeCD1IKJlgS1YL%2Fimage.png?alt=media&#x26;token=9d06c5c3-ef59-4a80-a70d-b5923740ba11" alt=""><figcaption></figcaption></figure>

<p align="center">图3-13依赖注入</p>

#### &#xD;3.6.2 交互系统

交互系统模式支持交互式软件系统的结构化。

**3.6.2.1 模型-视图-控制器模式**

将应用程序移植到不同的平台不应导致整个应用程序的重构。在这里，目标是简单地更改或扩展以及重用各个组件。

用户界面经常变化。相同的信息必须以不同的方式在不同的窗口中提供，导致所需框架的复杂性。不同的用户组需要不同的布局或格式。在模型的一致视图和过度更新导致的性能问题之间很难取得平衡。

<figure><img src="https://3692050972-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FggUOxqxsCZOTHblhkbzD%2Fuploads%2FQGgkTWLZmRwqesTw1tpq%2Fimage.png?alt=media&#x26;token=dcb26c11-dc36-46ad-9856-b6d5fee96e1c" alt=""><figcaption></figcaption></figure>

<p align="center">图3-14Model-view-controller</p>

为了解决这个问题，用户界面被分为三个责任区域。模型封装了通常稳定的业务逻辑及其数据。视图组件提供模型的视图。控制器处理用户事件，执行相应的业务逻辑，并触发视图组件的更新。

这种方法的一个很好的例子是电子表格程序，它为相同的数据模型提供了详细的表格视图和易于理解的图表视图。

**3.6.2.2Model-view-presenter模式**

模型 - 视图 - 展示器（MVP）是一种面向用户界面应用的交互式软件系统的架构模式。它基于模型 - 视图 - 控制器模式，侧重于严格分离用户界面和业务逻辑（分离视图和模型）。此模式背后的原则是将应用程序分解为三个组件:

• 模型包含业务逻辑和视图所需的所有数据。它仅由展示器控制，既不知道视图也不知道展示器。\
• 视图代表表示层，设计极其简单。它绝对不包含控制逻辑，仅负责展示和接收用户输入。\
• 展示器将视图链接到模型，并控制各层之间的逻辑流。它收集所有应用程序用例，接受来自视图的用户输入，调用模型中的方法，并在视图中设置数据。

<figure><img src="https://3692050972-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FggUOxqxsCZOTHblhkbzD%2Fuploads%2Fn9c5XNGqvtJJxpUn4EGF%2Fimage.png?alt=media&#x26;token=9edd7523-3cc8-4e08-a796-99b5948efca5" alt="" width="460"><figcaption></figcaption></figure>

<p align="center">图3-15Model-view-presenter</p>

由于表示层结构简单，视图可以被其他视图替换。通过这种方式，系统可以进行修改以便在不同平台上使用，并且与 MVC 相比，易于测试。自 2004 年马丁·福勒（Martin Fowler）定义以来，MVP 已被用于富客户端的开发。

与 MVC 相比，MVP 中的视图与模型绝对没有关系，因为与模型的交互是通过展示器严格控制的。这导致三个组件之间的责任分布略有不同。在 MVP 情况下，模型通过接口提供给展示器。相比之下，由于没有接口，MVC 模型与控制器的耦合更强。使用 MVC 方法，视图了解模型并实现数据同步。而在被动的 MVP 视图中，展示器接管数据绑定，视图不知道模型。此外，MVC 没有接口，因此更难替换。

**3.6.2.3Presentation-abstraction-control**

应用程序功能的增加也会增加用户界面的复杂性。对于复杂的用户界面，不同的功能区域可能会相互混杂，从而降低了可维护性。此外，简单的分解（如在 MVC 模式中）会导致，当所有用户事件都由单个控制器处理时，响应时间不令人满意等问题。

在这种模式中，用户界面的结构被分解为分层协作的“代理”。中级使用基本功能，然后为用户界面的各个底层元素提供功能。每个代理都由一个控制器、抽象和视图组成。控制器是代理与层次结构中上下层代理的接口，并控制其责任区域。抽象将完整模型的部分适配为一个本地模型，该本地模型仅包含本地视图所需的元素。这种严格的分层分离能够在用户界面内实现处理操作的并行化，特别是在只有完整模型的部分可用的情况下。

Eclipse IDE 就是一个很好的例子：工作区提供了菜单栏、工具栏、工作区和状态栏。透视图将这些作为嵌入的操作元素提供给每个透视图的主题区域使用。这些包括边距视图和编辑器窗口的区域，然后由特定的内容编辑器和视图用它们自己的菜单项填充。

<figure><img src="https://3692050972-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FggUOxqxsCZOTHblhkbzD%2Fuploads%2F1kwDYFvU14VFv8WTZNWi%2Fimage.png?alt=media&#x26;token=ad5e3594-1c81-4b7e-ae58-a126ae8d89b0" alt=""><figcaption></figcaption></figure>

<p align="center">图3-16Presentation-abstraction-control</p>

#### &#xD;3.6.3从混乱到结构

此类别中的模式用于避免组件和对象的混乱。特别是，它们为将高级系统任务分解为协同子任务提供支持。

**3.6.3.1 分层架构**

此模式有助于大型应用程序的结构化。重点在于开发一个复杂的系统，其主要特征是相互构建的复杂服务的混合。

相互依赖的高级和低级操作形成相互访问的功能，但可以细分为具有相同抽象级别的层。为了实现可重用性和/或可移植性，细分为封闭层，以便后续更改的影响仅影响该层。

问题的解决方案在于将系统水平分层堆叠，这些层封装了相同抽象级别的操作。抽象级别随着下层数量的增加而增加。信息交换通过称为服务的接口进行。较高层使用其下面一层提供的服务。不允许跨多层通信。这种分离导致各个层在特定的处理方面（如数据存储或用户交互）专业化。

<figure><img src="https://3692050972-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FggUOxqxsCZOTHblhkbzD%2Fuploads%2F8ZUPHtW2LnAZ3qvJSLWB%2Fimage.png?alt=media&#x26;token=3b2f853f-2943-4f7f-8eeb-58f4d6375690" alt="" width="341"><figcaption></figcaption></figure>

<p align="center">图3-17分层架构</p>

这个简单的概念减少了组件之间可能的依赖关系，并提供了更高的可重用性。然而，如果一层仅仅将提供特定服务的请求传递到下一层，也可能导致开销增加。此外，诸如添加数据字段之类的更改对所有层都有垂直影响。

性能问题可以通过跳过特定层来解决，尽管这再次创建了额外的依赖关系。

**3.6.3.2 管道和过滤器**

pipes-and-filters架构模式基于一系列处理单元（过滤器），它们通过数据通道（管道）相互连接。每个过滤器将其结果直接转发给下一个过滤器。管道将中间结果从一个过滤器传输到下一个过滤器，这涉及到流程各个方面的解耦:

• 时间顺序上(直接或有时移)\
• 传输机制/格式\
• 下一个过滤器的动态确定

并行、负载共享、可选过滤器

<figure><img src="https://3692050972-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FggUOxqxsCZOTHblhkbzD%2Fuploads%2FuedBNCb7WkgwpK02gBzZ%2Fimage.png?alt=media&#x26;token=4d4399b2-5345-4476-96ee-be29d8f7892b" alt=""><figcaption></figcaption></figure>

<p align="center">图3-18管路和过滤器</p>

过滤器彼此不知道对方，并且可以通过管道以任何顺序组合，从而为各个管道和过滤器提供了高度的可重用性。缺点是处理过程中出现的错误状态难以处理。

这种架构模式的典型使用示例有:\
• 编译器，在每个处理步骤之后逐步处理并转发结果。典型阶段有词法分析、解析器和代码生成器。\
• 数字信号处理，具有以下过滤器

图像采集、颜色校正、图像效果和压缩，它们都相互转发数字图像数据。

**3.6.3.3 Blackboard**

几个专门的子系统提供它们的知识，以创建一个可能不完整或近似的解决方案。\
图3-19展示了黑板模式的UML图。\\

<figure><img src="https://3692050972-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FggUOxqxsCZOTHblhkbzD%2Fuploads%2Fl5pS37HVm8UtTvpGZDNM%2Fimage.png?alt=media&#x26;token=c36aea07-0e0a-4a98-a1e6-577c6fe852ee" alt=""><figcaption></figcaption></figure>

<p align="center">图3-19Blackboard</p>

黑板的元素包括:\
• 一个或多个独立的知识源，从特定的角度分析问题，并将解决方案提议发送到黑板\
• 一个中央黑板，管理知识源的解决方案方法或解决方案元素\
• 一个控制组件，监控黑板，并在必要时控制知识源的执行

黑板模式的使用示例有图像处理、图像识别、语音识别和系统监控的软件系统。

#### &#xD;3.6.4 分布式系统

此类别中的模式对经过验证的任务分配形式以及子系统相互通信的方法进行了说明。

**3.6.4.1 Broker**

软件行业的当前发展导致了新的应用需求。软件必须能够在分布式系统上运行，但必须不受此类系统中不断发生的结构修改的影响。

应用程序需要访问的资源可以随意分布，因此各个软件组件必须能够访问这些分布式资源。在这种情况下，透明度是关键。对于单个组件，只有使用的服务的可用性是相关的，而服务在系统中的物理提供位置无关紧要。另一个因素是系统会不断进行修改。这意味着参与流程的组件很可能在运行时发生变化。

<figure><img src="https://3692050972-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FggUOxqxsCZOTHblhkbzD%2Fuploads%2FQ1mllhuXnqm9eDXubTA6%2Fimage.png?alt=media&#x26;token=699484c7-da33-474a-a117-69020fde7496" alt=""><figcaption></figcaption></figure>

<p align="center">图3-20Broker</p>

应用程序必须通过适当的措施来弥补这一点。必须避免应用程序用户必须（或能够）参与架构细节的情况。

在分布式应用程序的架构模型中，引入了一个“代理”组件。这充当了服务器和客户端之间通信的一种交换中心。代理组件是通信的中心点。每个服务器独立地向代理注册。对于服务器要提供的每个服务，在该服务器上实现相应的服务接口，并将这些接口传达给代理。当客户端希望访问特定服务时，它们将请求发送给代理。然后，代理为相应的服务定位可用的服务器，并将客户端的请求转发给它。在处理请求后，服务器将响应发送回代理，代理将响应转发给正确的客户端。

**3.6.4.2 面向服务**

面向服务的架构（SOA）将软件构建块的功能接口表示为分布式、可重用、松耦合的服务，这些服务通过标准化方法进行访问。

SOA定义了三个角色:\
**服务提供者**提供服务，并在目录服务中注册这些服务。**目录服务**发布由服务提供者注册的服务。**服务消费者**在目录中搜索特定服务，并通过目录服务响应消费者查询提供的引用调用它。然后建立到相应服务提供者的链接，并且可以使用该服务。

<figure><img src="https://3692050972-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FggUOxqxsCZOTHblhkbzD%2Fuploads%2FuR4zOpeZQhVsnJOdTY7C%2Fimage.png?alt=media&#x26;token=ae7be532-b52d-48cc-8131-49b3633dff7b" alt=""><figcaption></figcaption></figure>

<p align="center">图3-21SOA</p>

一般来说，服务提供低粒度的接口。当一个服务只需几次调用就能实现复杂功能时，就使用低粒度这个术语。

理想情况下，这些服务是无状态的、事务上自包含的和幂等的——换句话说，无论使用相同的输入数据调用它们多少次，它们总是提供相同的结果。

服务由契约式服务接口（用于将服务消费者与服务提供者链接起来）和服务实现组成。服务实现不属于契约的一部分，只要符合接口承诺，就可以替换。

服务与位置无关，并且可以在任何时间、从任何位置激活，只要消费者和应用程序具有适当的访问权限（“位置透明性”）。

**3.6.4.3 模块化**

模块化是指将软件系统合理地分解和安排为子系统和组件的术语。模块化的核心任务是将整个系统细分为组件，然后这些组件可用于映射应用程序的逻辑结构。模块化的目的是通过定义和记录清晰的边界来降低系统的复杂性。

在一个系统内结合不同的任务会增加其出错的可能性。在与执行的任务逻辑上不相关的区域中产生的不想要的副作用很难追溯和纠正。

创建单独的模块作为功能和责任区域的容器。系统耦合通过明确定义的接口进行，这些接口描述了模块之间的关系。功能适用性、完整性和简单性是模块创建中部分相互冲突的目标。

与分层架构相比，模块化允许创建单独的垂直系统和分离的责任区域。

**3.6.4.4 微服务**

微服务是创建和集成分布式系统的一种重要架构模式。这种方法涉及将大型系统构建为小型的功能单元。每个微服务都应该代表一个不同的功能单元。微服务是高度解耦的并且独立运行。与不应相互交流的独立系统相反，微服务可以同步和异步地相互通信。微服务是分别开发的，并且彼此独立地投入生产使用。
