设计模式---设计模式概述

     面向对象技术为软件技术带来新的发展。人们运用面向对象的思想分析系统、为系统建模并设计系统,最后使用面向对象的程序语言来实现系统。但是面向对象的设计并不是一件很简单的事情,尤其是要设计出架构良好的软件系统更不容易。为了提高系统的复用性,需要进行一些“额外”的设计(这里的额外并不是无用的,而是指业务领域之外),定义类的接口、规划类的继承结构、建立类与类之间的关系。毋庸置疑,良好的设计可以让系统更容易地被复用、被移植和维护,而如何快速进行良好的设计则是设计模式要讨论的问题。设计模式是软件架构设计师的必修课,设计模式中蕴含的思想是架构设计师必须掌握的。

    1 设计模式概述

    在 20 世纪 70 年代,Christopher Alexander 提出了城市建筑的模式,他认为:模式就是描述一个不断发生的问题和该问题的解决方案。随后,Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 写了一本著名的参考书《设计模式:可复用面向对象软件的基础》。后人也因为这本书称这四个人为四人组,将这本书中描述的模式称为 GoF(Gang of Four)设计模式。在这本书中,四人组将设计模式定义为:对被用来在特定场景下解决一般设计问题的类和互相通信的对象的描述。通俗地说,可以把设计模式理解为对某一类问题的通用解决方案。

   1  设计模式的概念

    首先,设计模式解决的是一类问题,例如工厂模式就是为了解决类创建的问题,而适配器模式则是为了解决类接口不匹配的问题。如果把解决 A 问题的设计模式使用在 B 问题上,结果肯定是张冠李戴了。所以在描述设计模式前,首先要描述这个设计模式究竟要解决什么样的问题。

    其次,设计模式是一种通用的解决方案,而不是具体的,也不是唯一的。在 GoF 的书中对设计描述主要着重于思想的描述,虽然也给出了 C++的实现方法,但同样也可以使用 Java 甚至非面向对象的语言实现。具体应用时可以根据实际情况进行相应的变化,例如,对于工厂模式就有很多种变化。

    需要指出的是,虽然在 GoF 的著作中第一次提出了软件的设计模式,但设计模式并非这四人所创,它来源于很多项目中的成功设计,并将这些优雅的设计方式进行抽象、总结、归纳出来。

    在学习设计模式时需要注意以下两点:

    (1)学习这些模式是一个方面,另一方面更要了解模式中的思想。设计模式本身是为了提高软件架构的质量,学习设计模式的目的也是为了提高架构设计的水平。虽然设计模式中描述的大多是面向对象的低层设计方案,但其中包含的却是软件设计的思想,同软件架构风格是一致的。例如,MVC 既可以看作一种设计模式也可以看作一种架构风格。掌握这种设计思想是非常有意义的。

    (2)设计模式虽然可以使设计变得更精妙,但滥用设计模式会适得其反。在软件设计中使用设计模式,可以优化设计,提高架构质量。但是,首先,设计模式有其应用的场合,不相宜的场合乱用设计模式有害无益;其次,设计模式主要解决对象之间相互通信、相互依赖的结构关系,架构设计师需要把握好使用设计模式的力度,过度的使用设计模式不但不会提高软件的复用性,反而会让架构变得混乱而难以维护。

 

2 设计模式的组成

    一般的,在描述一个设计模式时,至少需要包含四个方面:模式名称(Pattern name)、问题(Problem)、解决方案(Solution)、效果(Consequence)。这四个方面就是设计模式的四要素。名不正则言不顺,每种设计模式都有自己的名字,也就是模式名称;设计模式都有其应用的场合,即该设计模式意图解决的问题,超出了这个问题就不应该再应用这种模式,所以问题是设计模式的第二要素;设计模式的目的就是解决问题,所以在描述设计模式时当然要有解决问题的方法描述,这就是设计模式的另外一个要素——解决方案;虽然架构设计师知道应用设计模式可以提高架构质量,提高软件的复用性,但对于每一种设计模式而言,还有其更具体的效果描述,所以设计模式的最后一个要素就是效果。

    这四个要素是描述设计模式时必不可少的部分。在本章中,除了描述模式的四要素外,还补充了该模式的 Java 实现和对该模式的引申,也就是说,本章中的模式将按照如下的方式描述:模式名称、意图解决的问题、模式描述、效果、实现、相关讨论。使用这种方式介绍几种有代表性的设计模式。

 

3  GoF 设计模式

    GoF 的著作不反第一次总结了设计中的常用模式,还在学术上建立了软件设计模式的地位。因此,人们习惯上将 GoF 提出的 23 个模式统称为 GoF 模式,这 23 个模式分别简述如下。

    (1)Factory Method 模式。Factory Method 模式提供了一种延迟创建类的方法,使用这个方法可以在运行期由子类决定创建哪一个类的实例。

    (2)Abstract Factory 模式。Abstract Factory 又称为抽象工厂模式,该模式主要为解决复杂系统中对象创建的问题。抽象工厂模式提供了一个一致的对象创建接口来创建一系列具有相似基类或相似接口的对象。抽象工厂模式是一种很有代表性的设计模式。

    (3)Builder 模式。Builder 模式与 Abstract Factory 模式非常类似,但 Builder 模式是逐步地构造出一个复杂对象,并在最后返回对象的实例。Builder 模式可以把复杂对象的创建与表示分离,使得同样的创建过程可以创建不同的表示。

    (4)Prototype 模式。Prototype 模式可以根据原型实例制定创建的对象的种类,并通过深复制这个原型来创建新的对象。Prototype 模式有着同 Abstract Factory 模式和 Builder 模式相同的效果,不过当需要实例化的类是在运行期才被指定的而且要避免创建一个与产品曾是平行的工厂类层次时,可以使用 Prototype 模式。使用 Prototype 模式可以在运行时增加或减少原型,比Abstract Factory 和 Builder 模式更加灵活。

    (5)Singleton 模式。Singleton 模式也是一种很有代表性的模式。使用 Singleton 模式可以保证一个类仅有一个实例,从而可以提供一个单一的全局访问点。

    (6)Adapter 模式。Adapter 模式可以解决系统间接口不相容的问题。通过 Adapter 可以把类的接口转化为客户程序所希望的接口,从而提高复用性。

    (7)Bridge 模式。Bridge 模式把类的抽象部分同实现部分相分离,这样类的抽象和实现都可以独立地变化。

    (8)Composite 模式。Composite  模式提供了一种以树形结构组合对象的方法,使用Composite 可以使单个对象和组合后的对象具有一致性以提高软件的复用性。

    (9)Decorator 模式。Decorator 模式可以动态地为对象的某一个方法增加更多的功能。在很多时候,使用 Decorator 模式可以不必继承出新的子类从而维护简洁的类继承结构。在 9.2 节中将对 Decorator 模式作更详细的介绍。

    (10)Facade 模式。Facade 模式为一组类提供了一致的访问接口。使用 Facade 可以封装内部具有不同接口的类,使其对外提供统一的访问方式。Facade 模式在 J2EE 系统开发中发展为Session Facade 模式。

    (11)Flyweight 模式。Flyweight 模式可以共享大量的细粒度对象,从而节省创建对象所需要分配的空间,不过在时间上的开销会变大。

    (12)Proxy 模式。顾名思义,Proxy 模式为对象提供了一种访问代理,通过对象 Proxy 可以控制客户程序的访问。例如:访问权限的控制、访问地址的控制、访问方式的控制等,甚至可以通过 Proxy 将开销较大的访问化整为零,提高访问效率。

    (13)Interpreter 模式。定义了一个解释器,来解释遵循给定语言和文法的句子。

    (14)Template Method 模式。定义一个操作的模板,其中的一些步骤会在子类中实现,以适应不同的情况。

    (15)Chain of Responsibility 模式。Chain of Responsibility模式把可以响应请求的对象组织成一条链,并在这条对象链上传递请求,从而保证多个对象都有机会处理请求而且可以避免请求方和相应方的耦合。

    (16)Command 模式。将请求封装为对象,从而增强请求的能力,如参数化、排队、记录日志等。

    (17)Iterator 模式。Iterator 模式提供了顺序访问一个对象集合中的各元素的方法,使用Iterator 可以避免暴露集合中对象的耦合关系。

    (18)Mediator 模式。Mediator 模式可以减少系统中对象间的耦合性。Mediator 模式使用中介对象封装其他的对象,从而使这些被封装的对象间的关系就成了松散耦合。

    (19)Memento 模式。Memento 模式提供了一种捕获对象状态的方法,且不会破坏对象的封装。并且可以在对象外部保存对象的状态,并在需要的时候恢复对象状态。

    (20)Observer 模式。Observer 模式提供了将对象的状态广播到一组观察者的方式,从而可以让每个观察者随时可以得到对象更新的通知。

    (21)State 模式。State 模式允许一个对象在其内部状态改变的时候改变它的行为。

    (22)Strategy 模式。使用 Strategy 模式可以让对象中算法的变化独立于客户。

    (23)Visitor 模式。表示对某对象结构中各元素的操作,使用 Visitor 模式可以在不改变各元素类的前提下定义作用于这些元素的新操作。

 

4 其他设计模式

    在 GoF 之后,人们继续对设计模式进行发掘,总结出更多的设计模式。在 J2EE 应用领域,人们也对使用 J2EE 框架开发的应用程序总结出一系列设计模式,本节对几种较有代表性的 J2EE 设计模式进行简要介绍。因为这些设计模式是同 J2EE 技术紧密相关的,所以本节中将会使用一些 J2EE 技术术语。

    (1)Intercepting Filter 模式。在 J2EE 的 BPS(Basic Programming System,基本编程系统)应用框架下,在真正响应客户端请求前经常需要进行一些预处理,如客户身份验证、客户Session 的合法性验证、字符集转码、客户请求记录等。当然可以将这些请求预处理在每一个Servlet 中,不过很明显,这样的话预处理的代码就“侵入”了真正的处理程序,使得代码变得更加难以维护。Intercepting  Filter 模式提供了解决这个问题的方法。它通过截取客户请求,并将请求发送到 Filter 链中,一步一步地进行预处理,直到这些处理结束,请求才会被转发到真正响应客户请求的 Servlet 中。

    (2)Session Facade 模式。Session Facade 模式广泛应用于 EJB 开发的 J2EE 应用程序中。EJB 是一种分布式构件,EJB 的客户端需要通过 EJB 容器调用 EJB,即使 EJB 的客户端同 EJB 部署于同一台机器,对 EJB 的调用也许要通过网络接口进行远程调用。因此,在开发 EJB 时,需要尽量减少对 EJB 调用的次数以提高性能。同时为了提高 EJB 构件的可维护性和复用性,应该尽量将 EJB 构件的接口设计得一致。在 GoF 设计模式中就有 Facade 模式提高接口的一致性,在 J2EE 开发领域,人们把Session Bean 和 Facade 模式结合起来, 封装业务逻辑的接口,形成了 Session Facade 模式

 

5 设计模式与软件架构

    软件架构描述了软件的组成,例如,经典的“4+1”视图,将软件架构通过逻辑视图、开发视图、进程视图、物理视图及场景视图来进行描述。在这些视图中,描述了软件系统中类之间的关系、进程之间的关系、软件和硬件的结合等问题。一般来说,软件架构更倾向于从整体和全局上描述软件的组成。

    而设计模式则更侧重于类与类、对象与对象之间的关系。例如在逻辑视图中,可以使用多种设计模式来组织类与类之间的关系。因此,有很多人认为,设计模式和软件架构是面向不同层次问题的解决方案。

    同设计模式一样,软件架构也有一些固定的模式,通常称为架构风格。常见的架构风格有分层架构、客户端—服务器架构、消息总线、面向服务的架构(Service-Oriented Architecture,SOA)等。

    软件架构风格同设计模式在某种含义上是一致的。设计模式和软件架构中蕴含的很多思想是一致的。无论是架构风格还是设计模式,人们在追求良好设计的过程中,将一些常见解决方案总结、整理出来,形成固定的风格与模式。例如消息总线的架构风格同 Observer 模式就有神似之处。因此,掌握设计模式对于软件架构设计有非常大的帮助。

 

6 设计模式分类

    可以说,设计模式是面向问题的,即每一种设计模式都是为了解决一种特定类型的问题。因此,根据设计模式要解决的问题将设计模式分为三类,分别为创建型、结构型和行为型。

    事实上,面向对象的设计中,需要解决的就是:如何管理系统中的对象、如何组织系统中的类与对象、系统中的类与对象如何相互通信。这三类设计模式分别解决了这三个方面的问题。

    创建型设计模式主要解决对象创建的问题。在最简单的情况下,在程序中定义类,在使用时创建一个对象实例。但在实际开发中,对象的创建会变得复杂很多,这时就需要使用创建型设计模式解决创建对象的问题。

    随着开发系统的不断扩张,系统功能更加丰富,模块之间的复用越来越多,系统中类与对象的结构变得更加复杂。如果缺乏良好的设计,这些类之间的关系将会变得非常混乱。结构型设计模式就是为了解决这些问题的。

    除了这种分类方法外,GoF 还提出了可以根据设计模式主要应用于类还是对象来对设计模式进行分类,对于这种分类方法就不再赘述了。综合这两种分类方法,可以把 GoF 模式进行分类,如表 10-1 所示。

设计模式---设计模式概述

    随着 GoF 设计模式的提出,后人也总结出了更多的良好设计的范本,并根据其他的方法进行分类。例如,在《Core J2EE Patterns》一书中,作者将书中列举的 Design Pattern分为表现层模式、业务层模式和综合层模式。根据这种分类方法,可以得到应用于 J2EE 框架的设计模式图谱,如表 10-2 所示。

设计模式---设计模式概述