,,,
(1. 大庆油田有限责任公司测试技术服务分公司 黑龙江 大庆 163412;2. 东北石油大学电气信息工程学院 黑龙江 大庆 163318)
相对于大多数的商用软件来说,测井软件的需求变化是非常快的。这就要求测井软件必须能够及时修改,增添新的功能,适应新的变化,即对其扩展性及复用性提出了非常高的要求。
测井软件的一个主要功能就是数据采集和控制,当地面系统的硬件板卡改变或增减时,软件就需要相应的修改。另外,随着测井项目的不断增加,软件功能和要求也会越来越多。同时,随着操作系统的升级换代,对软件的兼容性和可移植性也提出了较高的要求。
以上需求的变化,大大增加了测井软件的开发难度。严重时,为了增加某一功能,软件可能就不得不重新设计。如何适应需求的变化,最大程度的实现软件复用,已成为软件人员迫切需要解决的问题。
设计模式[1-4]的概念最早来源于建筑学,由建筑师Alexander提出来的。它是一套被反复使用,经过分类编目的,代码设计经验的总结。使用设计模式是为了可重用设计,它可以被用于新的语境中,并为使用者提供一些处理新情况的建议。设计模式使代码编制真正工程化[5],是软件工程的基石。
设计模式可以帮助人们复用现有的设计结构和体系结构[6],并且实现起来具有过程简单,方便的特点。
1)模式名
助记名,一个有助记忆的名称,将模式中的问题、解决方案以及达到的效果用一(两)个词描述出来。应用设计模式,我们可以进行较高的抽象层次设计。模式名除了有助于我们思考之外,还可以使我们更方便的与其他设计者进行设计思想及设计结果的交流。
2)问题
它可能指出了设计中存在现有问题的原因,以及可能产生的后果,也有可能描述了需要增加的设计问题,也有可能描述了一些类或对象结构,它们是导致设计不灵活的主要原因。有时候,问题部分会包括使用模式必须满足的一系列先决条件。
3)解决方案
描述的是设计的组成部分之间的相互关系及各自的职责和协作方式。模式就好比是一个设计模板,可应用于多种不同场合,所以解决方案并不是一个特定而具体的设计或实现,它是对设计问题的抽象描述,以及指出怎样用一个具有一般意义的元素组合(类或对象组合)来解决这个问题。
4)效果
采用设计模式后的应用效果及使用模式设计过程中应权衡的问题。它包括模式效果和软件效果两个方面[7-9]。在进行设计决策时,模式效果虽然不常被提到,但它在评价设计选择和理解使用模式的代价及好处等方面却具有重要意义。一般情况下,软件效果表述的是语言和实现问题,是在时间和空间的衡量。
设计模式通常分为两类[10,11]:一是按照设计目的不同进行分类,该类模式主要以具体完成的工作作为分类标准;二是按照处理范围不同进行分类,该类模式主要以用于类还是用于对象实例作为分类标准。按照第一种分类方式,设计模式可分为创建模式、结构模式和行为模式三大类。按照第二种分类方式,设计模式可分为类模式和对象模式两大类。具体设计模式分类如表1所示。
表1 设计模式分类表
各种模式均有其自身特点和适用范围,所以在选取模式前应充分了解模式的内涵及侧重点,发掘出不同模式的适用规律[12,13]。
选择合适的软件设计模式通常需要考虑以下几个方面:
1)熟练掌握各种设计模式的处理范围;
2)浏览模式的意图部分;
3)研究模式之间如何相互联系;
4)将目的相同模式进行对比研究;
5)明确重新进行设计的意图;
6)明确设计中的可变元素。
一般的选择步骤如下:
1)对所要解决的问题进行抽象,并划分适当的类型;
2)根据问题类型选择适合的设计模式;
3)规划问题和匹配模式;
4)对选取的模式进行变体,即对模式的原始结构进行修改和扩展,以解决具体问题;
5)设计并细化所需的软件体系结构;
6)对设计质量进行度量。
测井软件的用户需求是变化的,因而软件的修改是不可避免的。需求的变化主要表现在测井地面系统硬件的改动及测井项目的增加,这些变化因素在软件的设计中必须重点考虑。针对上述实际情况,在软件设计过程中采用了设计模式[14,15],则主要是因为其具有适应需求的变化的特点。此外,由于设计模式的引入,使得测井软件系统不仅具有更好的可复用性,其扩展性和维护性也变得更加容易。
测井软件的一项主要功能是实现实时的数据采集。这就要求软件与具体的采集板卡发生交互,即下传控制指令,上传采集数据。另外,实时采集软件一般采用多线程设计,使得多个线程可能同时操作同一采集板卡或一个线程控制多个采集板卡。对于测井软件来说,地面硬件系统具有唯一性,但对于硬件系统自身来讲,它所包含的采集板卡的数量和功能是可变的。
下面以超越地面系统硬件接口组件设计为例,介绍一下如何将设计模式应用于测井软件的设计中,以提高软件质量。
定义: 保证一个类仅有一个实例,并提供一个访问它的全局访问点。适用于在一个系统要求一个类只有一个实例时才应当使用单例模式。
这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
优点:对唯一实例的受控访问;自行实例化并向整个系统提供这个实例。单件模式取代了全局对象,降低了代码耦合度,便于程序修改和维护。
针对超越地面系统硬件的唯一性,以前的测井软件设计常常将硬件接口对象定义为一个全局变量,而全局变量是程序员心中永远的痛。主要问题包括:
1)变量名冲突,增加管理成本。
2)耦合度增加。
3)单个实例问题。全局变量不能阻止程序员定义一个类的多个对象实例。在一个多人参与、并行开发的大型项目中,这也会增加很多管理上的负担。
4)初始化顺序。全局变量不可能保证相互之间遵循特定的初始化顺序,这完全由编译器决定。
5)多线程访问。当多个并发的线程都需要访问某些全局变量时,我们必须使用各种同步机制,小心地保护这些全局变量,以免陷入并发冲突的泥潭。
设计模式中的单件模式就很好地解决了此类问题,它即保证了对象的唯一性,也避免了全局变量带来的问题。单件模式就是以一个类只有一个对象实例为设计目的,并提供一个访问该对象实例的全局访问点。程序员不能通过类的构造函数获得对象实例(因为构造函数声明为private或protected类型),但可以从该类提供的静态成员函数得到该类唯一的对象实例指针或引用。从某种意义上说,我们可以把这个对象实例看做一个隐藏在单件类内部的“隐式全局变量”。因此在超越测井软件的硬件接口设计中采用了单件模式,程序部分代码如下:
class CExceedHard : public CHardInterface
{
private:
CExceedHard(void);
public:
~CExceedHard(void);
static CHardInterface* GetInstance(void);
static void DelInstance(void);
}
定义:定义一个创建对象的抽象类(接口),但是却让子类来决定具体实例化哪一个类。当一个类无法预料要创建哪种类的对象或是一个类需要由子类来指定创建的对象时,我们就需要用到该模式了。工厂方法模式可以根据不同的条件产生不同的实例,但这些不同的实例通常是属于相同的类型,并具有共同的父类。
优点:当系统扩展需要添加新的产品对象时,仅仅需要添加一个具体对象以及一个具体工厂对象,原有工厂对象不需要进行任何修改,也不需要修改客户端,很好的符合了“开放-封闭”原则。
超越地面系统硬件接口组件中聚合多个采集板卡对象,这些采集板卡对象是否都需要创建,还是选择性的创建几个,是由组件根据测井项目的需要来动态创建的。硬件接口组件的调用者不知道,也无需知道具体要创建哪些采集板卡对象。这种方式可以更好地适应未来采集板卡数量的改变及功能的变化。
设计模式中的工厂方法模式可以很好地解决此类问题。工厂方法模式以定义一个用于创建对象的接口为设计目的,让子类决定实例化哪一个类,将实例化延迟到其子类。对应的问题范畴就是一个类需要实例化另一个类的派生类,但不知道具体是哪个派生类。工厂方法模式允许派生类来做出决定。该模式在定义框架的过程中很常用。这是因为框架存在于一个抽象的层次上。通常,它们不知道,而且也不应该关心特定对象的实例化。它们需要将特定对象的决策推迟框架的用户。
在超越地面系统硬件接口组件设计中,就是采用工厂方法模式来动态创建需要的板卡对象。即根据测井项目需要的测量通道来动态创建含有此通道的采集板卡对象,而无需由程序员在代码中明确创建某一类型的采集板卡对象。这种实现方式也体现出了设计模式的设计原则和理念,即针对接口编程,而不要针对实现编程,根本的意图是适应需求变化。程序的部分代码如下:
short CExceedHard::AddChannel(CString strName, UINT uChannelSize, UINT uBufferLen, DataType iDataType)
{
CBDChannel* pChannel = new CBDChannel(strName, uChannelSize, uBufferLen);
if(pChannel != NULL)
{
BDChannelInfo* pChInfo = NULL;
for(int i=0; i { pChInfo = (BDChannelInfo*)m_inherentChannelInfoList.GetAt(i); if(pChInfo != NULL) { if(strName == pChInfo->strChannelName) { CString strBoardName = pChInfo->strBoardName; if(strBoardName == _T("SC58301A")) pBoard = new CBD58301A(); else if(strBoardName == _T("SC58112")) pBoard = new CBD58112(); else if(strBoardName == _T("SC58114")) pBoard = new CBD58114(); if(pBoard != NULL) { m_usedBoardList.Add(pBoard); return HD_SUCCESS;}}}}}} 采用该种设计的优点:当系统扩展需要添加新的产品对象时,仅仅需要添加一个具体对象以及一个具体工厂对象,原有工厂对象不需要进行任何修改,也不需要修改客户端,很好的符合了“开放-封闭”原则。 设计模式既简单又复杂。使用设计模式要求我们添加更多的类,进行更多的设计工作,这是设计模式复杂的一面。它提醒我们,设计模式只应对变化或可能变化的部分使用,对于不变的部分滥用就会造成“过度设计”。由于超越地面系统的测井软件具有用户需求变化,软件修改不可避免等特点。因此,在软件设计过程中采用设计模式,此外,由于设计模式的引入,使得测井软件系统不仅具有更好的可复用性,其扩展性和维护性也变得更加容易。实现了面对变化的需求时,我们只需要修改很少的代码,就可以满足新的需求,这是设计模式简单的一面,它可以大幅度简化我们后续的开发和维护工作。 设计模式最根本的意图就是适应需求变化,这一点正是测井软件急需解决而不好解决的问题。另外,设计模式可以有效地提高软件的设计和代码复用性,便于成果的继承,事半功倍。因此,学习运用设计模式是开发高质量测井软件的必由之路,前景广阔。 [1] GAMMA E.设计模式—可复用面向对象软件的基础[M].李英军,译.北京:机械工业出版社,2000:55-75. [2] JACOBSON I.The unified modeling language development process [M].Boston: Addison Wesley, 1998:141-152. [3] MIKKONEN T.Formalizing design patterns[A].Proceedings of International Conference on Software Engineering [C].New York: IEEE Computer Society Press, 1998:115-124. [4] GAMMA E. Design patterns: elements of reusable object-oriented systems[M] . Boston: Addison Wesley,1995:75-90. [5] 万剑怡,薛锦云.使用规范匹配实现设计模式的自动获取[J].小型微型计算机系统,2002,23(3):326-329. [6] 张世博,周树杰,闵艳.JAVA程序开发中的设计模式[J].微型电脑应用, 2002, 18(9): 45-47. [7] 钟茂生,王明文.软件设计模式及其使用[J].计算机应用,2002,22(8):32-35. [8] 廖志刚,李增智.设计模式在系统中的应用[J].计算机工程与应用,2002, (12): 7-10. [9] GAMMA E. Design patterns elements of reusable object-oriented software[M].北京:机械工业出版社,2002:124-130. [10] 童立,马远良.设计模式在基于组件的框架设计中的应用[J].计算机工程与应用,2002, (17): 123-128. [11] ALUR D.Core J2EE patterns[M]. 北京:机械工业出版社,2002:89-120. [12] ALAN SHALLOWAY & JAMES R. TROTT.设计模式精解[M].清华大学出版社,2004,12:1-238. [13] 王咏武 王咏刚.道法自然-面向对象实践指南[M].电子工业出版社,2004:10,1-432. [14] 刘海岩.设计模式及其在软件设计中的应用研究[J].西安交通大学学报,2005,39(10):1043-1047. [15] ECKEL B.Java 编程思想(第二版)[M].侯捷,译.北京:机械工业出版社,2002:5-120.3 结束语