非COM环境下的接口编程-问题,技巧,应用(一)

2008-02-23 07:18:13来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

先行知识:Delphi/接口/Dll/OOP

难度:★★★☆☆

引子:

接口的概念由来已久,早在COM出现之前(COM应该是95年左右)接口的概念就已经在面向对象的开发中根深蒂固了,著名的《设计模式》(94年出版)中也指出“针对接口编程而不是针对实现编程”。使用接口可以降低软件系统中不同模块的偶合性,利于软件系统的更新与维护。接口的优点绝对不只是出现在COM中,事实上在大多数的编程任务中接口都是一个不错的选择。(用delphi开发过Web Service的朋友知道,delphi也是使用接口来描述Web Methord的,所以接口的概念在面向对象领域永远不会过时)本文不是一篇讨论COM的文章,而是想通过一个例子来说明在delphi中接口的实际作用,以及在开发中可能碰到的问题和所需的技巧。

例子:

※第一印象:

熟悉Windows程序设计的人应该早已经在他们开发的系统中使用到了DLL,如果我们要把对象放入DLL中维护(而不仅仅是一些函数和过程)怎么办呢?最容易想到答案是使用COM。除此之外还有什么办法呢?使用delphi中的动态包bpl或则一些其他的一些办法(如内存拷贝)也许可以解决问题。不过现在我们要创建一个标准的DLL文件,我们可以象使用COM一样直接通过接口来操作维护在其中的对象,但又不用象COM组件一样需要注册,它应该是如同普通的DLL文件样只要加载就可以正常工作。这样的优点是明显的,也许我们正在需要一个如同大多数绘图软件一样允许有插件扩充的程序,那么除了标准的COM技术外我们可以将实现约定接口(也就是插件的契约)的对象放在一个标准的DLL库中,在主应用程序中根据一份可由用户配置的文件中的不同插件名称和所在路径来依次加载这些DLL,这样我们的插件下载到客户的计算机中后根本不用任何注册安装过程,而仅仅只是在主程序中配置它就可以正常工作了。这个过程看起来象这样:

for I:=0 to PluginCount-1 do

//PluginCount是从配置文件中得到的已经“安装”的插件数目

begin

Dllhnd[i]:=loadlibrary(PlugPath);

//PlugPath为每一个dll的路径,以由前面程序从培植文件中得到

@GetPlugIntf:=GetProcAddress(Dllhnd[i],’GetPlugIntf’);

PlugIntf[i]:= GetPlugIntf; //GetPlugInth可以返回一个IunKnown的接口

end;

现在我们就得到所加载的每一个插件的接口并可进行操作了。从上面的代码中可以大概的看出一些我们需要管理对象的DLL的样子:这个DLL只有一个唯一的导出函数以获得其中维护的对象的接口(GetPlugIntf,也有可能有其它的导出函数,但这个是必须的),这个函数可以返回一个对象实现的接口也可以直接返回Iunknown接口(这样便于用一个数组管理所有的插件接口,也利于用循环结构实现程序,就象上面看到的那样),主程序在需要的时候进行转换。另外我们的主程序需要和Dll共用一个描述接口的文件(契约)。返回接口导出的函数看起来象这样:

var

OurObject:TintfObject;

function GetFooObjectIntf:IUnKnown;stdcall;

begin

if not assigned(OurObject) then

begin

OurObject:= TintfObject.Create;

end;

result:= OurObject as IUnKnown;

end;

有了上面的描述后可以看到要在一个普通的DLL中维护对象并象COM一样发布对象的接口也是一件很简单的事情,没什么特别的,不过上面的讨论有一个很大的问题:如果我们的DLL只有一个导出函数,这意味这它只能导出一个对象的接口,就象上面那样,但如果我们要在这个DLL中维护多个对象怎么办呢(特别是一些按照继承关系连接起来的对象家族,或者具有共同特点的对象)?

※使用工厂模式:

解决上面问题的最好办法是在DLL的设计中使用工厂模式来管理其中维护的多个对象,这样做不仅可以维护不同的对象,还可以维护一个类的多个实例。然后我们只用在那个唯一的导出函数中导出这个工厂对象的接口,其它的对象接口都可以通过这个接口获得。比如象下面的样子:

function TFooManager.CreateAFoo: IFoo;

begin

inc(FooNum);

if length(FList)<FooNum then

setlength(FList,FooNum*2);

FList[FooNum-1]:=TFoo.Create;

result:=FList[FooNum-1] as IFoo;

end;

例如上面的TfooManager就是一个工厂类,它负责管理DLL中具体对象的生命周期,CreateAFoo创建一个DLL中名为Foo的对象,并把它保存到自己的私有字段,一个动态数组中:

private

FList:array of TFoo;

注意,上面的代码中用到了一个小小的技巧,每当flist的空间不够大时,我们采用了双倍分配策略,既在这个时候我们给flist它所需要的空间的两倍的空间。在创建对象请求频繁的时候,这无疑是一条有效的提高执行效率的策略,因为setlength函数在重新分配空间并依次移动已经存在的元素时是一个耗费时间的过程。这个策略也是一个典型的空间换时间的算法。

现在有了工厂模式后,我们要在dll中管理多个对象或则不同的对象就十分方便了,我们甚至可以让CreateAFoo接受一个参数以确定创建何种类型的对象,并且每新增加一种对象我们就在TfooManager工厂类中添加一个对象的私有字段(动态数组,当然也可以根据你的需要使用其它的数据结构如链表TList)。关于这个工厂类的其它方法的代码请参看文后的代码清单。

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:COM程序编写入门(三)

下一篇:delphi 7.0生成的AgentObjects_TLB.pas出错导至Ms Agent不能发声