1引言
从上世纪60年代末开始,由于计算机软件对生产力有巨大的推动作用,各种大型、复杂的软件系统相继被开发出来。然而,随着软件系统规模的扩大和复杂性的增加,软件开发对人力、物力的需求越来越大,同时软件系统的可靠性和可维护性明显降低,软件行业出现了危机。直到80年代,软件开发采用面向对象设计思想和开发技术,软件危机才在一定程度上得到缓解。面向对象开发方法的核心思想是将系统看成是对象及对象之间的相互关系的集合,思维方式更接近 人类认识世界的规律,克服了面向过程开发存在的诸多弊端。但是采用面向对象的方法来开发软件也需要一些正确的开发原则来指导,否则,开发的软件将不可避免地带有某些缺陷,如系统过于僵硬,不能很好地适应需求变化;系统过于脆弱,往往修改一处代码会带来无法预测的后果;系统复用率低,黏度过高等等。为了避免上述缺陷,设计出具备良好的可扩展性、可复用性、易维护性的系统,我们应在系统设计和实践阶段采用设计模式的思想。
设计模式是软件复用技术中的一个重要概念[1]。它是指以文档的形式把面向对象的软件设计经验记录下来,并予以系统的命名、解释和评价,使不同的开发人员在进行不同系统的设计与开发时,可以使用别人的成功经验而不必为普通的、重复的问题重新设计解决方案,使设计者更容易理解其设计思路,能为自己的问题找到更适合的解决办法,更快更好地完成系统设计。随着技术的不断完善,设计模式的种类日益增多,相对于GoF在1994年提出的23种通用设计模式,数量已大大增加。选择适合自己系统的模式对系统的设计与实现都至关重要,当对各种模式有足够全面的了解时,许多设计决策就自然而然产生了。为了研究设计模式是如何影响系统设计与实现的,应结合面向对象设计原则和软件工程思想来进行探讨[2]。
2 从设计原则到设计模式
2.1 设计原则
我们之所以提倡设计模式,就是为了代码复用,增强系统的可维护性。面向对象有几个原则:开闭原则、里氏代换原则、依赖倒转原则、接口隔离原则、合成/聚合复用原则、最小知识原则、单一职责原则和抽象原则。开闭原则具有理想主义色彩,它是面向对象设计的终极目标。其他几条,则可以看做是开闭原则的实现方法。设计模式就是实现了这些原则,从而达到了代码复用、增加可维护性的目的。
(1)开闭原则
一个软件实体应当对扩展开放,对修改关闭。开闭原则是面向对象设计中最基础的设计原则,它指导我们如何建立稳定灵活的系统。我们在重构代码时尽量在不修改原来代码的情况下进行扩展。这样在扩展的同时对旧版本有很好的支持,使得系统更加灵活,具有很强的适应性。
(2)里氏代换原则
子类可以扩展父类的功能,但不能改变父类原有的功能。父类中凡是已经实现好的方法(相对于抽象方法而言),实际上是在设定一系列的规范和契约,虽然它不强制要求所有的子类必须遵从这些契约,但是如果子类对这些非抽象方法任意修改,就会对整个继承体系造成破坏。
继承作为面向对象三大特性之一,在给程序设计带来巨大便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加了对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能会产生故障。
在模块中应当尽量从抽象类中继承,而不是从具体的类中继承。里氏替换是软件代码复用的重要基础。
(3)合成复用原则
合成是值的聚合,聚合是引用的聚合。合成和聚合都是关联的特殊种类,聚合表示整体和部分的关系,表示“拥有”;合成则是一种更强的“拥有”,部分和整体的生命周期是一样的,合成的新的对象完全支配其组成部分,包括他们的创建和湮灭。一个合成关系的成分对象是不能与另一个合成关系共享的。我们在开发中应尽量使用合成/聚合,而不是使用继承。
(4)依赖倒转原则
依赖倒置原则的核心思想是面向接口编程。要针对接口编程,不要针对实现编程;传递参数,或者在组合聚合关系中,尽量引用层次高的类。高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖于细节,细节应该依赖于抽象。程序在需要引用一个对象时,应当尽可能的使用抽象类型作为变量的静态方法,即面向接口编程,是达到开闭原则的途径。针对接口编程和抽象类就是要做到一个具体的类应当只实现类的接口和抽象类中声明的方法,而没有多余的方法。依赖倒置原则基于这样一个事实:相对于细节的多变性,抽象的东西要稳定得多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定得多。使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
(5)接口隔离原则
客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。
建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法应尽量少。我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。在程序设计中,依赖几个专用的接口要比依赖一个综合的接口更灵活。接口是设计时对外部设定的“契约”,通过分散定义多个接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。在面向对象设计时恰当地划分角色和角色对应的接口是非常重要的。将没有关系的接口合并在一起是对角色和接口的污染。原则要求我们不同的角色要给不同的接口,而不能交给一个接口。
(6)抽象原则
用抽象构建框架,用实现扩展细节。因为抽象灵活性好,适应性广,只要抽象得合理,可以基本保持软件架构的稳定。而软件中易变的细节,我们用从抽象派生的实现类来进行扩展,当软件需要发生变化时,我们只需要根据需求重新派生一个实现类来扩展就可以了。当然前提是我们的抽象要合理,要对需求的变更有前瞻性和预见性。抽象类不会有实例,一般作为父类被其他类继承,包含了子类的共同属性和方法。具体类是不被其他类所继承的,即子类继承了抽象类后,这个子类不应再被其他类所继承。
(7)迪米特法则
也称最少知识原则,一个对象应当对其他对象尽可能少地了解,让每个类专心做自己的事情而不去关心其他的事情,以降低耦合度。
(8)单一职责原则
不要存在多于一个导致类变更的原因,即一个类只负责一项职责。单一职责可以降低类的复杂度,其逻辑要比负责多项职责简单得多;提高类的可读性,提高系统的可维护性;
变更是必然的,但是单一职责原则可以将变更引起的风险降低,当修改一个功能时,可以显著降低对其他功能的影响。单一职责原则不只是面向对象编程思想所特有的,只要是模块化的程序设计,都适用单一职责原则。
2.2 设计模式
设计模式是一些富有经验的面向对象技术专家针对特定问题而设计的成功解决方案,可作为通用原则和惯用法,用于指导软件设计。将这些原则和惯用法以一种结构化的形式加以描述,给出问题和解决方案,然后形成一套模式,这就是设计模式[1]。
模式的一个主要目标就是以一种别人容易接受的方式,捕捉那些重复出现问题的解决方案。毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,如同大厦的结构一样,设计模式是软件工程的基石脉络。一般而言,一个模式有四个基本要素:模式名称、问题(描述应该在何时使用模式)、解决方案(描述设计的组成成分、它们之间的相互关系及各自的职责和协作方式)、效果(描述模式应用的效果及使用模式应权衡的问题)。
2.3 设计思想
在计算机科学中,对设计模式的简单定义就是对于一类重复出现的问题的一种可重用的解决方案,在软件工程中一个设计模式往往能解决一类软件设计问题。设计模式的使用帮助设计人员更加简单、方便地改进或复用以往成功的设计和体系结构,采用设计模式的软件系统具有更好的可维护性。
软件在不断进化,需求在不断改变,所以软件应该适应变化[3]。设计模式是为了让软件更加适应变化,有更多的可复用性。想要适应变化,就应该封装变化,让变化的影响最
小;封装复杂性,提供简单的接口;针对接口编程,而不是针对实现编程;最大程度地继承、组合、委托、多态和参数化。
耦合是元素与其他元素的连接、感知及依赖的度量。低耦合往往能够减少修改软件所需的时间、工作量和缺陷。例如信息专家模式支持低耦合度,因为信息专家模式把职责分配给拥有完成职责所需信息的对象。如果我们把职责分配给其他对象,则信息需要被这些对象共享,会增加耦合度。内聚是对一个类中的各个职责之间相关程度和集中程度的度量
[4]。一个具有高度相关职责的类并且这个类所能完成的工作量不是特别巨大,那么它就具
有高内聚度。不要给一个类分派太多的职责,在履行职责时尽量将部分职责分派给有能力完成的其它类去完成。不相关的职责不要分派给同一个类。
设计模式思想引入企业级数据库系统开发,于传统的开发模式可谓是一场革命。设计模式之于面向对象的设计与开发的作用就有如数据结构之于面向过程开发的作用一般,其重要性不言而喻。通常意义上的三层架构就是将整个业务应用划分为:表现层(UI)、业务逻辑层(BLL)、数据访问层(DAL)。区分层次的目的即为了“高内聚,低耦合”的思想。
可复用面向对象软件系统现在一般划分为两大类:应用程序工具箱和框架,我们平时开发的具体软件都是应用程序,Java的API属于工具箱;而框架是构成一类特定软件可复用设计的一组相互协作的类,EJB是Java应用于企业计算的框架。框架通常定义了应用体系的整体结构类和对象的关系等等设计参数,以便于具体应用实现者能集中精力于应用本身的特定细节。框架主要记录软件应用中共同的设计决策,框架强调设计复用,因此框架设计中必然要使用设计模式。
另外,设计模式有助于对框架结构的理解,成熟的框架通常使用了多种设计模式,熟悉这些设计模式可帮助我们掌握EJB、J2EE等框架的结构。
3 GRASP职责分配模式与设计模式的应用价值
GRASP 作为设计模式来描述对象设计和职责分配的基本原则。GRASP原则是对其他设计模式的归纳。为了使系统满足适应外部服务系统的可变接口,我们应用适配器模式,设计具有同一接口的Adapters;为了按系统的要求灵活选择所需的Adapter,我们应用工厂模式,设计一个Factory,按系统当前的要求实例化相应的Adapter;为了系统中同一类的Adapters实例具有唯一的创建逻辑,我们应用单实例类模式,使Factory在系统中仅有唯一的一个实例。
设计模式在以下几个方面体现了其应用价值:许多模式可以增强被包装类的复用能力;有效地处理需求变更,做到以不变应万变;大大减少各个分析类之间的耦合和依赖;是最有效的学习别人经验的方法,帮助我们更好地理解前人的知识精华,阅读优秀框架的源码,也使程序员之间的交互更为方便有效。设计模式的出现是对项目开发中不断出现相同问题的解决方法的一个最佳总结。它是项目维护升级时重要的基石。如果我们把项目比作成盖房子的话,设计模式就是一个个方砖,可拆可加可扩可收,增强系统的稳健和可扩展性,大大降低了相同问题的维护量。
4 小结
通过对设计模式的理论及应用价值的研究,我对其在系统设计整个项目过程中的地位和作用有了更加明确的认识。设计模式作为软件领域里的一个重要研究方向,对软件设计和开发的效率、复用率、成本、灵活性都有着重要意义[5]。各种设计模式提供了比较成熟的面向对象设计经验,大大推进了面向对象方法的发展与完善,对当前的很多系统都有着深刻影响,尤其在分布式和并行计算领域的应用系统中也提出了许多相关模式。虽然设计模式有诸多优点,但若无法掌握其本质而滥用模式就无法真正发挥其优势,反而会适得其反。我们在实际应用中要避免滥用带来的软件结构的复杂性和冗余,不能为了使用模式而
使用。只有当我们能够洞悉软件应用中所包含的各种设计模式及其背后的面向对象设计思想,全面地了解设计模式对系统各方面的影响时,才能对整个应用的体系结构有比较完整深入的理解和把握。为此,本文就设计模式的概念、发展状况、理论及应用价值进行了讨论,这些讨论不仅能帮助我们更好地理解设计模式本身,更使我们对隐藏在设计模式背后的面向对象思想有了更深一层的认识。
因篇幅问题不能全部显示,请点此查看更多更全内容