Delphi面向对象学习随笔八:物理封装

作者:巴哈姆特
http://www.cnpack.org
(转载请注明出去并保持完整)
前面说过的封装其实是逻辑意义上的封装。逻辑封装是对某一特定逻辑功能模块的封装,这个特定逻辑功能块可以是一个类,当然也可以是一个包,他们都有自己的逻辑边界。另一种封装方式,我们通常叫它为物理封装:物理封装其实是具体实现代码的物理集合,他可以以bpl,dll,com+等形式体现。

    逻辑封装里,对象的传递、数据共享与调用相对要简单的多,只要我们引用类所定义的单元(unit)就可以直接访问类中public和published所公布出来的属性或方法,在编译的时候,编译器会把工程内所有引用的单元全部打包到exe中。逻辑封装最终是以一个独立的物理文件存在的。虽然简单,但是无法实现物理上的切割,一旦其中某个单元或代码段发生改动,那么其他的单元或代码段也需要重新编译和连接。

    而在物理封装中,对象的传递、数据共享与调用要复杂的多了,由于在编译的时候,exe和dll或bpl是两个或多个文件,所以你无法像在逻辑封装中那样简单的uses那个unit。而物理封装的好处是可以减少维护量,因为每个dll都是动态调用的,所以,我们只需要更新我们改动过的相应的dll,而其他的部分则可以不用改动。

  用DLL封装对象:
    用DLL封装函数,我想几乎是所有程序员熟悉到不能再熟悉的技术,而且我们可以找到很多相关的书籍和资料。这里我们只讨论怎么用DLL来封装对象。
    用DLL封装对象有以下的好处:
      一、可以节约内存。我们可以在使用到DLL资源的时候动态装载,不用时释放。
      二、提高代码重用。DLL在封装好以后,我们可以使用任何一个支持DLL的开发工具来调用它。
      三、可以使软件拆分成若干个小块,这样可以有效的降低维护量。
    注意:如果你只为了减少软件体积而使用动态库,那么我建议你还是放弃使用动态库吧。

    当然,想使用DLL封装对象也有一定的困难:
      一、调用DLL的EXE只能使用DLL中对象的动态绑定的方法。
      二、DLL中的对象只能在DLL中创建。
      三、在DLL和调用方,都需要对封装的对象和被调用的方法进行声明。

    我们来看下面的例子:

    首先我们声明一个类:

typeTNewClass = class(TObject)publicprocedure SayHello; virtual;// 注意,这里不能使用静态方法,必须使用动态绑定(或者说晚绑定)技术。// 至于为什么——虚方法表有关,大家可以找其他资料详细研究^_^end;procedure TNewClass.SayHello; // 实现部分
beginShowMessage('Hello');
end;

 

新建一个Library项目

library dll;function GetObj: TNewClass; stdcall;
begin // 创建对象Result:= TNewClass.Create;
end;exports GetObj; // 定义输出函数
end.

 

下面,我们创建一个EXE工程,并且添加类的声明:

typeTNewClass = class(TObject)publicprocedure SayHello; virtual; abstract;{ 注意这里的声明方式和DLL中的不同,这里必须声明为virtual方法,还有由于此方法通过晚绑定用的是DLL中的实现,因此EXE中可不写其实现而声明成abstract方法。 }end;function GetObj: TNewClass; stdcall; external 'dll.dll';

 

之后,我们可以添加一段测试代码来测试我们是否实现了DLL对象的共用:

varNewClass: TNewClass;
beginNewClass:= GetObj;if not Assigned(NewClass) thenExit;tryNewClass.SayHello;finallyFreeAndNil(NewClass);end;
end;

 

我们可以看到,这的确达到了EXE与DLL之间传递对象的目的。
    但是,有点麻烦:首先,在DLL工程与EXE工程都需要有被封装对象的定义。其次,virtual和abstract必须正确使用。还有,如果一旦对象发生变化,那么两边的定义都需要修改,这样难免会出点小错。

    其实,我们可以使用接口来进行对象的传递,上面的例子我们可以稍微修改一下:

    首先,我们定义一个接口:

typeINewInterface = interface(IInterface)procedure SayHello(); // 定义我们要的方法end;

 

另外,修改TNewClass类的声名:

typeTNewClass = class(TInterfacedObject, INewInterface)publicprocedure SayHello();end;

 

实现部分无须改动。
    接着,我们修改先前的那个Library项目:

library dll;function GetObj: INewInterface; stdcall;
begin // 创建对象Result:= TNewClass.Create;
end;exports GetObj; // 定义输出函数
end.


在EXE工程中,我们直接引用接口定义的单元,并且修改输出函数的声明:

function GetObj: INewInterface; stdcall; external 'dll.dll';

 

之后,测试代码会成这样:

varNewInterface: INewInterface;
beginNewInterface:= GetObj;NewInterface.SayHello;NewInterface:= nil;
end;

 

这样做的好处是,我们可以避免在多处重复说明一个要传递的对象的声明,只要我们需要的方法的声明方式不动,我们只需要改动TNewClass的实现代码,而无需改动EXE程序中的任何代码部分。
    PS:Delphi的OpenToolsAPI接口就是这个通过接口共享对象原理的很典型的应用。(这是刘啸说的。老实说,这个用法是我在写这个笔记的时候临时想到的,因为从来没有使用过未经COM封装的interface。哪里知道竟然和OpenToolAPI一样的原理,自己YY下^_^)

    当然我们还可以使用COM来封装对象:
    首先,我们建立一个名为NewCom的COM模型,建立COM模型前一篇已经说过,这里不再重复。
    那么,我们的调用代码就会变成这样:

varNewCom: ITNewCom;
beginNewCom:= CoTNewCom.Create;// 当然,和我前一篇一样使用CreateComObject函数也是一样的NewCom.SayHello;NewCom:= nil;
end;

 

我们可以看到,实现代码几乎没什么改动。那么,假如我们什么时候要把SayHello的实现代码:

ShowMessage('Hello');

 

改成:

MessageBox(0, 'Hello', 'SayHello', MB_OK);

 

    那么,我们只需要更新这个COM文件,调用它的EXE程序无须改动,这就是接口的优点。

转载于:https://www.cnblogs.com/keyvip/archive/2010/11/06/1870632.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/378885.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java 方法 示例_Java扫描器具有示例的NextNextShort()方法

java 方法 示例扫描器类的hasNextShort()方法 (Scanner Class hasNextShort() method) Syntax: 句法: public boolean hasNextShort();public boolean hasNextShort(int rad);hasNextShort() method is available in java.util package. hasNextShort()方法在java.…

mysql创建表时出现1071_mysql 出现1071错误怎么办

mysql 出现1071错误的解决办法:该问题是由键值字段长度过长导致。mysql支持数据库表单一键值的最大长度不能超过767字节,超出这个长度即报错(见标题名称)。一般情况下,不会有键值字段的长度会超出该长度。但是需要注意的是,随着全…

srs rtmp从监听到接收到新连接的过程分析

:分析srs从启动开始的main入口开始。分析rtmp监听端口,然后到接收到新rtmp连接的过程分析。 目录一、流程图二、代码分析1、srs_main_server.cpp:main()2、srs_main_server.cpp:do_main()3、srs_main_server.cpp:run()4、srs_main_server.cpp:run_master…

OpenCV笔记(基于Python)

学习时,将每块注释分别去掉即为一个模块知识点 图像数字化 import numpy as np """ #二维的ndarry #两行四列全是0的uchar类型的二维数组 z np.zeros((2,4),np.uint8) print(type(z)) print(z) print("\n")#两行四列全是1的整型矩阵 o …

POJ 1222 1681 1830 3185 开关灯问题 (高斯消元 异或方程组)

POJ 1222 EXTENDED LIGHTS OUT 基本的开关灯问题.还保证唯一解. 我们把每一个灯泡当成一个状态xi,总共有30个,而且每个灯与其他灯的关系也很明显。所以我们就可以列30方程30个变元的方程组: xi 1 * xi 1 * x(i-1) 1 * x(i1) 1 * x(i-6) …

我看周马,以及3Q大战背后的社会问题

如今闹得不可开交的3Q大战已经成了一道独特的风景线,让我们在茶余饭后又增添了不少谈资。这两个中国最大的客户端软件提供商各有拥趸无数,双方铆足了劲相互吐口水、扔砖头,现在貌似到了动刀子了。周、马在媒体上也都将自己标榜为“美貌与智慧…

Java PushbackReader ready()方法与示例

PushbackReader类ready()方法 (PushbackReader Class ready() method) ready() method is available in java.io package. ready()方法在java.io包中可用。 ready() method is used to check whether this stream is ready to be read or not. ready()方法用于检查此流是否已准…

mysql数据库知识点梳理_MySQL数据库知识点整理 (持续更新中)

一、修改用户密码格式(在命令行下输入):mysqladmin -u 用户名 -p旧密码 password 新密码1. 给root添加密码ab12: mysqladmin -uroot -password ab122. 将root的密码修改为djg345: mysqladmin -uroot -pab12 password djg345二、添加新用户格式:grant…

加载一张照片,可选择是否另存为

加载一张照片,按下S键保存,ESC退出 加载一个灰度图(E:\Python-workspace\yanyu.png),显示图片按下’s’键保存(beyond.png)(保存后的路径和该程序所在路径一致)后退出,或者按下 ESC 键退出不保存 import cv2img cv2.imread(E:\…

RTMP代理的协议规范(RtmpProxy)

RtmpProxy 关于RTMP代理的协议规范。RTMP是字节协议,第一个包是c0,1个字节,一般是03表示是明文的RTMP。所以如果需要做RTMP代理,如果直接转发RTMP客户端的消息,是没法传递额外的信息的,譬如HTTP代理在Head…

经典地址收集

http://kuler.adobe.com/ 配色网站转载于:https://www.cnblogs.com/Wolves/archive/2010/11/08/1871914.html

Java Math类toDegrees()方法与示例

数学类toDegrees()方法 (Math class toDegrees() method) toDegrees() method is available in java.lang package. toDegrees()方法在java.lang包中可用。 toDegrees() method is used to convert an angle from radians to degrees. toDegrees()方法用于将角度从弧度转换为度…

谈谈Hybird3D中的光栅化优化

看到空明流转分享了他的SALVIA 0.5.2优化谈,我也来说说Hybird3D中和光栅化相关的一些优化技术。 Hybird3D的设计目标是打造一款准实时的软件高质量渲染器,采用了光栅化和光线跟踪混合算法,光栅化用于渲染eye ray,光线跟踪则用于阴…

RTP协议基本分析(RTSP、WebRTC使用)

目录1、介绍2、RTP3、格式4、RTP打包H2644.1、H264打包方式之Single NAL Unit4.2、H264打包方式之FU-A4.2.1、FU indication4.2.2、FU header4.2.3、第一个IDR帧的NALU第一个切片4.2.4、第一个IDR帧的NALU第二个切片4.2.5、第一个IDR帧的NALU最后一个切片5、RTP打包AAC5.1、AU-…

对照片进行边缘化处理,并将边缘化处理后的结果保存

对照片进行边缘化处理,并将边缘化处理后的结果保存 import cv2 from matplotlib import pyplot as plt img cv2.imread(E:\Python-workspace\OpenCV\OpenCV/water1.png,1)#第一个参数为选择照片的路径,注意照片路径最后一个为正斜杠其他都为反斜杠&…

小皇帝,篮球,热火

失败,又一次,完全预料之中. 热火的防守早已是千疮百孔,热火的攻击也是乱无头绪. 现在的热火,需要詹姆斯无球的跑动,需要韦德的助攻。 转载于:https://www.cnblogs.com/JeffChen/archive/2010/11/12/2600335.html

fastjson转换时有大括号或者冒号或者有中括号_[Python Basic] 字符串处理以及类型转换 1...

String Manipulation & Typecasting (1)1. 文本复制以及连接1.1 Multiply sign使用 multiply sigh/乘号* 来复制文本片段。乘号复制文本举例: print("Hi" * 3) # output: HiHiHi print("*" * 10)# output:**********1.2 连接1.2.1 使用 plu…

Java IdentityHashMap size()方法与示例

IdentityHashMap类的size()方法 (IdentityHashMap Class size() method) size() method is available in java.util package. size()方法在java.util包中可用。 size() method is used to return the size (i.e. How many key-value pair exists) of this IdentityHashMap. siz…

读《深入分析Java Web技术内幕》

这里这本书的预读章节,看完预读部分,解答了一些疑惑,也相信这是一本夯实Java Web架构体系的好书。 HTTP协议解析 开发一般使用firefox的firebug调试,这的确是一个利器,HTTP的请求头响应头一目了然。 浏览器缓存机制 当…

windows mobile多国语言实现[转]

介绍一种多国语言的实现办法,这也是微软推荐的方式,打开windows mobile下的windows目录可以看到有很多以MUI为后缀名的文件,例如shellres.dll.0804.mui、shell.dll.0804.mui。。。。。。我们可以用eXeScope.exe或者resources hacker这样的文件…