应用程序架构本质,第 2 部分: 设计模式入门
对于应用程序架构师而言,标识、应用和记录模式就像每日三餐一样普遍。模式涵盖很多复杂的方面,从应用程序体系结构的大型结构到特定的设计问题解决方案均包含在其中。为了保证设计的成功,必须考虑并能够应用现有模式。在本文中,您将了解如何标识在设计中
对于应用程序架构师而言,标识、应用和记录模式就像每日三餐一样普遍。模式涵盖很多复杂的方面,从应用程序体系结构的大型结构到特定的设计问题解决方案均包含在其中。为了保证设计的成功,必须考虑并能够应用现有模式。在本文中,您将了解如何标识在设计中重复出现的模式,以及如何记录其特征、优势和缺点。
从需求到形成有效的应用程序体系结构需要使用模型。建模 是记录模型在应用程序域内的状态和行为的过程。需要考虑这些模式才能帮助解决设计问题。随着时间的增长,域内的模式会变成交流设计优缺点的一种语言。
在关于应用程序体系结构的本系列的第二部分(即本文)中,我们将了解与设计模式相关的技能、工具和里程碑,从而帮助您解决常见问题。设计工作至少有一部分需要考虑如何解决存在冲突的各个需求,这些需求通常表述为影响因素 (force)。模式标识适用的影响因素,并提供对这些影响因素进行调和的解决方案。
技能和能力
作为应用程序架构师,您应该能够辨认需求和系统设计中涉及的影响因素。您应该熟悉现有模式,并能够标识和记录新的域特定模式。最后,您应该能够应用(或创建机制来便于其他人应用)域或系统设计内有用的各种模式。
熟悉现有模式
尽可能熟悉现有模式。(请参见参考资料部分提供的指向模式语言和存储库的链接。)通过全面认识和了解现有模式,可快速地标识和创建模式。不过请注意,不要对所有设计问题都勉强套用现有模式。
标识模式
能够确定在需求集或系统设计中适用的模式,这是应用程序架构师的一项主要技能。虽然这样说,但我认为开发生命周期中不应该存在模式相关的阶段或活动。由于模式出现在各个抽象级别,且涉及多个开发活动,甚至可以应用于开发模型本身,这就使得在需要时以及针对危险执行应急措施时都自然而然地想到进行模式标识和应用,但您并不需要过于纠缠这方面的工作。
记录模式
我此处非常想讨论两个领域:模式内容的记录和模式用法的记录。您不仅应该能够创建易于理解的模式的影响因素、上下文和解决方案方面的描述,而且还应该记录模式在设计和实现中标识、应用或适用的位置。随着时间的增长,通过简单的收集和重构,就应该能够构建适用于应用程序域的涵盖范围更为广泛的模式语言。我并不是建议在工作中每次使用模式时都进行记录。我们习惯于遵循模式行事,这种自然的驱使力量无法抗拒。请仅记录有助于提高系统整体质量的重要设计方法。
当然,您应该熟悉一个或多个有关模式记录的行业方法和格式,而且需要熟悉特定于您的环境的任何标准。有关现有模式存储库和书籍的信息,请参见参考资料中提供的链接。
抽象
我在本系列的 第 1 部分曾提到,抽象是需求分析的一项关键技能。勿庸置疑,这至少在有效使用设计模式方面也非常适用。当更改设计,以适应新需求时,请稍稍后退一步,抽象出细节,直到得到轴心点,在不影响现有元素的情况下可以在此轴心点处理特定类型的更改。这将很可能涉及到将现有模式作为候选解决方案来提供恰当的分离。
大部分模式都基于可插入性 (plugability),也称为模板与挂钩 (template-and-hook)。 将定义通用行为(模板),并提供域特定的行为和域类提供的状态(提供挂钩)。这是作为大部分模式基础的元模式,务必在开发自己的模式过程中记住这一点。
体系结构风格
体系结构风格 处理应用程序的全部或部分总体结构。特定的风格提供对应用程序的一个或多个质量属性的改进——通常以牺牲其他属性为代价。例如,管道和筛选风格允许在链的组件间实现方便的可组合性和松散偶合。不过,在此风格中的可分布性和可预测性会受到负面影响。您应该对其中的大部分结构设计原则加以熟悉——特别是您的应用程序域中应用的结构设计原则。另外,还要注意每个风格所具有的优缺点。
建模
正如我在本系列的 第一篇文章中提到的,建模是应用程序架构师的一项关键技能。大部分与设计模式相关的操作都在建模领域中进行。甚至底层编码模式也可应用于此领域,因为可以将代码视为解决方案的另一个模型。您应该能够使用建模语言实现模式,以在其他项目中使用,而且应该能够将现有模式应用到模型。
为了将模式作为模型实现,需要使用建模语言创建属于模式的模板内容。对于统一建模语言(Unified Modeling Language,UML),这可以为单个包,其中包括相应的子包、类、关系和接口;或者可以为完整的建模项目,其中包含针对模式解决方案各个方面的多个模型。无论使用哪个工具进行建模,我都找到了创建这些模板的方法,以便重用整个体系结构模型或单个分析模式。
目前有多种将模式作为首要概念处理的多种建模工具可用。在这些环境中,可以为模型创建和应用模式,并能利用各个级别的自动化功能来支持将模式角色映射到模型对象。(有关更多信息,请参见 工具和技术部分。)
要将模式应用到模型,涉及到将模式解决方案中的通用规则映射到模型中特定的域类。为此,您必须将模式需要的所有状态和关系添加到域类中。您还可能会希望更改模式的通用方面的名称,以与域的相应语言对应。例如,会计分析模式可能会包括
Account 和 Customer 之类的角色,而两个角色分别又涉及 getCustomer
和
getAccount
之类的操作。如果您的域包括希望对其应用模式角色的 CreditCard
和
Member
,则可能会希望更改操作,以与之对应(即 getMember
和
getCreditCard
)。您当然应该能够进行此映射工作。如果您的工具未实现此流程的自动化,请开发自己的脚本或宏。
|
|
工具和技术:分解应用程序
现代企业应用程序已变得相当复杂。应用程序体系结构中涉及的相互依赖组件的数量正在不断地增加。不过,我们仍然很少充分利用这种体系结构类型所带来的灵活性。让我们了解一下应用程序的实际情况以及企业应用程序的结构正如何发展为“分解”应用程序。
我将首先给出自己关于应用程序构成部分的简单定义。应用程序 是提供对计算资源的访问以支持特定用途的构造体。因此,大部分应用程序都包含以下组件:
- 规则——应用程序实现指定适用输入、输出可见性等的规则。
- 工作流——应用程序通过受支持的活动定义步骤序列。请注意,有些应用程序相对而言是“没有模式”,几乎不涉及工作流。另外,还可以利用上面提到的规则定义工作流。
- 子系统集成——应用程序可以与一个或多个子系统交互,在这些子系统之间提供受控集成。
- 事件——应用程序管理与外部及子系统间的事件流。此过程可以非常复杂(如异步事件队列),也可以非常简单(如重复调用子系统功能来调用行为、读取状态或传播状态的主循环)。
- 信息模型——应用程序在子系统的基础上定义其信息模型。信息模型是子系统状态和应用程序特定状态的组合视图。应用程序通常必须提供子系统间的一些信息映射。
- 绑定与传播——应用程序提供包括外部和动态配置、子系统状态视图和应用程序特定状态视图的信息模型。应用程序代码的大部分都涉及到在子系统间保持这些信息同步、响应更改以及将更改传播到相关组件。
所有这些导致了模式的出现:随着包括越来越多的支持应用程序功能的通用组件(例如,规则引擎、工作流引擎、企业服务总线),应用程序会变得越来越含混和分散,所包含的各个应用程序支持组件的声明规范和配置集合也变得越来越大。如果您是规模相当大的企业的架构师,请务必记住这一点。如果您所属的企业较小,也不要认为自己可以不受分解趋势的影响。您迟早(如果尚未开始的话)都会采用这种方式构建应用程序。
|
|
工具和技术:层次
到目前为止,我已经讨论了如何在多个抽象级别、应用程序整个周期甚至开发生命周期本身中标识、记录和应用模式。有了所有这些潜在的模式后,您需要采用某种方式对其进行组织。限制适用模式的一个方法是,根据其应用到的体系结构层次对模式进行分类。
图 1 中的关系图显示了不同类型的层次一种可能的安排方式。概念层次(业务、应用程序、信息和技术体系结构)显示在右侧。垂直部分是与应用程序及信息体系结构相关的逻辑层次。底部是技术层次,显示逻辑层次映射到物理基础设施的方式。这个安排显示了一个瘦客户机部署,其中的全部表示和一些应用程序逻辑都映射到了客户机物理层。
图 1. 层次映射
概念层次
概念层次 将非常抽象的模式空间划分为业务域模式、解决方案设计模式和基础设施模式。
逻辑层次
逻辑层次 根据层次的体系结构功能划分模式空间。可以采用多种方式定义逻辑层次,但通常分为三个或四个级别:
- 表示——支持信息显示和捕获外部事件及请求的组件。
- 应用程序——实现应用程序流、聚合信息和调用业务事务的组件。
- 业务——实现业务实体、规则、流程和事务的组件。
- 数据——提供对持久性数据和其他外部数据的访问的组件。
逻辑层作为应用程序部署体系结构的一部分射到物理层。这些映射也包含一些公用模式。例如,瘦客户机 或独立体系结构会将全部或大部分逻辑层映射到客户机物理层。分布式企业 Web 应用程序很可能会将逻辑层分布到一系列物理层。最后,富客户机 体系结构将表示层(还可以包括应用程序层)映射到客户机。
应用程序体系结构模式或原型 涵盖所有逻辑层,定义完整应用程序的总体结构。其他模式将位于较低的粒度级别,作用于单个层次中的组件或组件交互设计。另一类模式涵盖两个层次间的交互。这种划分模式空间的方式至少能让您在寻找现有模式或对新模式进行分类时找到着手点。
物理层次
物理层次 根据模式应用的物理部署平台对模式空间进行划分。这个类别中的模式与解决特定平台上的问题相关。例如,您可以有一个通用会话状态管理模式以及多个平台特定的模式,用于在 Microsoft® ASP.NET 和 JavaServer Pages (JSP) 站点中实现通用模式。
J2EE
Java™ 2 Platform Enterprise Edition (J2EE) 标准将应用程序的逻辑层作为容器堆栈定义。所定义的容器有:
- 客户机容器——支持对应用程序资源的客户端访问。
- Web 容器——处理客户机请求和用于进行显示的格式数据。
- EJB 容器——为业务逻辑的高可用性、高性能实现提供环境。
- 连接器——提供对数据库和大型机等外部资源的访问。
|
|
工具和技术:组件设计模式
基于组件的软件工程(Component-based Software Engineering,CBSE)通过接口连接组件来构造系统的方式。重要模式包括契约式设计、定义良好的接口、可组合性、可预测的行为和组件测试。目前已经有很多关于组件的分布、交互和构造的模式和模式语言。我下面将给出一些例子:
- 交互模式——组件交互模式可帮助解决与体系结构的组件的协作、通信及协调相关的设计问题。与体系结构风格类似,这些模式处理应用程序体系结构的大型结构。例如,“抽象交互”(Abstract Interactions) 模式建议将组件交互作为抽象接口进行定义。通过与“组件总线” (Component Bus) 模式结合(以及精心的设计),可以创建没有耦合的灵活组件体系结构。
- 分布模式——组件分布模式处理与异类平台和环境中分布组件相关的问题和影响因素。
- 连接模式——组件连接模式处理组件如何“连接”到一起(动态或静态)。
- 行为模式——组件行为模式处理组件及其环境间的交互。
- 部署模式——部署模式帮助处理组件部署相关的影响因素。例如,在分布式组件体系结构中,组件间的延迟有时候会由于分布开销而不能接受。组件打包模式允许在灵活性和分布式环境的其他属性以及“本地”组件交互的可预测性能之间求得平衡。
- 业务组件工厂——业务组件工厂方法在 Peter Herzum 和 Oliver Sims 撰写的书中进行了详细说明(请参见 参考资料)。此书给出了完整的模式框架,为企业的业务组件的规范、构造和部署工作提供支持。
- 依赖项注入—— 依赖项注入 是用于将对象或组件连接在一起的一项技术。依赖项注入不会构建对自身进行初始化的组件(包括创建依赖对象),而是将提供一个容器(或工厂),以查找或创建依赖对象和对所请求的对象调用 setter 挂钩来初始化其引用。通过此功能,可方便地重新配置组件,并能够方便地支持可插入设计,从而避免组件之间的硬编码依赖关系。这种类型的容器的一个很好的示例就是 Spring(请参见 参考资料)——支持为传统 Java 对象(Plain Old Java Object,POJO)进行依赖项注入的开源容器。组件使用可扩展标记语言(Extensible Markup Language,XML)配置文件连接在一起。
|
|
工具和技术:企业集成模式
企业集成模式讨论企业内应用程序和系统的大规模集成。这些模式侧重于应用程序及其他信息系统组件间的通信。消息(以及消息的创建、路由、响应和转换)是这些模式中的主要概念。
|
|
工具和技术:企业应用程序模式
企业应用程序模式处理应用程序及其子系统的体系结构内的设计问题。这些模式讨论表示、到关系数据存储的映射、域逻辑、应用程序状态管理和创建的应用程序功能。
|
|
工具和技术:分析模式
分析模式处理业务域或解决方案域。这些模式解决在建模业务流程和实体过程中及业务事务的实现方法中的设计问题。
|
|
工具和技术:原型 (Archetype)
原型 是在某个域或多个域中重复出现的通用概念和模式。可以将其映射到特定的域,并能用于进行模型转换、代码生成等工作。Party、Inventory、Purchase Order 和 Money 就是这方面的例子。虽然和分析模式类似,但原型的抽象级别更高。原型可以包括可选功能、选择功能或基于业务上下文的完整结构变体(异形结构)。
|
|
工具和技术:表示模式
表示模式处理信息的显示及处理操作信息或调用应用程序流的事件。此领域的模式的主要目标是将表示特定的方面从应用程序及业务逻辑分离出来。其中的一些例子包括:
- 模型-视图-控制器(Model-View-Controller,MVC)——此模式将应用程序模型的可视表示形式(视图)从模型本身分离。控制器负责更新视图和调度视图级别的事件。
- 表示-抽象-控制(Presentation-Abstraction-Control,PAC)——此模式在很多方面与 MVC 相似,也将功能分离为类似的角色。PAC 比 MVC 更为抽象和通用,可能更适合在用户界面(User Interface,UI)之外的领域使用。从某些方面而言,此模式与层次相关,因为可以使用类似的模式来设计每个层次边界的交互。一个层次的模型就是另一个层次的视图。通过将此模式作为组织体系结构原则使用,可为“可插入”体系结构提供极大的支持。
- 前端控制器 (Front controller)——前端控制器适用于请求/响应类型的交互,将使用单个对象来接收请求、进行调度、路由并收集响应。
- 显示服务器——此模式将表示实现定义为显示信息和订阅事件的抽象服务。此模式最成功的用法是 X Windows 系统。
|
|
工具和技术:面向服务的体系结构
从很多角度而言,面向服务的体系结构(Service-Oriented Architecture,SOA)是对基于一组常见影响因素(与以组件为基础的应用程序相关)的模式集的标准化。组件充当服务实现,而服务相关的基础设施处理关于连接、交互等等的决策,如上面的 工具和技术:组件设计模式中所述。
服务组件体系结构
服务组件体系结构(Service Component Architecture,SCA)是定义了一种声明语言(基于 XML)的 IBM 项目,可将此语言用于指定组件的组装以及其内部和外部服务接口依赖关系。可以采用 Spring 的方式将组件连接到一起,但 SCA 将组件连接功能安排到了实现模块的分布边界之外。此标准现在由 Open Service-Oriented Architecture Collaboration 进行管理。
模型驱动的体系结构
模型驱动的体系结构(Model-Driven Architecture,MDA)中支持模型转换和充实的功能也可以支持基于模式的就地模型转换。IBM® Rational® Software Modeler 和 Rational Software Architect 提供了定义模式并将其直接应用于模型的支持。ArcStyler(iO Software 的产品)为 MDA 提供了成熟而广泛的支持,可以用于应用模式转换。
|
|
里程碑
架构师的工作中总会涉及到模式的标识和应用。由于这个原因,很难在项目周期中确定特定的点来说明模式工作的进展。不过,我希望在此讨论应用程序生命周期中一些重要的部分,应该在这些方面寻找和确定应用程序中的模式。
体系结构
应用程序体系结构是主要的交付内容,而在大多数情况下,模式在其开发中扮演着极为重要的角色。标识体系结构风格及在体系结构中使用的其他大型模式具有多方面的作用。模式可以带来以下好处:
- 使体系结构更易于理解和说明。
- 通过所使用的模式的已知特征重点突出主要体系结构决策和优缺点。
- 允许重用模型和事项的较大组成部分,特别组织依赖于最佳实践模式作为框架和工具时更是如此。
域模型
域建模并非真的很新,但关于如何进行相关工作的信息却比较少。Eric Evans 撰写的书 Domain-Driven Design(请参见 参考资料)提供了一组用于建模域的模式,其中包括用于标识及确定域边界和细化域设计的技术。
通过使用这些技术来记录域,将极大地提高应用程序体系结构的有效性和持久适用性。
分析模型
大部分域通常都得到了大家的认可和实现了自动化。因此,域模型能够充分地利用设计良好模式语言。请寻找为您的域提供全部或部分支持的可用模式。如果没有此类模式,请考虑自行创建一个。这些模式应该为在域或标准模式中经常重复出现的模式(如 money 和 currency 模式)。
设计和实现
同样,在应用程序生命周期的设计和实现阶段有很多方法和时机使用模式,很难指定与模式相关的特定交付内容。不过,基于模式和抽象的恰当使用的体系结构将更易于理解和维护。您应该能够演示在何种情况下使用模式(在特定区域内使用或作为体系结构指导原则使用)处理了某个需求或提高了体系结构的总体质量。
方法
在我看来,建议的开发方法属于应用程序体系结构的一部分。在网络和一些文献资料中提供了很多开发模式。其中一些重要的例子有:
- 统一流程 (Unified process)——一种迭代式的分阶段开发方法,在 IBM Rational 和 IBM Rational Unified Process® (RUP®) 的推动下得到了广泛的应用。RUP 包括可以用于配置基本统一流程框架的工作流和模版。
- RM-ODP——开放式分布处理的参考模型(Reference Model for Open Distributed Processing,RM-ODP)是系统体系结构规范的建模方法。RM-ODP 将系统的规范和建模拆分为视角,其中每个视角处理一组特定的考虑事项。
- 极限编程(Extreme Programming,XP)——XP 是轻量级方法,避免了与“测试第一”方法相关的正式建模和分析。系统需求作为用户案例捕获。用户案例转换为相应功能的测试,然后编写代码实现测试用例。此模式最适合用于小型的技能娴熟的团队,难以扩展为大型项目。
- Scrum:另一个轻量级方法,侧重于短期的成就(团队针对其提交特定的交付内容)。会频繁评估进度并根据风险和需求优先级的要求调整工作投入。
|
|
总结
处理模式是架构师日常工作中所必须涉及的内容。正如我在 第 1 部分提到的,抽象和建模是两项关键技能。二者都与模式有关系。您必须能够将相关抽象从具体需求中提取出来,从而不仅形成体系结构的总体概貌及其指导原则,还能处理关键设计问题,或在设计和实现的较低级别对风险带来的更改做好准备。
另外,并非所有内容都是模式,也不是每个模式都值得记录和发布。应用不恰当或不必要的模式可能会莫明其妙地让您的设计变得复杂。请始终记住每个模式的优缺点,并对影响其使用的实际影响因素进行评估。