Delphi 中,基于接口,封装类为 BPL 包动态加载的程序架构 之二

2017-04-06 11:33:31来源:CSDN作者:pcplayer人点击

在Delphi 中,将一个或多个类,编译为一个 BPL,让需要使用到这几个类的代码来调用,实际上在使用 DELPHI 的时候,我们就随时在应用这个功能 --- DELPHI IDE 里面安装的控件,就是让 IDE 动态加载控件的设计期 BPL。

这样的用法有一个问题:你要用到那个类的地方,必须【知道】那个类。也就是必须引用那个类的单元。尽管那个类你已经编译为了 BPL,但你的代码还是需要引用那个类的单元,也就是你需要那个类的 PAS 文件或者 DCU 文件。

一个好的程序架构,各类、各单元之间的耦合度,应该尽量低。并且,如果完成该功能的类增加了,比如新写一个相似功能的类编译为另一个BPL文件,那么,你的程序就需要增加对这个新的类的引用,你的程序必须修改源程序,重新编译。


因此,我们先针对上述问题,提出我们想要的:

1. 运行期动态加载一个BPL,使用BPL里面的类提供的功能;但不需要知道和引用这个BPL里面的类的单元和类的类型。

2. 增加新的相似功能的类,增加新的 BPL 文件,也只需要在运行期动态加载 BPL 的时候,换掉BPL文件就可以,程序不用重新编译,也不必引用新的类所在地单元。

要实现上述两点,可以采用基于接口的方式。首先单独定义一个接口单元。实现该接口的类,需要引用该单元;需要使用这个类提供的功能的程序,也引用该单元。但程序不需要引用类的单元,也不需要知道类的类型。当有新的类实现相似功能的时候,同样实现该接口。对于程序来说,因为都是调用相同的接口,具体是哪个类来实现这个接口程序不用关心,因此程序不需要做任何改动。 采用接口,降低了耦合度,同时提高了灵活性。

在上述前提下,首先把接口单元,放入一个单独的包(源代码情况下就是一个单独的 DPK 文件,编译后就是一个单独的 BPL 文件)。编译为BPL文件,同时DELPHI会输出一个 DCP 文件。

然后,再将实现该接口的一个类或多个类,放到一个 DPK 里面,因为这些类需要引用接口单元,而接口单元又编译为 BPL 文件,所以这个 DPK 需要引用(require)接口BPL对应的 DCP 文件。这样就可以编译出一个 BPL 文件。


而使用该接口的程序,设置为需要运行期包的编译方式,并填入接口单元所在BPL包文件的名字,就会让程序在运行启动时静态加载这个接口单元所在地 BPL 文件,如果文件不存在,则程序运行失败。

程序运行起来后,程序的代码可以通过动态加载一个包的方式,将实现接口的类所在的包加载进来。


问题来了:程序并不知道类的类型(没有引用类所在单元),如何根据需要创建该类的对象?没有对象实例,何来接口实例?

一个简单的办法:自己写一个简单的类工厂。这个类工厂本身,也编译为一个 BPL 包!也同样基于接口!

这样的架构,简单的描述就是:


1. 接口定义编译为 BPL 包,让程序静态加载;


2. 实现接口的类,编译为另一个 BPL 包,让程序动态加载;


3. 用类工厂来根据需要创建相应的类的实例,然后可以从该实例获得接口的实例。程序只需要调用接口里的方法、函数、属性等东西就可以了。创建的类不同,实现的功能可能就不同,但因为接口相同,对于使用接口的程序来说,就实现了传说中的【多态】的功能,只是这个多态不是基于类继承的方式,而是基于接口。

要让程序编译为运行时静态加载一个 BPL,而不是编译时直接把该 BPL 涉及到的 DCU 编译到程序的 EXE 里面去,需要在 DELPHI IDE 里面做如下设置:


Project -- Options -- Packages -- Build with runtime packages 选项打勾,然后在该选项下面的输入框里填上你的程序需要静态加载的 BPL 的名字。注意该输入框里已经默认把 DELPHI 提供的一堆 BPL 的名字都填在里面了。如果那些名字不去掉,则编译后的程序的 EXE 会非常小,但你发布程序的时候就必须带上一大堆 DELPHI 提供的 BPL。好处是,下次再发布程序,只要那台电脑里已经有那些BPL了,你就可以不用拷贝那么多的 BPL 给客户了,只需要给他一个很小的 EXE 文件就好了。


至于动态加载的 BPL,和动态加载 DLL 类似。

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台