设计模式GRASP和GoF是怎样解决耦合的问题
最近网友Uranus问我了一个非常有趣的问题:设计模式GRASP和GoF是怎样解决耦合的问题?实际上虽然同是设计模式,解决对象间耦合的问题都是它们的终极目标,但是它们在解决它们的方式上却是完全不同的,GRASP是从整体设计上解决耦合的问题,而GoF却是从具体实现上解决的,在这里我们不妨探讨一下。
设计模式GRASP其名称翻译过来就是“通用职责分配设计模式”,从字面上我们不难发现,“职责分配”是GRASP的核心。GRASP认为,在对象设计时,只要各个对象的职责分配清楚了,能够各司其职,耦合就会降低。因此,GRASP应用的一个非常重要的场景是,我们项目的业务需求已经分析清楚了,正准备开始设计对象。GRASP告诉我们,一个系统里面到底应当有哪些对象,应当来源于领域模型,换句话说就是来源于现实时间中的事物。现实世界中有什么事物,我们软件空间中就应当有什么对象。当然,现实世界中的事物不一定都需要在软件空间中有对应的对象,要根据需求而定,但软件空间中的对象应当对应于现实世界中的事物,这种设计原则有个专用术语叫“低表示差异”。按照这个原则设计好了我们的对象以后,应当如何分配它们的职责呢?当然是来源于现实世界。我们的对象在现实世界中应当有什么职责,那么我们的对象就应当有什么样的职责。如此分析,每个对象的职责就非常清楚,那么业务需求中的各种功能应当如何分配给对象呢?现实世界是怎样分配的,软件世界就怎样分配的。最后,如何在各个功能中将对象组织起来呢?GRASP的专家模式解答了这个问题;对象应当由谁来创建,GRASP的创建者模式解答了这个问题。GRASP通过这样一个步骤,设计对象、分配职责、确定功能、建立联系,一个项目的整体框架就展现出来了,而这样一个框架必然是低耦合高内聚的,为什么呢?这方面的详细论述朋友们可以看我写的《(原创)一个优秀软件开发人员的必修课:GRASP软件开发模式浅析》,这里我就不再累赘了。但是,GRASP只解决了整体设计中的耦合问题,只解决了一部分,而另一部分需要依靠GoF。
前面我们说到运用GRASP进行整体设计依靠的是现实世界和业务需求。这样的分析还没有介入任何技术的成分。但是随着我们的进一步分析,我们开始尝试运用各种具体的技术去实现这些业务需求需要实现的功能,我们发现问题出现了。比如,我们在整体设计的时候设计的一组继承关系的对象,其它需要使用它们的对象如何去调用它们?去具体地调用它们的某个子类吗?这显然不是一个好的方案,GoF的工厂模式告诉我们,这里需要设计一个工厂类。这里增加的这个工厂类不是来源于现实世界,GRASP称这样的对象为“纯虚对象”。纯虚对象是从现实对象中抽象出来的一些功能,但GRASP不能告诉我们应当抽象出哪些纯虚对象,解决这样的问题需要运用GoF。再比如,商场中的许多商品都需要打折,但是各种商品的打折策略其实并不是完全不同的,是有相似之处的,有的是按比例打折,有的是满500送100,有的是学生才打折。如此这般,如果我们对一个商品父类的所有商品子类都重载一遍自己“折扣”方法,显然不是一个好的设计,不如将所有的折扣方法抽象出来,形成抽象的“折扣策略”父类和具体的折扣子类,然后由各个商品子类自己去选择自己需要的折扣策略。如此这般,就运用了GoF的策略模式。通过以上分析,我们不难看出,GoF是在具体实现中去解决对象的耦合问题的。它是在GRASP分析的整体框架下,对一些具体的对象及其方法进行重新组织,解决对象耦合问题的。
现在,我们再换一个角度,从整个软件开发过程来分析GRASP和GoF。按照RUP的设计流程,首先当然是设计用例模型和领域模型,然后就进入分析模型和设计模型阶段。但是非常神奇的是,分析模型和设计模型既非常相似,又没有一个明显的界限来区分哪些是分析模型,哪些是设计模型。按照我的理解,分析模型是从不包含任何技术的,纯业务的角度开始对象分析的。不论是Java的项目、C++的项目还是Delphi的项目,不论是B/S的结构还是C/S的结构,分析模型的开始都是一样的。从分析模型的一开始,就是运用GRASP一步一步的进行对象分析和设计的。通过这样的设计,对应于现实世界的一个一个对象被设计出来,然后赋予它们职责和功能,分配它们属性和方法,建立起它们相互之间的联系与协作。分析完这些以后,它们的各个具体的属性和方法就需要开始实现,这时技术的东西开始介入,我们开始考虑把一些具体的实现抽象为接口和抽象类,以适应今后的需求变化。这时候,GoF的一些设计模式开始广泛运用,纯虚对象开始一个一个出现。把具体的实现抽象为接口和抽象类,以适应今后的需求变化,是GoF一个非常重要的设计原则,GoF中的许多设计模式都是由此而生的。分析模型和设计模型的界线也许就是分析模型还仅仅只是分析阶段,而设计模型已经进入到了软件开发阶段,但这样的界线是模糊的,并没有一个绝对的时间点。
最后,一个很有意思的问题是,GRASP的作用大还是GoF的作用大?做分析的朋友可能说GRASP的作用大,因为所有问题越是在项目开发的早期解决,越是给项目带来的代价越小;做设计的朋友可能会说GoF的作用大,因为GRASP只是在对象分析的初期运用,而GoF的运用贯穿整个软件设计的始末,作用时间更长。我认为这个问题不好回答,它们各司其职,我们只有配合使用它们才能有效地提高我们的设计水平。上一篇: 一堂如何提高代码质量的培训课(3)
下一篇: 如何使用Hive集成Solr?