面向对象(Object-Oriented,OO)是当下软件开发的主流方法。在OO分析与设计中,我们首先从问题领域中抽象出领域模型,在领域模型中以适当的粒度归纳出相关的类;然后定义各个类之间的关联关系,并给这些类分配相应的职责,同时定义这些类之间的协作方式。将相应的职责分配给具体的类是OO过程中非常重要的一步。GRASP设计模式是职责分配过程中的一套非常重要的设计模式。它给出了在给类分配职责的过程中,设计者们所需要遵从的一些原则或者指导性的建议。
说到设计模式,更为人所知的当然是GoF(Gang of Four)的23种设计模式。与GoF的23种设计模式不同的是,GRASP设计模式描述的是在OO设计中为互相协作的类分配职责的原则或者建议,而GoF的设计模式则是在更高的层次上描述一个OO系统或者其局部系统的行为以及结构上的抽象。
GRASP设计模式的全称是General Responsibility Assignment Software Patterns,即通用职责分配软件模式。它定义了9个基本的OO设计原则或基本的设计构件。这9个设计模式分别是:创建者(Creator)、信息专家(Information Expert)、低耦合(Low Coupling)、控制器(Controller)、高内聚(High Cohesion)、多态性(Polymorphism)、纯虚构(Pure Fabrication)、间接性(Indirection)、防止变异(Protected Variations)。
一、创建者(Creator)
创建者模式关注这样一个问题:假设系统中存在一个类A,那么在这个系统中,谁应该负责创建类A的新实例?
创建类的实例是面向对象的系统中最常见的活动之一。合理分配类的创建职责的设计能够支持低耦合,提高封装性、可复用性、可扩展性。创建者模式为这一活动给出的指导性的建议是,将创建类A的新实例的职责分配给类B,如果以下条件成立:
lB“包含”或组成聚集A。
lB记录A。
lB直接使用A。
lB具有A的初始化数据,并且在创建A的实例时会将这些数据传递给A。
这些条件成立得越多越好。如果有一个以上的条件成立,那么通常首选聚集或包含A的类B。
举一个非常简单的例子。假设系统中存在链表类和节点类。一个链表类的对象包含了多个节点的对象,链表类的用户(这里的用户不是指现实世界中的人,而是指使用链表类的代码)可以向一个链表类的对象插入一个新的节点。那么,被插入的新的节点对象应该由谁来创建?显然,链表类的用户本身是一个创建者的候选者,因为用户本身拥有节点对象的初始化数据,由用户创建节点对象是可以实现的。然而,根据创建者模式,链表类却是一个更好的选择。因为链表类包含了节点,并直接使用、操作节点。由链表类本身创建节点可以消除链表类的用户对于节点类的依赖关系,从而消除了用户代码与节点类的耦合,使系统中仅剩下用户代码与链表类的耦合。这形成了一个良好的设计。
使用创建者模式的好处是不会增加系统的耦合度,因为根据创建者模式的建议,类的实例的创建者已经与这个类存在着某种形式的耦合。因此该模式支持低耦合的设计,能产生具有较低的维护依赖性与较高的复用性的系统。
在一个设计灵活的OO系统中,对象的创建方式往往非常复杂。比如,有些系统需要为了更好的性能而集中创建或者使用回收的实例(线程池、连接池、对象池等);有些系统需要根据某些条件来创建一族类的实例;甚至有些框架系统在框架的编写过程中根本不知道需要实例化哪一个类,等等。在这些情况下,最好的方法是将创建类的实例的职责委派给抽象工厂(Abstract Factory)、具体工厂(ConcreteFactory)、创建器(Builder)等等辅助类,而不是创建者模式所建议的类。
二、信息专家(InformationExpert)
信息专家模式关注这样一个问题:给对象分配职责的基本原则是什么?
在一个OO系统中可能会定义成百上千个软件类,而所有这些类必须履行的职责的数量之和甚至更多。如果能很好地给所有这些类分配好职责,那么这个系统就会易于理解、维护和扩展。信息专家模式关于这个问题给出的答案是:把职责分配给信息专家,它具有实现这个职责所必需的信息。
这一设计模式理解起来非常简单,它并不是某种深度认证的结论,而更像是一种直觉,即对象完成与它所具有的信息相关的那些职责。有许多例子都能说明这一点,其中最常见的包括各种GUI库的绘图函数。比如iOS的UIKit框架中的layoutSubviews方法、wxWidgets库中的OnPaint方法、MFC框架中的OnPaint方法、Swing库中的paint/updata/repaint方法,等等。这些方法都是将GUI界面的绘制功能分配到了每一个具体的视图类中,无论是库/框架中已经存在的具体视图,或者是用户自定义的具体视图。因为只有这些具体的视图类才拥有绘制自身所必需的信息,它们能很好地履行绘制自身的这一职责。这是信息专家模式的一个直接应用。
在信息专家模式给出的建议中,由于对象可以使用自身的信息来完成它的职责,因此信息的封装性得以维护,从而支持了低耦合;同时,由于类的职责都根据自身所拥有的信息来分配,因而该模式也支持高内聚的设计。
当然,