面向对象技术为软件技术带来新的发展。人们运用面向对象的思想分析系统、为系统建模并设计系统,最后使用面向对象的程序语言来实现系统。但是面向对象的设计并不是一件很简单的事情,尤其是要设计出架构良好的软件系统更不容易。为了提高系统的复用性,需要进行一些“额外”的设计(这里的额外并不是无用的,而是指业务领域之外),定义类的接口、规划类的继承结构、建立类与类之间的关系。毋庸置疑,良好的设计可以让系统更容易地被复用、被移植和维护,而如何快速进行良好的设计则是设计模式要讨论的问题。设计模式是软件架构设计师的必修课,设计模式中蕴含的思想是架构设计师必须掌握的。 10.1 设计模式概述 在 20 世纪 70 年代,Christopher Alexander 提出了城市建筑的模式,他认为:模式就是描述一个不断发生的问题和该问题的解决方案。随后,Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 写了一本著名的参考书《设计模式:可复用面向对象软件的基础》。后人也因为这本书称这四个人为四人组,将这本书中描述的模式称为 GoF(Gang of Four)设计模式。在这本书中,四人组将设计模式定义为:对被用来在特定场景下解决一般设计问题的类和互相通信的对象的描述。通俗地说,可以把设计模式理解为对某一类问题的通用解决方案。 10.1.1 设计模式的概念 首先,设计模式解决的是一类问题,例如工厂模式就是为了解决类创建的问题,而适配器模式则是为了解决类接口不匹配的问题。如果把解决 A 问题的设计模式使用在 B 问题上,结果肯定是张冠李戴了。所以在描述设计模式前,首先要描述这个设计模式究竟要解决什么样的问题。 其次,设计模式是一种通用的解决方案,而不是具体的,也不是唯一的。在 GoF 的书中对设计描述主要着重于思想的描述,虽然也给出了 C++的实现方法,但同样也可以使用 Java 甚至非面向对象的语言实现。具体应用时可以根据实际情况进行相应的变化,例如,对于工厂模式就有很多种变化。 需要指出的是,虽然在 GoF 的著作中第一次提出了软件的设计模式,但设计模式并非这四人所创,它来源于很多项目中的成功设计,并将这些优雅的设计方式进行抽象、总结、归纳出来。 在学习设计模式时需要注意以下两点: (1)学习这些模式是一个方面,另一方面更要了解模式中的思想。设计模式本身是为了提高软件架构的质量,学习设计模式的目的也是为了提高架构设计的水平。虽然设计模式中描述的大多是面向对象的低层设计方案,但其中包含的却是软件设计的思想,同软件架构风格是一致的。例如,MVC 既可以看作一种设计模式也可以看作一种架构风格。掌握这种设计思想是非常有意义的。 (2)设计模式虽然可以使设计变得更精妙,但滥用设计模式会适得其反。在软件设计中使用设计模式,可以优化设计,提高架构质量。但是,首先,设计模式有其应用的场合,不相宜的场合乱用设计模式有害无益;其次,设计模式主要解决对象之间相互通信、相互依赖的结构关系,架构设计师需要把握好使用设计模式的力度,过度的使用设计模式不但不会提高软件的复用性,反而会让架构变得混乱而难以维护。 10.1.2 设计模式的组成 一般的,在描述一个设计模式时,至少需要包含四个方面:模式名称(Pattern name)、问题(Problem)、解决方案(Solution)、效果(Consequence)。这四个方面就是设计模式的四要素。名不正则言不顺,每种设计模式都有自己的名字,也就是模式名称;设计模式都有其应用的场合,即该设计模式意图解决的问题,超出了这个问题就不应该再应用这种模式,所以问题是设计模式的第二要素;设计模式的目的就是解决问题,所以在描述设计模式时当然要有解决问题的方法描述,这就是设计模式的另外一个要素——解决方案;虽然架构设计师知道应用设计模式可以提高架构质量,提高软件的复用性,但对于每一种设计模式而言,还有其更具体的效果描述,所以设计模式的最后一个要素就是效果。 这四个要素是描述设计模式时必不可少的部分。在本章中,除了描述模式的四要素外,还补充了该模式的 Java 实现和对该模式的引申,也就是说,本章中的模式将按照如下的方式描述:模式名称、意图解决的问题、模式描述、效果、实现、相关讨论。 10.1.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 模式可以保证一个类仅有一个实例,从而可以提供一个单一的全局访问点。将在 9.2 节中对 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 模式可以在不改变各元素类的前提下定义作用于这些元素的新操作。
|