设计模式问答(四)

什么是桥接模式?

桥接模式有助于将抽象从实现中解耦。这样如果实现发生变化但是不会影响到抽象,反之亦然。如图“抽象和实现”所示,开关为抽象层,电子设备是实现;而开关可以被任何电子设备实现,因此开关是一种抽象思维而设备是实现。

图1:抽象和实现

下面我们编写开关和设备例子的代码。首先要做的事情是将实现和抽象分开到两个类总。图“实现”展示了‘IEquipment’接口,该接口包含‘Start()’和‘Stop()’两个方法。我们实现了两个设备,一个是冰箱,另一个是电灯泡。

图2:实现

第二部分是抽象。这个例子中的抽象部分是开关,它有一个‘SetEquipment’方法来设置对象;‘On’方法调用设备的‘Start’方法,‘off’调用设备的‘stop’方法。

图3:抽象

最后是客户端代码。我们之前分别创建了实现对象和抽象对象,下面我们可以单独使用他们。

图4:桥接模式客户端代码

什么是组合模式?

很多时候对象是以树形结构组织的,开发者必须去理解叶子对象和分枝对象的区别。这使得代码变得复杂,错误率提升。例如以下是一个简单的树形结构对象,消费者是主对象,每个消费者有多个地址,每个地址有多个电话对象。

图5:通常处理

我们假设想要插入完整的对象树。简单的代码示例如下,循环所有消费者、消费者中的地址对象以及所有地址中的电话对象。下面代码片段中在循环内调用了更新方法。

以上代码的问题是每个对象都需要更新。对于消费者来说是‘UpdateCustomer’、地址类为‘UpdateAddress’、电话类为‘UpdatePhone’。换句话说就是主对象和被包含的叶子对象被区别对待,这种方式会导致应用混乱增加出错几率。

如果能够统一对待主对象和叶子对象,那么代码就会变的整洁。以下代码中我们创建了一个接口(IBusinessObject),该接口限制所有类如消费者、地址、电话类使用统一的接口。统一接口以后,所有对象就都有一个“Update”方法了。

为了实现组合模式我们首先创建了一个接口,如下代码片段中所示:

强制所有根节点以及叶子节点使用该接口,如下所示:

同样,地址对象也强制实现该接口。

电话对象同理,如下所示:

什么是装饰器模式?

提示:装饰器模式增加了动态叠加行为来帮助我们在运行时改变对象的行为。

有很多情况下我们需要在运行时为一个类增加动态叠加行为。其中叠加是非常重要的词。例如,让我们考虑以下一个酒店销售面包餐的例子;他们有3个主要的产品,订单可以包含以下几种组合:

  • 简易面包

  • 面包和鸡肉

  • 面包和饮料

  • 面包和鸡肉、饮料

换句话说,订单流程的行为和订单成本在运行时的变化取决于组合的类型。

下面的例子是一个只有面包的订单,有两个函数‘prepare’和‘calculatecost’。我们将根据客户需求来在基础面包订单上动态增加新产品。

以下是所有订单的接口,每个订单都有如Prepare和CalculateCost两个功能。

基础产品是面包,实现了IOrder接口。可以在面包订单上增加新的产品并改变整个订单的行为。

我们可以使用装饰器模式动态选择面包订单。实现装饰器模式有以下5个步骤:

步骤1:创建一个装饰器类,并聚集需要动态增加行为的对象或接口

这个装饰器类将持有这些对象,所有对主对象方法的调用都要先调用所有持有对象的方法才调用主对象方法。

因此,如果调用了prepare方法,这个装饰器类将调用所有持有类的prepare方法,最后才调用该类的prepare方法。我们可以在下图看到引入装饰器类后输出结果是如何变化的。

步骤2:被持有的对象或接口指向需要初始化的类。我们可以通过多种方式实现该例子,下面我们将开放一个简单的构造函数并传递对象到该构造函数来初始化所持有对象。

步骤3:我们将实现IOrder接口并通过virtual方法调用所持有对象的方法。大家可以看到我们创建了virtual方法来调用所持有对象的方法。

步骤4:我们已经完成了重要的步骤,如创建装饰器。现在需要创建对象行为对象,这种对象可以被添加到装饰器并在运行时改变对象的行为。

以下是一个简单的鸡肉订单,该订单可以被添加到面包订单来创建出一个不同订单,叫做鸡肉+面包订单。鸡肉订单是通过继承订单装饰器类来被创建的。

任何对该对象的调用,首先要调用鸡肉订单的自定义函数,接着才调用持有对象的函数。例如当调用prepare方法时,他先调用准备鸡肉方法然后调用所持有对象的prepare方法。

花费计算也会加入鸡肉的花费,并调用所持有订单的花费来计算总额。

同理,准备订单饮料,如下所示:

步骤5:之后一步是观察装饰器模式的应用。所以在客户端我们可以写一些代码如创建一个面包订单。

以下是上面代码的输出结果:

创建一个包含有面包、鸡肉、饮料的订单代码逻辑如下:

输出结果如下:

换句话说,我们现在可以将这些行为附加到主对象之上并在运行时动态改变其行为。

以下是我们可以生成不同订单的组合,这样我们可以动态选择订单的行为。

什么是门面模式?

门面模式位于一组子系统之上并允许这些子系统以一种统一的方式交互。

图6:门面和子系统

图“订单门面”展示了门面模式的一个实例。为了下订单系统需要和产品、支付、发票类交互。所以订单成了一个门面来统一产品、支付和发票类。

图7:订单门面

图“门面模式实践”展示了类‘clsorder’通过实现‘PlaceOrder’函数来统一使用‘clsproduct’、’clsproduct’、‘clsInvoice’类。

图8:门面模式实践

什么是责任链模式(COR)?

当我们有一系列处理逻辑时使用责任链模式比较合适。下面我们来了解下这句话的意思。假设,一个请求需要一系列处理;因此请求被第一个句柄处理,他可以处理一部分或者不能处理,一旦完成,该句柄将请求传入链总的下一个处理。这种流程将一直持续直到有处理收到请求后完成处理。

图9:责任链模式概念

下面我们通过一个简单例子来了解这个概念。图“简单例子”中有一些处理逻辑,其中有3个处理将通过;也就是处理1做一些处理传给处理2,处理2做一些处理后传递给处理3来完成整个处理。

图10:简单例子

图“责任链模式类图”中,3个处理都继承自一个抽象类。其中重要的一点是每个处理点都会调用下一个需要处理的过程。所以在处理类中我们加入了一个额外的处理对象‘objProcess’,这个对象指向系一个需要被调用的处理对象。

图11:责任链模式类图

定 义好所有类以后既可以写客户端调用了。我们分别创建3个处理过程对象process1、process2、process3,使用 ‘setProcess’来定义处理对象调用链。我们将process2链接到process1之后,将process3链接到process2之后。当 链接创建完成后在运行过程中就会按照我们定义的顺序依次运行。

图12:责任链模式客户端代码

什么是代理模式?

代理模式从根本上讲就是一个表现的像接口的类指向一个拥有数据的真实类,其中的数据可以是很大的图片也可以是很大的不能被复制的对象数据。这样我们可以创建多个代理指向内存消耗较大的对象来完成操作。这避免了对象的复制从而节省了内存。所以代理就是指向真实对象的引用。

图“代理和实际对象”展示了接口与真实对象的创建。其中接口‘IImageProxy’为代理,实现类如‘clsActualImage’为真实类。在客户端代码中可以看到接口是如何指向真实对象的。

图13:代理和实际类

使用代理模式的优点是安全并且避免了大数据对象的复制;不是使用实际对象代码而是使用代理,这样避免了在客户端装载实际类的代码;而在客户端只装载代理类更安全。第二点是当有大对象时在网络或域名之间传输这些对象将非常耗内存;所以不传这些大对象而是传递代理性能会更好。

什么是模板模式?

模板模式属于行为模式。它定义了一个主要处理模板,主处理模板中还有子处理,在这个序列中调用字处理。而主处理中的子处理则可以选择产生不同的行为。

提示:模板模式的使用场景是当我们在一般化和特殊化关系之间想创建扩展行为时使用。

例如,以下是一个简单的格式化数据并存储到oracle中的例子。数据来源可以有多种,如文件、SQL server等。不考虑数据从哪来的话,整体处理流程是从数据源加载数据、解析数据、将数据存入oracle。

图14:总处理

接下来我们可以根据总的处理,通过重写‘Load’和‘Parse’字处理来创建一个CSV文件加载数据处理或者SQL server数据加载处理。

图15:模板模式处理方式

从上图中我们可以看出如何改变‘Load’和‘Parse’子出来生成CSV文件处理和SQL server处理。‘Dump’函数和子处理在整个处理中的调用方式并没改变。

实现模板模式需要以下4个步骤:

  1. 通过创建一个父抽象类来创建模板或主处理

  2. 通过定义抽象方法和函数来创建子处理

  3. 创建一个方法来定义字处理被调用的顺序。这个方法应该为正常方法,这样子类既可以重写该方法

  4. 最后创建子类来实现抽象方法或实现新的子处理流程

‘SqlServerParser’继承‘GeneralParser’并重写‘Load’和‘Parse’来实现SQL server解析。

‘FileParser’继承‘GeneralParser’并重写‘Load’和‘Parse’来实现特定文件解析

在客户端我们可以调用这两个类:

两个解析器输出结果如下:

1. 本文由程序员学架构翻译

2. 本文译自 http://www.codeproject.com/Articles/28402/Design-pattern-FAQ-part

3. 转载请务必注明本文出自:程序员学架构(微信号:archleaner )

4. 更多文章请扫码:

猜你喜欢

转载自yunjiechao-163-com.iteye.com/blog/2201874
今日推荐