关于设计模式
本文主要是对原版GoF的23种设计模式提供一个快速的指南。每个模式的介绍包括了类图,讲解,使用信息和真实案例。
C | 抽象工厂模式 | S | 装饰者模式 | C | 原型模式 |
---|---|---|---|---|---|
S | 适配器模式 | S | 门面模式 | S | 代理模式 |
S | 桥接模式 | C | 工厂方法模式 | B | 观察者模式 |
C | 构建者模式 | S | 享元模式 | C | 单例模式 |
B | 责任链模式 | B | 解释器模式 | B | 状态者模式 |
B | 命令模式 | B | 协调者模式 | B | 模板方法模式 |
S | 复合模式 | B | 备忘录模式 | B | 访问者模式 |
责任链对象行为模式
目的
给一个或多个对象通过将接收对象连接起来去处理一个请求的机会。
什么时候用
举例
在一些编程语言对异常处理上就使用了这个模式。当一个异常在方法中被抛出时,运行时检查器就要看这个方法是否有能够处理这个异常的机制或还是决定将其传递给调用栈。当异常被传递给调用栈后,进程就要检查代码直到这个异常被处理或没有父对象再去处理这个请求。
命令对象行为模式
目的
将请求封装为一个对象。也就是允许请求因关系被传统对象处理,如队列和回调。
什么时候用
举例
工作队列被广泛用于实现算法的异步处理。通过将被命令者模式所执行的功能交给一个工作队列来处理,此时队列不需要知晓正在被调用的真实实现是什么。这个被放入队列的命令对象则实现了队列所期望的接口规范的特定算法。
解释器类行为模式
目的
定义了一个语法的呈现以及去理解和执行这个语法的机制。
什么时候用
举例
在19世纪80年代时,有一个非常流行的基于文本的冒险游戏,这个游戏就是这个模式的好例子。许多简单的命令如按“向下”箭头就可以在游戏中往返移动。这些命令被嵌入用于改变他们的含义。如:“向里”与“向上”就具有不同的结果意义。通过创建一个基于命令的命令等级制度和限定符(非终结和终结表达式),应用可以很容易将许多命令变种映射到一棵行为关系树上。
迭代器对象行为模式
目的
允许访问一个聚集对象的元素而不用访问它的底层呈现。
什么时候用
举例
Java自带的迭代器模式允许用户遍历不同的数据集,而不需要担心集合的底层实现。由于客户端只是简单的与迭代器接口打交道,定义合适的迭代器被交给了集合自身。有些允许完全访问底层的数据集,而有些却对某些特定功能有限制,比如移除元素。
协调者对象行为模式
目的
通过封装不同对象之间交互来做到松耦合。允许将对象的行为设置相互独立。
什么时候用
举例
邮件列表软件持续追踪谁已经注册了邮件列表而且提供了一个给任何人可与整个列表进行通信的访问单点。没有协调者的实现,一个人想要发送一条消息到组内将需要不断的去检查谁已经或不在邮件列表中。通过实现协调者模式,系统可接收来自任何点的信息来决定转发消息到哪个接收者,没有发送者的消息就需要关注实际的接收者列表。
备忘录对象行为模式
目的
允许捕获和将一个对象的内部状态固有化用于后续可被恢复,所有的这些都不会破坏封装。
什么时候用
举例
撤消功能就可很好的使用备忘录模式去实现。在状态发生变化前,通过序列和反序列一个对象的状态,我们能够保存一个快照用于在用户选择执行撤消操作时恢复出来。
观察者对象行为模式
目的
用于将系统内其它对象的状态更改通知到一个或多个对象。
什么时候用
举例
这个模式基本上在每个GUI环境中都可以被找到。当按钮,文本和其它字段框在系统中创建后,系统通常都会为这些控件注册一个监听器。当用户触发了一个事件,比如点击了一个按钮,控制器将会遍历注册了监听这个事件的所有观察者并将通知发给它们。
状态对象行为模式
目的
将对象的环境与行为紧密相连,允许对象基于内部状态有不同的表现行为。
什么时候用
举例
一个邮件对象可以有很多不同的状态,每个状态都驱使对象执行不同的功能。如果状态是“未发送”,则可以调用send()方法发送消息,而recallMessage()方法可能会抛出异常或什么也不做。然而,如果状态是“已发送”,则调用send()方法将会要么抛出异常要么什么也不做,而这时调用recallMessage()则将尝试发送一个recall通知到接收者。为了避免在多数或所有的方法中有条件状态语句,需要有多个状态对象去处理每个特定状态的具体实现。邮件对象内的调用将会被代理到合适的状态对象去处理。
策略对象行为模式
目的
定义一套封装好的算法可用于随时被替换去执行一个具体的行为。
什么时候用
举例
当将数据导入到一个新系统时,数据集不同,则需要执行不同的校验算法。通过配置导入,利用策略条件逻辑决定什么需要执行的校验集可被移除,同时导入操作可与实际的校验代码解耦。这允许我们在导入期间可动态调用一个或多个策略。
模板方法类行为模式
目的
定义一个算法的框架,允许实现类去定义真实的行为。
什么时候用
举例
一个父类,像InstantMessage,可能有处理发送一条消息的所有方法。然而,待发送数据的真实序列化可能因实现而不同。一个视频数据和一个文本数据需要不同的算法去正确的序列化。InstantMessage的子类提供自己的序列化方法实现,允许父类调用而不需要理解它们具体的实现。
访问者对象行为模式
目的
允许一个或多个操作可在运行时被用于一套对象身上,将操作与对象的结构相解耦。
什么时候用
举例
对不同地区的发票进行计税需要许多不同的计算逻辑种类。实现一个访问者可将逻辑从发票和明细支出中解耦出来。这就允许款项的层级可通过计算代码被访问到,然后将地区的适当税率附加上去。这样改变一个地区只需要简单的替换一个不同的访问者。
适配器类和对象结构模式
目的
允许类可通过创建一个可供它们交互的共同对象与该类的不同接口一起工作。
什么时候用
举例
一个账单应用需要与HR应用交互以交换员工数据,而各自都有自己对员工对象的接口和实现。另外,SSN以不同的格式存储在各自系统中。通过创建一个适配器我们可以创建两个应用的共同接口用于通过它们自身的对象进行通信以及能够在进程中传输SSN格式。
桥接对象结构模式
目的
定义一个抽象的对象结构独立于具体的实现类的对象结构以做到限制耦合。
什么时候用
举例
JVM有它自身一套对视窗系统,系统日志和二进制代码执行的本地功能抽象,然后具体的实现将会被代理到JVM所在运行的操作系统上。当一个应用命令JVM渲染一个窗口,实际上是它将渲染请求代理到JVM所知道可与交互的操作系统的具体实现上去实现对窗口的渲染。
复合对象结构模式
目的
实现对象层级的创建能够使得每个对象可被独立对待或者作为一套可被同一接口嵌入的对象。
什么时候用
举例
有时候购物车里的信息展示只是一个单一产品,而有时候却是一些产品的聚集展示。将产品项实现为一个复合对象,则我们可以将聚集的和普通的产品项同等对待,这样就允许我们可以简单的遍历产品树并调用每个产品项的功能。通过调用任何树结点上的getCall()方法可让我们得到每个项及其子项的所有成本,单一项和组项将被一视同仁。
装饰器对象结构模式
目的
允许动态包装对象用于修改他们已有的职责和行为。
什么时候用
举例
许多的公司利用装饰器去设置它们的邮件系统。当公司内部员工发一封邮件给外部接收地址时,邮件服务器则要在原始邮件基础上装饰公司自己的版权和隐私信息。只要邮件还在内部,则这些信息不会被加上。这种装饰允许消息维持不变,直到运行时决定给消息包装一些额外信息为止。
门面对象结构模式
目的
给系统内部的一套接口提供一个单一的接口。
什么时候用
举例
通过web服务暴露一组功能给客户端,此时客户端只需要关心暴露给它的那个简单接口而不需要去关心web服务背后不确定的复杂关系。一个简单的web服务请求去更新一个系统的数据可能需要与多个数据库或系统打交道,然而,具体的实现已通过门面模式被隐藏了。
享元对象结构模式
目的
促进许多细粒度对象的重用,从而更高效的使用大量对象。
什么时候用
举例
允许用户定义他们自身的应用流程和布局的系统通常需要追踪许多几乎相同的字段,页面和其它对象。通过将这些对象轻量化,每个对象的所有实例在分离外在状态时可共享内在化的状态。内在的状态可存储共享的属性,如一个文本框的样子,需要hold住多少数据以及什么事件需要对外暴露。外在状态将存储那些不能共享的属性,如物品属于哪里,如何响应用户的点击以及如何处理事件。
代理对象创建模式
目的
允许通过扮演一个传递实体或占位对象来做到对象级的访问控制。
什么时候用
举例
总账应用通常给用户提供了一个按需使得用户银行账单和总账数据一致的方法,处理的过程多数是自动的。而实际它与第三方的通信操作是相对昂贵且需要限制的。通过一个代理去代表通信对象,我们可以限制通信被调用的次数或通信的间隔。另外,我们可以在代理类内包装通信对象的复杂实例,将调用代码与具体的实现相解耦。
抽象工厂对象创建模式
目的
提供一个代理一个或多个具体类的创建请求去生产具体的对象的接口。
什么时候用
举例
邮件编辑器允许编辑多种格式包括普通文本,富文本和HTML,根据不同的格式,需要创建不同的对象。如果消息是普通文本,则消息将会有一个对象体来代表普通文本和一个附件对象用base64对附件进行加密。如果消息是HTML,则对象体代表了HTML编码后的文本,而附件对象则允许内联呈现一个标准的附件。通过利用抽象工厂来创建,我们可以保证根据邮件需要发送的样式可创建合适的对象集。
构建者对象创建模式
目的
允许基于可易互换的算法来动态的创建对象。
什么时候用
举例
一个文件传输应用可能使用了许多不同的协议来发送文件,而真正的传输对象的创建将直接取决于所选择的协议。使用构建器我们可以用正确的构建器去实例化正确的对象。如果设置的是FTP,则FTP的构建器将被用于创建对象。
工厂方法对象创建模式
目的
暴露一个方法用于创建对象,允许子类控制实际的对象创建过程。
什么时候用
举例
许多应用有一些用于安全的用户和组的形式。当应用需要去创建一个用户时,它通常将用户的创建代理到多个用户的实现。父类的用户对象将会处理每个用户的多数操作,但是,子类会定义工厂方法来处理创建每种不同用户的差别。一个系统可能有管理员和普通用户,它们都继承于用户对象。管理员对象可能会执行一些额外的工作来保证访问权限,而普通的用户只会有有限的访问权限。
原型对象创建模式
目的
通过对已有对象来克隆它的模板来创建对象。
什么时候用
举例
费用处理引擎通常需要查询许多不同的配置值,初始化这样的引擎相对是一个昂贵的过程。当需要多个这样的引擎实例时,比如以多线程的方式导入数据,则初始化许多这样的引擎的开销是巨大的。通过使用原型模式,我们可以保证只有一个引擎的拷贝被初始化了,然后通过简单的克隆来创建一个已初始化对象的复制品。这种附加的好处就是克隆只需要包括它们自身所需要的相关数据。
单例对象创建模式
目的
确保在一个系统内一个类只有一个实例。
什么时候用
举例
许多编程语言提供了一些系统或环境对象用于允许该语言与本地的操作系统进行交互。由于应用只是物理运行于仅有的一个操作系统上,那么这个系统对象只需要一个单例。语言运行时实现的单例模式可确保一个系统对象只有一个实例被创建,也能确保适当的操作允许访问它。