设计模式在软件开发中的应用——2012年 11 月 25 日高级软件工程课程小论文
2014-11-03 10:08 stubman 阅读(1521) 评论(0) 编辑 收藏 举报摘要:设计模式通常是对某一类软件设计问题提出的通用的解决方案,将设计模式引入软件设计和开发过程,目的就在于充分利用已有的软件开发经验,甚至是已有的代码框架。最近一些年,设计模式已经成为软件项目团体中最热门的话题之一,并且经常在社区引起激烈的讨论。
本文介绍了设计模式的概念、描述、法则、分类以及程序设计语言与设计模式的关系,以实际案例介绍设计模式在软件开发中的应用,并在此基础上提出了一些软件设计与开发过程中使用设计模式存在的问题。
关键词:设计模式;软件设计与开发;面向对象;
1 引言
面向对象的实质是一种系统建模技术,面向对象思想只是一种高级编程规范,我们只有利用它,并且在总结和继承前人开发经验的基础上使用有特色的面向对象软件开发方法,才可能充分地利用其优越性,来解决我们系统的各种需求及需求变更。
模式是一种方案,利用这种方案,我们可以完成某项工作;模式也是一种途径,通过这种途径,我们可以达到某个目的;同时,模式也是一种技术,我们必须获取并利用有效的技术。设计模式也是一种模式,是一种完成某个目的或构思的方案。它要求使用某种面向对象提供的类及相关机制[1]。
2 设计模式概述
2.1 设计模式概念
模式(pattern)的概念最早由建筑大师 Christopher Alexander 于 20 世纪 70年代提出,应用于建筑领域。20 世纪 80 年代中期由 Ward Cunningham 和 Kent Beck 将其思想引入到软件领域,1994 年开始由 Hillside Group(由 Kent Beck 等发起成立)和 OOPSLA 联合发起了国际程序设计模式语言大会(Patterns Languages of Program Design,简称 PloP 或 PloPD),如今模式已成为软件工程领域内的一个热门话题,其在计算机领域的影响超过了在建筑界的影响[2]。
GoF 在《设计模式:可复用面向对象软件基础》一书中归纳出模式的四个基本要素:
- 模式名称(Pattern name)
一个助记名,它用一两个词来描述模式的问题、解决方案和效果。命名一个新的模式增加了我们的设计词汇。设计模式允许我们在较高的抽象层次上进行设计。基于一个模式词汇表,我们自己以及同事之间就可以讨论模式并在编写文档时使用它们。模式名可以帮助我们思考,便于我们与其他人交流设计思想及设计结果。找到恰当的模式名也是我们设计模式编目工作的难点之一。 - 问题(Problem)
描述了应该在何时使用模式。它解释了设计问题和问题存在的前因后果,它可能描述了特定的设计问题,如怎样用对象表示算法等。也可能描述了导致不灵活设计的类或对象结构。有时候,问题部分会包括使用模式必须满足的一系列先决条件。 - 解决方案(solution)
描述了设计的组成部分,它们之间的相互关系及各自的职责和协作方式。因为模式就像一个模板,可应用于多种不同场合,所以解决方案并不描述一个特定而具体的设计或实现,而是提供设计问题的抽象描述和怎样用一个具有一般意义的元素组合(类或对象组合)来解决这个问题。 - 效果(consequences)
描述了模式应用的效果及使用模式应权衡的问题。尽管我们描述设计决策时,并不总提到模式效果,但它们对于评价设计选择和理解使用模式的代价及好处具有重要意义。软件效果大多关注对时间和空间的衡量,它们也表述了语言和实现问题。因为复用是面向对象设计的要素之一,所以模式效果包括它对系统的灵活性、扩充性或可移植性的影响。
2.2 模式的描述
设计模式的描述方法有自然语言描述法、形式化语言描述法等、统一标记语言(UML)描述法。
自然语言描述法比较简单、方便,但在现实与设计之间的过渡描述不够流畅。形式化语言主要包括DisCO、LePUS、Lay0M、ADV/ADO、CDL、PDL、PDSP等,其中DISCo侧重于描述设计模式中参与者的交互行为。
统一建模语言 UML 是在多种面向对象建模方法的基础上发展起来的建模语言,主要用于软件密集型系统的建模。在多种面向对象建模方法流派并存和相互竞争的局面中,UML 树起了统一的旗帜,使不同厂商开发的系统模型能够基于共同的概念,使用相同的表示法,呈现彼此一致的模型风格。而且它从多种方法中吸收了大量有用的建模概念,使它的概念和表示方法在规模上超过了以往任何一种方法,并且提供了允许用户对语言做进一步的扩展机制。
2.3 模式的法则
在设计模式中,需要用到一些与 OOP 相关的法则,这些法则也是设计模式自身所依赖和体现的重要原则。通过使用这些法则,可以消除系统间的耦合性,提高系统内的内聚性,使系统更加灵活和易于维护 [3] 。
- 开闭法则(OCP):对扩展开放,对修改关闭。
- Liskov代换法则(LSP):继承必须确保超类拥有的性质子类中仍然成立。
- 依赖反转法则(DIP):依赖抽象而不依赖具体。
- 接口隔离法则(ISP):不应强迫客户端依赖于它们用不上的方法。
- 单一职责法则(SRP):一个类应该仅有一个原因导致其变化。
2.4 模式的分类
根据设计模式在粒度和抽象层次上各不相同,存在着众多的设计模式,我们按照模式的目的(即模式是用来完成什么工作的)对模式进行分类,从而更好地分析和使用模式。模式依据其目的可以分成创建型 (Creational)、结构型(Struetural)、行为型(Behavioral)三种[4]。
1.创建型模式
社会化的分工越来越细,自然在软件设计方面也是如此,因此对象的创建和对象的使用分开也就成为了必然趋势。因为对象的创建会消耗掉系统的很多资源,所以单独对对象的创建进行研究,从而能够高效地创建对象就是创建型模式要探讨的问题。这里有6个具体的创建型模式可供研究,它们分别是:
简单工厂模式(Simple Factory);
工厂方法模式(Factory Method);
抽象工厂模式(Abstract Factory);
创建者模式(Builder);
原型模式(Prototype);
单例模式(Singleton);
2.结构型模式
在解决了对象的创建问题之后,对象的组成以及对象之间的依赖关系就成了开发人员关注的焦点,因为如何设计对象的结构、继承和依赖关系会影响到后续程序的维护性、代码的健壮性、耦合性等。对象结构的设计很容易体现出设计人员水平的高低,这里有7个具体的结构型模式可供研究,它们分别是:
外观模式(Facade);
适配器模式(Adapter);
代理模式(Proxy);
装饰模式(Decorator);
桥模式(Bridge);
组合模式(Composite);
享元模式(Flyweight);
3.行为型模式
在对象的结构和对象的创建问题都解决了之后,就剩下对象的行为问题了,如果对象的行为设计的好,那么对象的行为就会更清晰,它们之间的协作效率就会提高,这里有11个具体的行为型模式可供研究,它们分别是:
观察者模式(Observer);
状态模式(State);
策略模式(Strategy);
职责链模式(Chain of Responsibility);
命令模式(Command);
访问者模式(Visitor);
调停者模式(Mediator);
备忘录模式(Memento);
迭代器模式(Iterator);
解释器模式(Interpreter);
2.5 程序设计语言与设计模式的关系
模式是不依赖于编程语言的[5]。从某种程度上讲,模式构成了一种语言,它比编程语言更进了一步,使开发人员可以彼此交流设计思想。然而,认识到特定的编程语言在软件系统的发展过程中扮演着重要角色是非常重要的。现在,我们必须考虑工具、库以及他们提供的通用环境,特别是当我们考虑使用象Java和C#这样的新的面向对象程序设计语言时。这些语言不但提供了传统的编程语言语法,而且还构造在虚拟机之上,并且带有完整而丰富的库或包,使得开发和重用更加容易。
3软件设计与开发过程中设计模式的运用实例
使用设计模式能为软件项目的设计与开发带来很大的优势,而要实现将模式转化为优势,需要根据实际情况,选择正确的设计模式。正确的将设计模式运用到软件设计与开发中,让整个软件的健壮性更强,让开发速度大大提高,本文将介绍基于抽象工厂的通用数据库访问层,此数据库访问层通过抽象工厂设计模式实现了多种类型的数据库的访问(Oracle,Sqlserver,Mysql,Sqlite等),大大方便了笔者的开发。
抽象工厂模式是类的创建模式之一, 其用意是定义一个创建产品对象的工厂接口, 将实际创建工作推迟到子类中。在工厂模式中, 核心的工厂类不再负责所有产品的创建, 而是将具体创建工作交给子类去做。这个核心类仅负责给出具体工厂必须实现的接口, 而不接触产品类被实例化的细节。这使工厂模式可以允许系统在不修改工厂角色的情况下引进新产品[6]。
在基于.NET的实际的应用开发中ADO.NET数据访问模型为开发者提供了以下5个命名空间: System.Data.SqlClient,System.Data.odbc, System.Data.oracleC lient, System.Data.OleDb 和System.Data.SqlServerClient。分别用于访问SQL Server, 托管空间中的ODBC (Open DataBase Connectivity)数据源、Oracle、OLEDB ( Object Linking and Embedding DataBase) 数据源以及在托管的环境下从基于ServerCE中的数据库。因此, 根据工厂模式的设计思想, 将这几个对象进行封装, 形成一个含有这些对象公共方法的抽象类, 而不必针对不同的数据库类型分别编程实现。应用程序仅需要为客户端提供一个通用的耦合, 并极大地提高了代码的复用性和系统的扩展性。基于工厂模式的数据访问层类图如图1所示。
图 1基于抽象工厂模式的数据访问层类图
因为版面所限,此图只截取了SqlserverDBFactory,OracleDBFactory为例,其他数据库类型,只需要相应的添加类似的工厂即可实现,使用快捷方便。
数据库访问层通过DBHelper类与外界联系,其他类库调用DBHelper对数据库进行操作,DBHelper,创建DataBaseConnectEntity对象,创建对象的过程中,根据配置文件中的数据库类型,选择相对于的数据库工厂,对DBFactory进行实例化。返回与数据库类型相对应的AdoHelper对象,AdoHelper对象提供了对数据库的具体操作方法。
应用设计模式和.NET平台进行数据访问层的设计,采用工厂模式思想实现了不同数据库间的异构整合。
4 软件设计与开发过程中使用设计模式面临的问题
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。虽然设计模式为提高软件开发效率以及可靠性提供了很美好的蓝图,然而,设计模式在实际软件设计与开发过程中的使用却存在着一些需要克服的问题,以下列举几个问题:
(1) 代码复用引起的问题
代码复用本是软件设计与软件开发中非常优秀的特性,避免了重复制造轮子消耗的人力物力,而设计模式的存在更加便利了代码的复用。然而,由于一些疏忽或者理解上的偏差,代码复用的过程中忽略了一些可能因为BUG的可能,造成程序崩溃,甚至引发灾难也不是不可能的。所以,在代码复用的过程中,一定要务必确认代码复用是否合理正确,是不是存在复用的可行性。
(2) 模式选用正确性的问题
虽然很多问题都是可以套用相应的设计模式进行设计,然后,不是运用了设计模式就是好的设计,如果对设计模式了解不够透彻,或者对业务了解不够深刻,都狠容易造成设计模式使用不正确,影响设计的科学性,甚至影响软件最终运行的正确性。
(3) 过度设计的问题
简单是美,是软件开发中最最重要的一个准则。不仅体现在代码简单化方便实现,而且BUG减少,容易维护,所以,能够简单实现的尽可能简单实现,这应该是软件开发过程中开发人员和设计人员应该遵循的第一准则。没有必要复杂的业务或者功能,套用复杂的设计模式,这本来就是有悖设计模式的初衷的。然而一些模式发烧友,偶尔会出现为了使用设计模式而使用设计模式的现象,难免出现过度设计,引起系统代码冗长,难以维护,简单业务复杂化等一些列本不该出现的问题。
4 结束语
设计模式是对特定问题经过无数次经验总结后提出的能够解决它的优雅的方案。但是,如果想要真正使设计模式发挥最大作用,仅仅知道设计模式是什么,以及它是如何实现的是很不够的,因为那样就不能使你对于设计模式有真正的理解,也就不能够在自己的设计中正确、恰当的使用设计模式。
设计模式并不仅仅是一个有关特定问题的解决方案这个结果,它的意图以及它的动机往往更重要,因为一旦我们理解了一个设计模式的意图、动机,那么在设计过程中,就很容易的发现适用于我们自己的设计模式,从而大大简化设计工作,并且可以得到一个比较理想的设计方案。
另外,在学习设计模式的过程中,应该更加注意设计模式背后的东西,即具体设计模式所共有的的一些优秀的指导原则,基本上有两点[7]:
- 发现变化,封装变化
- 优先使用组合(Composition),而不是继承
如果注意从这些方面来学习、理解设计模式,就会得到一些比单个具体设计模式本身更有用的知识,并且即使在没有现成模式可用的情况下,我们也一样可以设计出一个好的系统来。
参考文献
[1] 莫勇腾. 深入浅出设计模式(C#/Java版)[M].北京:清华大学出版社,2006
[2] StevenJohnMetsker. C#设计模式[M].北京:中国电力出版社.2005
[3] 郭领艳. 软件设计模式的研究及应用. 天津大学硕士学问论文[D],2005
[4] 柳小文. 设计模式研究及应用.中南大学硕士学问论文[D],2007
[5] BrandonGlodfedder. 模式的乐趣[M].北京:清华大学出版社,2003
[6]金迪,陈晓玲,林和平.基于设计模式的.NET通用数据访问层研究.吉林大学学报[D],2010
[7] 孙鸣. 使用设计模式改善程序结构,IBM开发者论坛 2001