Quarkus实践指南:构建新一代的Kubernetes原生Java微服务
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.3 GoF设计模式的Quarkus 实现

2.3.1 GoF设计模式简介

自从GoF(Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides这4位博士)的《设计模式:可复用面向对象软件的基础》问世以来,全世界的开发者形成了学习、使用设计模式的热潮。在本节中,会结合Quarkus 和具体语言来完成GoF设计模式的实现,一方面对设计模式进行了诠释和给出了案例说明,另一方面也可以学习Quarkus 开发的一些基本技巧。

GoF设计模式总共23种,可分为如下3类。

(1)创建型模式(Creational Pattern):主要负责对象的创建工作,其应用过程不再是简单地直接实例化对象。创建型模式包括工厂方法(Factory Method)模式、抽象工厂(Abstract Factory)模式、构造器(Builder)模式、原型(Prototype)模式及单例(Singleton)模式等 5种模式。

(2)结构型模式(Structural Pattern):一般用于复杂的用户界面和统计数据,结构型模式描述了如何组合类与对象以形成更大的结构,包括适配器(Adapter)模式、桥梁(Bridge)模式、组合(Composite)模式、装饰(Decorator)模式、门面(Facade)模式、享元(Flyweight)模式和代理(Proxy)模式等7种模式。

(3)行为型模式(Behavioral Pattern):主要用于精确定义系统中对象之间的通信流程,以及在一些相当复杂的程序中如何控制该流程。通常可以分为职责链(Chain of Responsibility)模式、命令(Command)模式、解释器(Interpreter)模式、迭代器(Iterator)模式、调停者(Mediator)模式、备忘录(Memento)模式、观察者(Observer)模式、状态(State)模式、策略(Strategy)模式、模板(Template)模式和访问者(Visitor)模式等 11 种模式。

本节按照 23 种设计模式分别完成实现和讲解。首先给出设计模式的经典概念,并结合设计模式来分析和解析;然后针对设计模式虚拟应用场景进行介绍;最后对应用场景进行编程实现。

2.3.2 GoF设计模式案例的Quarkus 源码结构及演示

获取代码,可以从GitHub上克隆预先准备好的示例代码:

该程序位于“018-quarkus-sample-gof23”目录中,是一个Maven工程项目。

导入Maven工程项目,这是一个典型的Maven工程的结构。程序结构如图2-10所示。

图2-10 程序结构

在基于 Quarkus 的 GoF 设计模式中,每个模式程序都由其名称、位置和主程序类组成。表2-3列出了程序的设计模式名称、程序源码位置和主程序类表。

表2-3 程序的设计模式名称、程序源码位置和主程序类表

续表

有3种方式可以演示应用程序的执行效果。

第 1种是在通过 Quarkus 开发模式启动程序后,在命令行窗口中键入相关内容,查看演示效果。

例如,要查看工厂方法模式的演示效果,步骤如下。

(1)在程序目录下输入命令mvnw compile quarkus:dev,启动程序。

(2)在命令行窗口中键入命令curl http://localhost:8080/gof23/factorymethod。

(3)在Quarkus 的命令行窗口中可以看到输出结果,如图2-11所示。

图2-11 工厂方法模式在开发模式下的演示效果图

对于这种模式,程序目录中有一个执行文件 quarkus-sample-gof23-test.cmd,可以一次性演示全部效果。

第 2种是在 Gof23Application类上通过 Quarkus 命令模式启动程序,然后在命令行窗口中键入相关内容,查看演示效果。

例如,要查看工厂方法模式的演示效果,步骤如下。

(1)在开发工具(如Eclipse)中执行Gof23Application类的run命令,启动程序。

(2)在命令行窗口中键入命令curl http://localhost:8080/gof23/factorymethod。

(3)在开发工具(如Eclipse)的控制台窗口中可以看到输出结果,如图2-12所示。

图2-12 工厂方法模式在命令模式下结合外部调用的演示效果图

对于这种模式,程序目录下有一个执行文件 quarkus-sample-gof23-test.cmd,可以一次性演示全部效果。

第3种是在设计模式的各个主程序类上,通过命令模式运行程序,查看演示效果。

例如,要查看工厂方法模式的演示效果,步骤如下。

(1)首先打开工厂方法模式的 FactorymethodClient 类文件,然后在开发工具(如Eclipse)中执行run命令,启动程序。

(2)在开发工具(如Eclipse)的控制台窗口中可以看到输出结果,如图2-13所示。

图2-13 工厂方法模式在命令模式下的演示效果图

其他22种设计模式都可以参照上述3种方式来查看演示效果。

2.3.3 案例场景、说明和Quarkus 源码实现

为了方便理解程序,下面对设计模式及其应用场景进行简要说明,这样可以了解各个案例具体的实现过程并理解Quarkus 使用方法。

1.工厂方法模式

(1)标准定义和分析说明

工厂方法模式标准定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。工厂方法模式是一个创建型模式,它要求工厂类和产品类分开,由一个工厂类根据传入的参数决定创建哪一种产品类的实例,但这些不同的实例有共同的父类。工厂方法把创建这些实例的具体过程封装了起来,当一个类无法预料将要创建哪个类的对象或一个类需要由子类来指定创建的对象时,就需要用到工厂方法模式了。

(2)应用场景举例

比如某一类公司能提供一种产品,但是这种产品有不同的型号。当客户需要一种产品,但是没有具体给出是哪一种型号,只是提供了一些产品参数时,公司就根据这些参数来提供产品,这就是工厂方法模式。

在这里,可以把公司(Company)理解为抽象工厂角色;把 CompanyA 理解为具体工厂(Concrete Creator)角色;把 Product 理解为抽象产品(Product)角色;把 ProductA 和ProductB理解为具体产品(Concrete Product)角色。图 2-14(遵循 UML 2.0规范绘制)是实现该应用场景的类结构图。

图2-14 工厂方法模式的应用案例类结构图

2.抽象工厂模式

(1)标准定义和分析说明

抽象工厂模式标准定义:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式是一个创建型模式,与工厂方法模式一样,它要求工厂类和产品类分开。但是核心工厂类不再负责所有产品的创建,而是将具体的创建工作交给子类去做,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节,由一个具体的工厂类负责创建产品族中的各个产品。其实质就是由 1个工厂类层次、N个产品类层次和N×M个产品组成的。

(2)应用场景举例

比如,几家公司同时能生产计算机和电话,但是计算机系列包括 PC 机、笔记本电脑和服务器,电话系列包括座机电话和手机,对于这种情况就可以采用抽象工厂模式。

在这里,可以把 Company 理解为抽象工厂(Abstract Factory)角色;把 CompanyA 和CompanyB理解为具体工厂(Concrete Creator)角色;把Computer和Telephone理解为两类不同的抽象产品(Product)角色;把 NotebookComputer 和 PersonalComputer 理解为基于Computer抽象产品的具体产品(Concrete Product)角色;把DesktopPhone和Mobile理解为基于 Telephone 抽象产品的具体产品(Concrete Product)角色。图 2-15(遵循 UML 2.0 规范绘制)是实现该应用场景的类结构图。

图2-15 抽象工厂模式的应用案例类结构图

3.构造器模式

(1)标准定义和分析说明

构造器模式标准定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。构造器模式属于创建型模式,它就是将产品的内部表象和产品的生成过程分割开来,从而使一个构建过程生成具有不同内部表象的产品对象。构造器模式使得产品内部表象可以独立变化,客户不必知道产品内部组成的细节。构造器模式可以强制实行一种分步骤进行的构造过程。构造器模式就是解决这类问题的一种思想方法——将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

(2)应用场景举例

比如,公司要做一个软件项目,该软件项目由可行性研究、技术交流、投标、签订合同、需求调研、系统设计、系统编码、系统测试、系统部署和实施、系统维护等多个过程组成,但是不同的项目由不同的过程组成,对于这种情况就可以采用构造器模式。

对于非投标项目 ProjectA,只有需求调研、系统设计、系统编码、系统测试、系统部署和实施、系统维护等过程,这时就可以采用构造器模式。可以把 AbstractProjectProcessBuilder 理解为抽象构造者(Builder)角色;把 ConcreteProjectProcessBuilder 理解为实现抽象构造者(Builder)角色的具体构造者(Concrete Builder)角色;把 ProjectA理解为导演者(Director)角色和产品(Product)角色的结合。图 2-16(遵循 UML 2.0 规范绘制)是实现该应用场景的类结构图。

图2-16 构造器模式的应用案例类结构图

4.原型模式

(1)标准定义和分析说明

原型模式标准定义:用原型实例指定创建对象的种类,并且通过复制(克隆)这些原型创建新的对象。原型模式也是一种创建型模式。当一个系统应该独立于它的产品创建、构成和表示,以及要实例化的类是在运行时指定的时候,可使用原型模式。原型模式适用于任何等级结构。原型模式的缺点是每一个类都必须配备一个克隆方法。

(2)应用场景举例

比如,公司对各个产品都有自己的宣传资料,每个宣传资料都是首先对公司进行介绍,然后对公司组织结构进行介绍,中间内容才是对产品的技术介绍、案例说明,最后还要留下公司的通信联系方式。不同产品的宣传资料中公司介绍、组织结构介绍和通信联系方式都是一样的,这样就可以采用原型模式,从基本的公司产品资料中克隆出一个介绍模板,然后根据具体产品来加上产品的技术参数。

在这里,可以把 AbstractPrototype 类理解为抽象原型(Prototype)角色,把CompanyBaseIntroduction理解为具体原型(Concrete Prototype)角色。图2-17(遵循UML 2.0规范绘制)是实现该应用场景的类结构图。

图2-17 原型模式的应用案例类结构图

5.单例模式

(1)标准定义和分析说明

单例模式标准定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例模式属于创建型模式。单例模式就是采取一定的方法保证在整个软件系统中对某个类只能存在一个对象实例,并且其他类可以通过某种方法访问该实例。单例模式只应在有真正的“单一实例”的需求时才可使用。单例模式只有一个角色,就是要进行唯一实例化的类。

(2)应用场景举例

比如,公司规定一个市场用户只能由一个市场人员跟踪。最初用户联系公司的时候,任命一个市场人员负责这个用户,以后这个用户再联系公司时,仍由指定的这个市场人员负责。

在这里,SaleMan 类是一个要求唯一实例化的类,ServiceManager 类是一个提供唯一实例化方法的类。图2-18(遵循UML 2.0规范绘制)是实现该应用场景的类结构图。

图2-18 单例模式的应用案例类结构图

6.适配器模式

(1)标准定义和分析说明

适配器模式标准定义:将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。适配器模式属于结构型模式。适配器模式也叫变压器模式,也叫包装器模式。

(2)应用场景举例

比如,公司的客户与公司设计代码的开发人员直接进行交流比较困难,这时加入一个需求分析人员,就可以使事情变得简单。客户把自己的想法告诉需求分析人员,需求分析人员把用户需求转化成需求分析,并告诉设计代码的开发人员如何进行设计和实现。需求分析人员就是一个适配器,把两个毫无联系的人员匹配起来。在软件外包行业内就有这样的实际情况。

在这里,可以把 Customer 类理解为目标(Target)角色;把 Designer 类理解为源(Adapter)角色;把Analyst类理解为适配器(Adapter)角色。图2-19(遵循UML 2.0规范绘制)是实现该应用场景的类结构图。Analyst类继承自Customer类并关联Designer类。

图2-19 适配器模式的应用案例类结构图

7.桥梁模式

(1)标准定义和分析说明

桥梁模式标准定义:将抽象部分与它的实现部分分离,使它们都可以独立变化。桥梁模式属于结构型模式,它将抽象化与实现化脱耦,使得二者可以独立变化,也就是说将它们之间的强关联变成弱关联,指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立变化。

(2)应用场景举例

比如,公司有几个技术部门,分别是研发部、开发部和售后服务部,这些部门都有培训和开会等工作。培训的时候,要有培训老师、培训教材、培训人员和培训教室。开会也一样,要有会议主持人、开会地点等。因此,可以把部门理解为抽象单位,研发部、开发部和售后服务部继承自抽象单位并实现具体的工作。日常工作可以抽象,培训和开会继承自日常工作,不同部门的日常工作是不同的。

在这里,可以把 AbstractDepartment 类理解为抽象化(Abstraction)角色;把AbstractAction类理解为实现化(Implementor)角色;把DevelopmentDep类、FinanceDep类和MarketDep类理解为修正抽象化(Refine Abstraction)角色;把Meeting类和Training类理解为具体实现化(Concrete Implementor)角色。图 2-20(遵循 UML 2.0 规范绘制)是实现该应用场景的类结构图。

图2-20 桥梁模式的应用案例类结构图

8.组合模式

(1)标准定义和分析说明

组合模式标准定义:将对象组合成树形结构以表示“部分-整体”的层次结构,定义了包含基本对象和组合对象的类层次结构,使得用户对单个对象和组合对象的使用具有一致性。组合模式属于结构型模式,其就是一个处理对象的树结构模式。组合模式把部分与整体的关系用树结构表示出来,使得客户端同等看待一个个单独的成分对象与由它们复合而成的合成对象。

(2)应用场景举例

比如,团队(组织)是一个总体的抽象类,集团公司、公司、工厂、部门、班组、项目组都是团队,都可以继承团队,但是团队本身也是有层次结构的。我们要构架一个软件公司,就要这样先形成公司,再形成公司下面的部门,接着形成部门下面的项目组。如果构架一个工厂性质的公司,那么在形成公司根节点后开始形成公司下属的工厂,在工厂下面再形成车间。

在这里,可以把抽象组织(AbstractOrganization)类理解为抽象构件(Abstract Component)角色;把公司(Corporation)类、工厂(Factory)类、部门(Department)类理解为树枝构件角色,在其下面还有组织;把车间(Workshop)类和项目组(WorkTeam)类理解为树叶(Leaf)构件角色,其下面已经没有组织了。图2-21(遵循UML 2.0规范绘制)是实现该应用场景的类结构图。公司(Corporation)类、工厂(Factory)类、部门(Department)类、车间 (Workshop)类和项 目组(WorkTeam)类 全部都继 承自抽象 组织(AbstractOrganization)类。

图2-21 组合模式的应用案例类结构图

9.装饰模式

(1)标准定义和分析说明

装饰模式标准定义:动态地给一个对象添加额外的职责,以达到扩展其功能的目的,是继承关系的一个替代方案,提供了比继承更多的灵活性。装饰模式属于结构型模式。动态地给一个对象增加功能,这些功能也可以再动态地撤销,增加由一些基本功能的排列组合而产生的大量功能。

(2)应用场景举例

比如,公司的软件工程都是由需求分析、设计、编码、测试、部署和维护组成的。这只是一般过程,但是万一要加上需求分析验证,或要加上设计验证等过程,这时就可以通过装饰模式来实现。软件工程过程是抽象构件角色;标准软件工程过程是具体构件角色,定义一个将要接收额外责任的类;附加验证是装饰角色;需求分析验证是具体装饰角色。

在这里,可以把 AbstractProcess 类理解为抽象构件(Abstract Component)角色;把StandardProcess类理解为具体构件(Concrete Component)角色;把AdditionalProcess类理解为一种装饰(Decorator)角色;把 DesignCheckProcess 类和 RequestVerificationProcess 类理解为具体装饰(Concrete Decorator)角色。图2-22(遵循UML 2.0规范绘制)是实现该应用场景的类结构图。AbstractProcess 抽象类有两个子类,一个是 StandardProcess 类,另一个是AdditionalProcess 类。AdditionalProcess 类不仅继承自 AbstractProcess 抽象类,而且还关联AbstractProcess 抽象类。DesignCheckProcess 类和 RequestVerificationProcess 类是继承自AdditionalProcess类的子类,它们是附加类的具体实现。

图2-22 装饰模式的应用案例类结构图

10.门面模式

(1)标准定义和分析说明

门面模式标准定义:为子系统中的一组接口提供一致的界面,门面模式定义了一个高层接口,这个接口使得子系统更容易使用。门面(Facade)模式也叫外观模式,属于结构型模式。外部与一个子系统的通信必须通过一个统一的门面对象进行,每一个子系统只有一个门面类,而且该门面类只有一个实例,也就是说它是一个单例模式,但整个系统可以有多个门面类。

(2)应用场景举例

比如,公司基本上都有前台,来访人员可分为几类,一类是过来工作的,一类是访客,一类是快递员,还有就是来视察的领导等,他们都需要经过前台。这时就可以通过门面模式来实现。

在这里,可以把Facade类理解为门面(Facade)角色;把DoWork类、Inspection类、Post类和Visit类理解为子系统(Subsystem)角色。图2-23(遵循UML 2.0规范绘制)是实现该应用场景的类结构图。Facade类聚合DoWork类、Inspection类、Post类和Visit类等4个类。

图2-23 门面模式的应用案例类结构图

11.享元模式

(1)标准定义和分析说明

享元模式标准定义:以共享的方式高效地支持大量的细粒度对象。享元模式属于结构型模式。享元模式能做到共享的关键是其区分了内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所改变。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态,它们是相互独立的。将可以共享的状态和不可以共享的状态从常规类中区分开来,将不可以共享的状态从类中剔除。客户端不可以直接创建被共享的对象,而应当使用一个工厂对象负责创建被共享的对象。享元模式大幅度地降低了内存中对象的数量。

(2)应用场景举例

比如,公司里有资料需要共享,这些资料包括技术文档、财务文档、行政文档、管理文档、日常文档等。享元对象的外蕴状态就是技术、财务、行政、管理、日常等类别。

在这里,可以把Document(文档类)抽象类理解为抽象享元(Flyweight)角色;把TechnicalDocument(技术文档)类、FinancialDocument(财务文档)类、AdministrativeDcoment(行政文档)类理解为具体享元(Concrete Flyweight)角色;把DocumentRepository(资料库)类理解为享元工厂(Flyweight Factory)角色。图2-24(遵循UML 2.0规范绘制)是实现该应用场景的类结构图。TechnicalDocument类、FinancialDocument类和AdministrativeDocument类都继承自Document抽象类。Document抽象类与DocumentRepository类是聚合关系,即DocumentRepository实例化对象会包容多个Document实例化对象。

图2-24 享元模式的应用案例类结构图

12.代理模式

(1)标准定义和分析说明

代理模式标准定义:为其他对象提供一种代理以控制对这个对象的访问。代理模式属于结构型模式。代理就是一个人或一个机构代表另一个人或者另一个机构采取行动。在某些情况下,客户不想或者不能直接引用一个对象,这时代理对象可以在客户和目标对象之间起到中介的作用。客户端分辨不出代理主题对象与真实主题对象,代理模式可以不知道真正的被代理对象,而仅持有一个被代理对象的接口,这时代理对象不能创建被代理对象,被代理对象必须由系统的其他角色代为创建并传入。

(2)应用场景举例

比如,公司为了拓展业务,在 A 省设置办事处。所有在 A 省的用户请求都通过该办事处转达给公司,其中办事处就是一个代理机构。

在这里,可以把 AbstractOrganization 类理解为抽象主题(Abstract Subject)角色;把Agency 类理解为代理主题(Proxy Subject)角色;把 Corporation 类理解为真实主题(Real Subject)角色。图2-25(遵循UML 2.0规范绘制)是实现该应用场景的类结构图。Corporation只用继承自AbstractOrganization类,而Agency类既要继承自 AbstractOrganization类,还要聚合Corporation类。

图2-25 代理模式的应用案例类结构图

13.职责链模式

(1)标准定义和分析说明

职责链模式标准定义:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间出现耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它。职责链模式属于行为型模式。在职责链模式中,各种服务组合或对象由每一个服务或对象对其下家的引用接起来形成一个整体的系统链。请求在这个链上传递,直到链上的某一个对象决定处理该请求。客户并不知道链上的哪一个对象最终会处理请求,系统可以在不影响客户端的情况下动态地重新组织链和分配责任。处理请求者有两个选择:承担职责或者把职责推给下家。一个请求最终可能不被任何接收端的对象所接收。职责链可以提高系统的灵活性,通过配置多变的职责链可以完成系统功能的扩充或改变,保证系统的可移植性。

(2)应用场景举例

比如,公司技术部门有几位技术高手,当菜鸟在工作中遇到问题时,向这些高手请教,如果第一位高手能解决问题,那这个过程就结束,否则传递给下一位高手,下一位高手也执行同样的操作,不能解决问题的话就再交给下下一位高手。就这样,要么这些高手中的其中一位能解决问题,要么这些高手全都不能解决问题,这就是职责链模式。

在这里,可以把AbstractSuperMan抽象类理解为抽象处理者(Abstract Handler)角色;把SuperManOne 类、SuperManTwo 类和 SuperManThree 类理解为具体处理者(Concrete Handler)角色。图 2-26 (遵循 UML 2.0 规范绘制)是实现该应用场景的类结构图。SuperManOne 类、SuperManTwo 类和 SuperManThree 类继承自 AbstractSuperMan 抽象类,AbstractSuperMan抽象类进行自我关联。

图2-26 职责链模式的应用案例类结构图

14.命令模式

(1)标准定义和分析说明

命令模式标准定义:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销操作。命令模式属于行为型模式。命令模式把一个请求或者操作封装到一个对象中,把发出命令的职责和执行命令的职责分开,委派给不同的对象,允许请求的一方和发送的一方相互独立,使得请求的一方不必知道接收请求一方的接口,更不必知道请求是怎么被接收的,以及操作是否执行、何时执行、怎么执行。另外,系统支持命令的撤销。

(2)应用场景举例

比如,公司的管理者对下属安排工作就可以通过命令模式。管理者是客户角色;命令角色是一个抽象类;安排工作就是具体命令角色,具体要求包括编写工作计划、上报工作报告等;下属就是接收者角色。比如,老李这个管理者安排小王编写工作计划和上报工作报告。

在这里,可以把 Manager 类理解为客户(Client)角色;把 Command 抽象类理解为命令(Command)角色;把 Computer 类理解为一种抽象产品(Abstract Product)角色;把PlanCommand 类和 ReportCommand 类理解为具体命令(Concrete Command)角色;把Subordinate类理解为接收者(Receiver)角色。图2-27(遵循UML 2.0规范绘制)是实现该应用场景的类结构图。Manager类聚合Command抽象类,Command抽象类聚合Subordinate类,PlanCommand类和ReportCommand类继承自Command抽象类。

图2-27 命令模式的应用案例类结构图

15.解释器模式

(1)标准定义和分析说明

解释器模式标准定义:给定语言,定义其文法的一种表示,并同时提供一个解释器,这个解释器使用该表示来解释语言中的句子。解释器模式属于行为型模式。解释器模式将描述怎样在有简单文法后使用模式设计、解释语句。解释器模式提到的语言是指任何解释器对象能够解释的任何组合。在解释器模式中,需要定义一个代表文法的命令类的等级结构,也就是一系列的组合规则。每一个命令对象都有一个解释方法,代表对命令对象的解释。命令对象的等级结构中的对象的任何排列组合都是语言。

(2)应用场景举例

比如,公司接了一个项目,不同的部门对项目有不同的理解。技术部门从技术角度来说明这个项目的情况,而市场部门从市场角度来诠释这个项目,财务部门从财务角度来解释这个项目。在这种情况下就可以采用解释器模式。

在这里,可以把 AbstractExpression 类理解为抽象表达式(AbstractExpression)角色;把FinancialDepExpression类文件、MarketDepExpression类文件和 TechnicalDepExpression类文件理解为终结符表达式(Terminal Expression)角色;把 Project类理解为环境(Context)角色。图 2-28(遵循 UML 2.0 规范绘制)是实现该应用场景的类结构图。AbstractExpression 类为抽象类,FinancialDepExpression 具体类、MarketDepExpression 具体类和 TechnicalDepExpression具体类继承自AbstractExpression抽象类。Project类与AbstractExpression类存在依赖关系。

图2-28 解释器模式的应用案例类结构图

16.迭代器模式

(1)标准定义和分析说明

迭代器模式标准定义:提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。迭代器模式又称迭代子模式,属于行为型模式。多个对象聚在一起形成的总体被称为聚合,聚合对象是能够包容一组对象的容器对象。迭代器模式将迭代逻辑封装到一个独立的子对象中,从而与聚合本身隔开。迭代器模式简化了聚合的接口。每一个聚合对象都可以由一个或一个以上的迭代器对象组成,每一个迭代器的迭代状态可以是彼此独立的。迭代算法可以独立于聚合角色变化。

(2)应用场景举例

比如,公司想统计所有员工中有硕士文凭的人数和他们的姓名,可以把所有员工都放到一个集合中,然后一个一个地询问他们是否是硕士,这样就可以知道有多少名硕士了。

在这里,可以把Iterator抽象类理解为抽象迭代器(Iterator)角色;把ImplementIterator类理解为具体迭代器(Concrete Iterator)角色;把 EmployeeCollection 类理解为具体聚合(Concrete Aggregate)角色。图 2-29(遵循 UML 2.0 规范绘制)是实现该应用场景的类结构图。ImplementIterator 类实现了 Iterator 接口并关联 EmployeeCollection 类。Employee 类聚合EmployeeCollection类,即EmployeeCollection包容多个Employee。

图2-29 迭代器模式的应用案例类结构图

17.调停者模式

(1)标准定义和分析说明

调停者模式标准定义:用一个中介对象来封装一系列的对象交互。中介对象使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。调停者模式也叫中介者模式,属于行为型模式。当某些对象之间的作用发生改变时,不会立即影响其他对象之间的作用,调停者模式可以保证这些作用彼此独立变化。调停者模式将多对多的相互作用转化为一对多的相互作用,将类与类之间的复杂相互关系封装到一个调停者类中。调停者模式将对象的行为和协作抽象化,使对象在小尺度行为上与其他对象的相互作用分开。

(2)应用场景举例

比如,公司有很多项目,项目包括项目工作和项目人员。但有的时候,一些项目人员过多,一些项目人员过少,一些项目工作量大,一些项目工作量小,这就需要技术总监充当调停者,把一些项目的人员调到另一些项目上,或者把一些项目的工作安排给其他项目。规则不允许项目经理之间自我调整,而必须由技术总监来调停,这就是调停者模式。

在这里,可以把 Mediator 抽象类理解为抽象调停者(Abstract Mediator)角色;把TechnicalDirector类理解为具体调停者(Concrete Mediator)角色;把 AbstractProject抽象类理解为抽象同事类(Abstract Colleague)角色;把 ProjectA 类和 ProjectB 类理解为具体同事类(Concrete Colleague)角色。图 2-30(遵循 UML 2.0 规范绘制)是实现该应用场景的类结构图。TechnicalDirector 类一方面继承自 Mediator 抽象类,另一方面关联 AbstractProject 抽象类。ProjectA类和ProjectB类继承自AbstractProject抽象类,同时关联TechnicalDirector类。

图2-30 调停者模式的应用案例类结构图

18.备忘录模式

(1)标准定义和分析说明

备忘录模式标准定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。备忘录模式属于行为型模式。备忘录对象是一个用来存储另一个对象内部状态的快照对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉住并外部化、存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。

(2)应用场景举例

比如,公司领导在每周周一都要召开项目会议,每次会议后都提交会议纪要。会议纪要需要汇总现阶段项目情况,而这些项目情况就是备忘录,上面有时间戳标志。

在这里,可以把 Meeting 类理解为发起人(Originator)角色;把 Caretaker 类理解为负责人(Caretaker)角色。图 2-31(遵循 UML 2.0 规范绘制)是实现该应用场景的类结构图。Memento类与Caretaker类的关系是聚合关系,即Caretaker类拥有多个Memento类。

图2-31 备忘录模式的应用案例类结构图

19.观察者模式

(1)标准定义和分析说明

观察者模式标准定义:定义对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并获得自动更新。观察者模式属于行为型模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。这一模式主要针对两个对象 Object和 Observer。一个 Object对象可以有多个 Observer对象,当一个 object对象的状态发生改变时,所有依赖于它的Observer对象都会得到通知并获得自动更新。

(2)应用场景举例

比如,公司的通信录是员工都会用到的,同时也经常发生变化。每次通信录变化时,都要把更新后的通信录分发给所有公司员工。这时就可以采用观察者模式。

在这里,可以把AbstractAddressBook抽象类理解为抽象主题(Abstract Subject)角色;把AbstractEmployee 抽象类理解为抽象观察者(Abstract Observer)角色;把 CompanyAddressBook类理解为具体主题(Concrete Subject)角色;把 CompanyEmployee 类理解为具体观察者(Concrete Observer)角色。图 2-32(遵循 UML 2.0 规范绘制)是实现该应用场景的类结构图。CompanyAddressBook类继承自AbstractAddressBook抽象类,CompanyEmployee类继承自AbstractEmployee 抽象类。AbstractEmployee 抽象类关联 AbstractAddressBook 抽象类,即AbstractEmployee类有AbstractAddressBook类的属性。

图2-32 观察者模式的应用案例类结构图

20.状态模式

(1)标准定义和分析说明

状态模式标准定义:允许一个对象在其内部状态改变时改变它的行为,对象看起来就像修改了它的类一样。状态模式属于行为型模式。状态模式可被理解为在不同的上下文中相同的动作导致的结果不同。状态模式把所研究对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的子类。状态模式的意图是让一个对象在其内部状态改变的时候行为也随之改变,需要对每一个系统可能取得的状态创建状态类的子类。当系统的状态发生变化时,系统便改变相应的子类。

(2)应用场景举例

比如,公司的项目有这么几个状态:项目立项、项目开发、项目试运行、项目验收、项目维护、项目结项等。当项目启动时,需要进行项目立项工作;在项目立项完成后,接下来的工作是项目开发;在项目开发完成后,工作变成了项目试运行;在项目试运行完成后,进入了项目验收阶段;在项目验收完成后,进行项目维护;在维护工作结束后,最后是项目结项。这时整个项目就全部完成了。因此,项目在不同的状态下有不同的工作内容。通过设置项目状态,我们可以知道针对不同状态的项目应该采取什么样的工作。

在这里,可以把 State 抽象类理解为抽象状态(Abstract State)角色;把ProjectBuilderState类、ProjectDevelopmentState类、ProjectMaintenanceState类、ProjectRunState类和 ProjectEndState 类理解为具体状态(Concrete State)角色;把 Project 类理解为环境(Context)角色。图 2-33 (遵循 UML 2.0 规范绘制)是实现该应用场景的类结构图。ProjectBuilderState类、ProjectDevelopmentState类、ProjectMaintenanceState类、ProjectRunState类和 ProjectEndState类继承自 State抽象类。Project类关联 State抽象类,即 State类是 Project类的一个属性。

图2-33 状态模式的应用案例类结构图

21.策略模式

(1)标准定义和分析说明

策略模式标准定义:定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换,该模式使算法可独立于使用它的客户而变化。策略模式属于行为型模式,通过分析策略模式可以发现:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立类中,从而使得它们可以相互替换。策略模式使算法可以在不影响客户端的情况下发生变化,把行为和环境分开。环境类负责维持和查询行为类,而各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减、修改都不会影响环境和客户端。

(2)应用场景举例

比如,公司项目涉及多个行业,对于不同的行业,项目有不同的做法。行业项目就是环境(Context)角色,有一个抽象策略(Abstract Strategy)角色。而银行行业项目管理策略、能源行业项目管理策略、电信行业项目管理策略、政府项目管理策略全部都继承自抽象策略并有各个行业的实现模式。

在这里,可以把 Project 类理解为环境(Context)角色;把 Strategy 抽象类理解为抽象策略(Abstract Strategy)角色;把 BankStrategy 类、GovernmentStrategy 类、TelecomStrategy 类理解为具体策略(Concrete Strategy)角色。图 2-34(遵循 UML 2.0规范绘制)是实现该应用场景的类结构图。BankStrategy类、GovernmentStrategy类和TelecomStrategy类继承自Strategy抽象类,Project类关联Strategy抽象类。

图2-34 策略模式的应用案例类结构图

22.模板模式

(1)标准定义和分析说明

模板模式标准定义:定义一个操作中算法的骨架,而将一些步骤推后到子类中执行。Template Method 使子类不改变一个算法的结构就可以重定义该算法的某些特定步骤。模板模式属于行为型模式,其准备了一个抽象类,将部分逻辑以具体方法和具体构造形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,这样剩余逻辑获得了不同的实现。即先制定一个顶级逻辑框架,将逻辑的细节留给具体的子类去实现。

(2)应用场景举例

比如,公司研发项目的过程是可行性研究、需求分析、总体设计、详细设计、系统编码、系统测试、系统部署、系统维护等标准过程,这些可以形成接口,但是为了简化工作,也可以形成一个抽象的模板类,把这些步骤全部都实现了。如果不能实现,那么就使用抽象方法。现在有某个具体项目,其中的总体设计和详细设计与模板不同,这时就可以采用模板模式。

在这里,可以把ProjectProcessTemplate抽象类理解为抽象类(Abstract Class)模板角色;把ProjectA类和ProjectB类理解为具体类(Concrete Class)模板角色。图2-35(遵循UML 2.0规范绘制)是实现该应用场景的类结构图。ProjectA 类和 ProjectB 类都继承自ProjectProcessTemplate抽象类并实现ProjectProcess接口。

图2-35 模板模式的应用案例类结构图

23.访问者模式

(1)标准定义和分析说明

访问者模式标准定义:表示一个作用于某对象结构中各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式属于行为型模式,其目的是封装一些施加于某种数据结构元素之上的操作。一旦需要修改这些操作,就接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未确定的系统,它把数据结构和作用于结构上的操作解耦,使得操作集合可以相对自由地演化。访问者模式使得增加新的操作变得很容易,即增加一个新的访问者类;还将有关行为集中到一个访问者对象中,而不是分散到一个个节点类中。当使用访问者模式时,要将尽可能多的对象浏览逻辑放到访问者类中,而不是放到它的子类中。访问者模式可以跨几个类的等级结构访问属于不同等级结构的成员类。

(2)应用场景举例

比如,公司一般都要接受多方面的审查。工商部门的审查主要看是否符合商务审计,税务部门的审查主要看是否合法纳税,会计师事务所的审查主要是对公司进行财务审计。这些部门都是外部参观者,是抽象访问者(Abstract Visitor)角色;工商部门、税务部门和会计师事务所是具体访问者角色。需要定义一个抽象公司的抽象节点角色,不同的公司工商情况、税务情况和会计情况就是具体节点角色。

在这里,可以把 Visitor 抽象类理解为抽象访问者(Abstract Visitor)角色;把AccountingFirm 类、TaxBureau 类、TradeBureau 类理解为具体访问者(Concrete Visitor)角色;把 AbstractCompany 抽象类理解为抽象节点(Abstract Node)角色;把 CompanyA 类和CompanyB类理解为具体节点(Concrete Node)角色。图2-36(遵循UML 2.0规范绘制)是实现该应用场景的类结构图。AccountingFirm类、TaxBureau类、TradeBureau类继承自Visitor抽象类,CompanyA类和CompanyB类继承自AbstractCompany抽象类,AbstractCompany抽象类关联Visitor抽象类。

图2-36 访问者模式的应用案例类结构图