选择开始

设计模式的意图

Abstract Factory:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
Adapter:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原个由于接口不兼容而不能一起工作的那些类可以一起工作。
Bridge:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
Builder:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
Chain of Responsibility:为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。
Command:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。
Composite:将对象组合成树形结构以表示“部分—整体”的层次结构。Composite使得客户对单个对象和复合对象的使用具有一致性。
Decorator:动态地给一个对象添加一些额外的职责。就扩展功能而言,Decorator模式比生成子类方式更为灵活。
Facade:为子系统中的一组接口提一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
Factory Method:定义一个用于创建对象的接口,让子类决定将哪一个类实例化。Factory Method使一个类的实例化延迟到其子类。
Flyweight:运用共享技术有效地支持大量细粒度的对象。
Interpreter:给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
Iterator:提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。
Mediator:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
Memento:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到保存的状态。
Observer:定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。
Prototype:用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。
Proxy:为其他对象提供一个代理以控制对这个对象的访问。
Singleton:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
State:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它所属的类。
Strategy:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法的变化可独立于使用它的客户。
Template Method:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
Visitor:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

设计应支持变化

下面阐述了一些导致重新设计的一般原因,以及解决这些问题的设计模式:

通过显式地指定一个类来创建对象

在创建对象时指定类名将使你受特定实现的约束而不是特定接口的约束。这会使未来的变化更复杂。要避免这种情况,应该间接地创建对象。

设计模式:Abstract Factory,Factory Method,Prototype

对特殊搡作的依赖

当你为请求指定一个特殊的操作时,完成该请求的方式就固定下来了。为避免把请求代码写死,你将可以在编译时刻或运行时刻很方便地改变响应请求的方法。

设计模式:Chain of ReSposibility,Command

对硬件和软件平台的依赖

外部的操作系统接口和应用编程接口(API)在不同的软硬件平台上是不同的。依赖于特定平台的软件将很难移植到其他平台上,甚至都很难跟上本地平台的更新。所以设计系统时限制其平台相关性就很重要了。

设计模式:Abstract Factory,Bridge

对对象表示或实现的依赖

知道对象怎样表示、保存、定位或实现的客户在对象发生变化时可能也需要变化。对客户隐藏这些信息能阻止连锁变化。

设计模式:Abstract Factory,Bridge,Memento,Proxy

算法依赖

算法在开发和复用时常常被扩展、优化和替代。依赖于某个特定算法的对象在算法发生变化时不得不变化因此有可能发生变化的算法应该被孤立起来。

设计模式:Builder,Iterator,Strategy,Template Method,Visitor

紧耦合

紧耦合的类很难独立地被复用,因为它们是互相依赖的。紧耦合产生单块的系统,要改变或删掉一个类,你必须理解和改变其他许多类。这样的系统是一个很难学习、移植和维护的密集体。

松散耦合提高了一个类本身被复用的可能性,并且系统更易于学习、移植、修改和扩展。设计模式使用抽象耦合和分层技术来提高系统的松散耦合性。

设计模式:Abstract Factory,Command,Facade,Mediator,Observer,Chain of Responsibility

通过生成子类来扩充功能

通常很难通过定义子类来定制对象。每一个新类都有固定的实现开销(初始化、终止处理等)。定义子类还需要对父类有深人的了解。如,重定义一个操作可能需要重定义其他操作。一个被重定义的操作可能需要调用继承下来的操作。并且子类方法会导致类爆炸,因为即使对于一个简单的扩充,你也不得不引人许多新的子类。

一般的对象组合技术和具体的委托技术,是继承之外组合对象行为的另一种灵活方法。新的功能可以通过以新的方式组合已有对象,而不是通过定义已存在类的子类的方式加到应用中去。另一方面,过多使用对象组合会使设计难于理解。许多设计模式产生的设计中,你可以定义一个子类,且将它的实例和已存在实例进行组合来引人定制的功能。

设计模式:Bridge,Chain of Responsibility,Composite,Decorator,Observer,Strategy

不能方便地对类进行修改

有时你不得不改变一个难以修改的类。也许你需要源代码而又没有(对于商业类库就有这种情况),或者可能对类的任何改变会要求修改许多已存在的其他子类。设计模式提供在这些清况下对类进行修改的方法。

设计模式:Adapter,Decorator,Visitor

这些例子反映了使用设计模式有助于增强软件的灵活性。这种灵活性所具有的重要程度取决于你将要建造的软件系统。让我们看一看设计模式在开发如下三类主要软件中所起的作用:应用程序、工具箱和框架。

软件中应用设计模式的作用

应用程序

如果你将要建造像文档编辑器或电子制表软件这样的应用程序(Application Program),那么它的内部复用性、可维护性和可扩充性是要优先考虑的。内部复用性确保你不会做多余的设计和实现。设计模式通过减少依赖性来提高内部复用性。松散耦合也增强了一类对象与其他多个对象协作的可能性。例如,通过孤立和封装每一个操作,以消除对特定操作的依赖,可使在不同上下文中复用一个操作变得更简单。消除对算法和表示的依赖可达到同样的效果。

当设计模式被用来对系统分层和限制对平台的依赖性时,它们还会使一个应用更具可维护性。通过显示怎样扩展类层次结构和怎样使用对象复用,它们可增强系统的易扩充性。同时,耦合程度的降低也会增强可扩充性。如果一个类不过多地依赖其他类,扩充这个孤立的类还是很容易的。

工具箱

一个应用经常会使用来自一个或多个被称为工具箱(Toolkit)的预定义类库中的类。工具箱是一组相关的、可复用的类的集合,这些类提供了通用的功能。工具箱的一个典型例子就是列表、关联表单、堆栈等类的集合,C++的I/0流库是另一个例子。工具箱并不强制应用采用某个特定的设计,它们只是为你的应用提供功能上的帮助。工具箱强调的是代码复用,它们是面向对象环境下的“子程序库”。

工具箱的设计比应用设计要难得多,因为它要求对许多应用是可用的和有效的。再者,工具箱的设计者并不知道什么应用使用该工具箱及它们有什么特殊需求。这样,避免假设和依赖就变得很重要,否则会限制工具箱的灵活性,进而影响它的适用性和效率。

框架

框架(Framework)是构成一类特定软件可复用设计的一组相互协作的类。例如,一个框架能帮助建立适合不同领域的图形编辑器,像艺术绘画、音乐作曲和机械CAD。另一个框架也许能帮助你建立针对不同程序设计语言和目标机器的编译器。而再一个也许能帮助你建立财务建模应用。你可以定义框架抽象类的应用相关的子类,从而将一个框架定制为特定应用。

框架规定了你的应用的体系结构。它定义了整体结构,类和对象的分割,各部分的主要责任,类和对象怎么协作,以及控制流程。框架预定义了这些设计参数,以便于应用设计者或实现者能集中精力于应用本身的特定细节。框架记录了其应用领域的共同的设计决策。因而框架更强调设计复用,尽管框架常包括具体的立即可用的子类。

这个层次的复用导致了应用和它所基于的软件之间的反向控制(inversion of control)。当你使用工具箱(或传统的子程序库)时,你需要写应用软件的主体并且调用你想复用的代码。而当你使用框架时,你应该复用应用的主体,写主体调用的代码。你不得不以特定的名字和调用约定来写操作地实现,但这会减少你需要做出的设计决策。

你不仅可以更快地建立应用,而且应用还具有相似的结构。它们很容易维护,且用户看来也更一致。另一方面,你也失去了一些表现创造性的自由,因为许多设计决策无须你来作出。

如果说应用程序难以设计,那么工具箱就更难了,而框架则是最难的。框架设计者必须冒险决定一个要适应于该领域的所有应用的体系结构。任何对框架设计的实质性修改都会大大降低框架所带来的好处,因为框架对应用的最主要贡献在于它所定义的体系结构。因此设计的框架必须尽可能地灵活、可扩充。

更进一步讲,因为应用的设计如此依赖于框架,所以应用对框架接口的变化是极其敏感的。当框架演化时,应用不得不随之演化。这使得松散耦合更加重要,否则框架的一个细微变化都将引起强烈反应。

刚才讨论的主要设计问题对框架设计而言最具重要性。一个使用设计模式的框架比不用设计模式的框架更可能获得高层次的设计复用和代码复用。成熟的框架通常使用了多种设计模式。设计模式有助于获得无须重新设计就可适用于多种应用的框架体系结构。

当框架和它所使用的设计模式一起写人文档时,我们可以得到另外一个好处。了解设计模式的人能较快地洞悉框架。甚至不了解设计模式的人也可以从产生框架文档的结构中受益。加强文档工作对于所有软件而言都是重要的,但对于框架其重要性显得尤为突出。学会使用框架常常是一个必须克服很多困难的过程。设计模式虽然无法彻底克服这些困难,但它通过对框架设计的主要元素做更显式的说明可以降低框架学习的难度。

因为模式和框架有些类似,人们常常对它们有怎样的区别和它们是有区别感到疑感。它们最主要的不同在于如下三个方面:

模式和框架的不同

设计模式比框架更抽象

设计模式比框架更抽象框架能够用代码表示,而设计模式只有其实例才能表示为代码。框架的威力在于它们能够使用程序设计语言写出来,它们不仅能被学习,也能被直接执行和复用。而本书中的设计模式在每一次被复用时,都需要被实现。设计模式还解释了它的意图、权衡和设计效果。

设计模式是比框架更小的体系结构元素

一个典型的框架包括了多个设计模式,而反之决非如此。

框架比设计模式更加特例化

框架总是针对一个特定的应用领域。一个图形编辑器框架可能被用于一个工厂模拟,但它不会被错认为是一个模拟框架。而本书收录的设计模式几乎能被用于任何应用。当然比我们的模式更特殊的设计模式也是可能的(如,分布式系统和并发程序的设计模式),尽管这些模式不会像框架那样描述应用的体系结构。

框架变得越来越普遍和重要。它们是面向对象系统获得最大复用的方式。较大的面向对象应用将会由多层彼此合作的框架组成。应用的大部分设计和代码将来自于它所使用的框架或受其影响。

怎样选择设计模式

这里给出几个不同的方法,帮助你发现适合你手头问题的设计模式:

考虑设计模式是怎样解决设计问题的

1.6节讨论了设计模式怎样帮助你找到合适的对象、决定对象的粒度、指定对象接口以及设计模式解决设计问题的几个其他方法。参考这些讨论会有助于你找到合适的模式。

浏览模式的意图部分

设计模式的意图列出了目录中所有模式的意图(intent)部分。通读每个模式的意图,找出和你的问题相关的一个或多个模式。可以使用下图所显示的分类方法缩小你的搜查范围。

研究模式怎样互相关联图

下图显示了设计模式之间的关系。研究这些关系能指导你获得合适的模式或模式组。

研究目的相似的模式

模式分类有三部分:创建型模式、结构型模式、行为型模式。

检查重新设计的原因

看一看从设计应支持变化讨论的引起重新设计的各种原因,再看看你的问题是否与它们有关,然后再找出哪些模式可以帮助你避免这些会导致重新设计的因素。

考虑你的设计中哪些是可变的

这个方法与关注引起重新设计的原因刚好相反。它不是考虑什么会迫使你的设计改变,而是考虑你想要什么变化却又不会引起重新设计。最主要的一点是封装变化的概念,这是许多设计模式的主题。下表列出了设计模式允许你独立变化的方面,你可以改变它们而又不会导致重新设计。

设计模式所支持的设计的可变方面

创建
结构

Builder(3.2)
FactoryMethod(3.3)
Prototype(3、4)
Singleton(3、5〕
Adapter(4.1)
Bridge(4.2)
Composite(4.3)
Dorator〔4.4)
Facade(4.5)
Flyweight(4.6)
Proxy(47)
ChainOfResponsibility(Sl)
Command(S2)
Interpreter〔5.3〕
Iterator(5.4)
Mediator(5.5)
Memento(5.6)
Observer(5.7)
State(5.8)
Strategy〔5.9)
TemplateMethod(5.10)
Visitor(5.11)

产品对象家族
如何创建一个组合对象
被实例化的子类
被实例化的类
一个类的唯一实例
对象的接口
对象的实现
一个对象的结构和组成
对象的职责,不生成子类
一个子系统的接口
对象的存储开销
如何访问一个对象;该对象的位置
满足一个请求的对象
何时、怎样满足一个请求
一个语言的文法及解释
如何遍历、访问一个聚合的各元素
对象间怎样交互、和谁交互
一个对象中哪些私有信息存放在该对象之外,以及在
什么时候进行存储
多个对象依赖于另外一个对象,而这些对象又如何保
对象的状态
算法
算法中的某些步骤
某些可作用于一个〔组)对象上的操作.但不修改这
些对象的类

猜你喜欢

转载自blog.csdn.net/Star_Inori/article/details/80250894
今日推荐