两层与三层的比较
序号 功能 两层 三层
1 可移值性。(可移值到WEB) 无 有
2 2 资源POOLING。(可以在很多用户下使用,而不浪费资源) 无 有
3 线程POOLING。(用户运行程序采用多线程) 无,单线程 有。多线程
4 数据库联接POOLING。(每次用户数据库联接) 每次都重新联接,联接慢 联接一次即以后都很快联接
5 允许大量的客户应用程序能够同时执行它们的工作 无 有
6 JIT对象激活功能(用户需要激活程序才会去做激活,真正瘦客户) 无 有
7 数据库事务处理 有 有
8 安全机制。(NT的安全管理) 无 有。只有权限才可以使用中间层。(NT安全管理)
9 安装数据库客户端(分发程序的时候) 要 不要
10 访问数据的稳定性 有 有
11 访问数据的速度 直接存取速度根据客户端的配置来决定速度 中间层存取速度根据服务器的配置来决定速度
12 程序升级 每台客户端去做升级 直接升级服务器
13 程序维护 每台客户端去做维护 维护服务器
序号 功能 两层 三层
1 可移值性。(可移值到WEB) 无 有
2 2 资源POOLING。(可以在很多用户下使用,而不浪费资源) 无 有
3 线程POOLING。(用户运行程序采用多线程) 无,单线程 有。多线程
4 数据库联接POOLING。(每次用户数据库联接) 每次都重新联接,联接慢 联接一次即以后都很快联接
5 允许大量的客户应用程序能够同时执行它们的工作 无 有
6 JIT对象激活功能(用户需要激活程序才会去做激活,真正瘦客户) 无 有
7 数据库事务处理 有 有
8 安全机制。(NT的安全管理) 无 有。只有权限才可以使用中间层。(NT安全管理)
9 安装数据库客户端(分发程序的时候) 要 不要
10 访问数据的稳定性 有 有
11 访问数据的速度 直接存取速度根据客户端的配置来决定速度 中间层存取速度根据服务器的配置来决定速度
12 程序升级 每台客户端去做升级 直接升级服务器
13 程序维护 每台客户端去做维护 维护服务器
1.1 名词与概念
1.1.1 部件
大家在知道要在DELPHI中调用WORD,要采用CreateOleObject(‘word.basic’);
其实这当中的WORD即为一个部件。部件可以为你完成很多功能。而你需要
做的事就是调用而已。或你可以把它理解为一个对象。即组件对象。
如零件管理可以做成一个部件,它有增加、删除、修改、查看零件详细资料、
查看零件库存几个功能。那我们的瘦客户只需要调用这个部件的一些方法。
即可以完成你所需要完成的功能。
为什么我们不把它做成一个通用的增加、删除、修改的部件呢?
因为这已经违背了组件对象模型的概念了,组件不是针对一个功能而去作要
做的事情。而是针对一个对象去封装这个对象,以便我们其它部件可以进行接口。
如销售的时候我们要查看零件的库存,可以使用这个对象。
1.1.2 客户
目前我们可以把一些常用的客户分为管理客户、端点客户。
1.1.2.1 管理客户
负责部件的维护管理、增加、删除、修改、更新等操作,都可以属于管理管理客户。
如部门管理模块我们可以把它叫做管理客户。因为它要做的事只是对部件部门进行
增加、删除等操作而已。
1.1.2.2 端点客户
负责部件的一部分企业逻辑功能我们可以把它叫做端点客户。
如医院管理的院长查询模块、前台收费模块都可以称为端点客户。
1.1.3 接口与类的区别
1、类的定义为
Type TclassName=Class(Tobject)
接口的定义为
Type IinterFaceName=InterFace
[ GUID ]
2、接口没有范围指示。
3、接口不能声明变量。
4、接口所有声明的函数和过程,概念上说都是Virtual的。
1.2 开发模式
从原先的通用组件开发模型要转换成为部件的组件开发型。
完成真正的企业逻辑封装。
如人事模块,它的开发模式将要从通用的增加、删除方式转换成为一个人事部件。
它要提供的功能应包括人事资料增加、删除、人事调动、人事辞职、人事资料导入等。
这样它将成为一个部件,我们可在任何模块中调用。一个医院管理的开发模式流程图:
1.3 编码方式
1.3.1 从原先的肥客户调用通用组件的方式转换为瘦客户调用功能组件的方式。如:导入导出部件提供两个过程。
Procedure ImPort; 导入
Procedure Export; 导出
那我们可以在每个模块里都加上一个导入导出功能。直接使用此部件的方法。又如:零件部件提拱两个过程。
Procedure QueryKC; 查询库存
Procedure ApplyKC; 更新库存。
我们可以在销售的时候更新库存,也可以在订货的时候查询零件库存。1.3.2 实现真正通用组件。
真正通用组件,即可以在没有任何互相关联的情况下在任何模块中调用。达成像ADO的COM组件原型。
如更新库存,我们至少需要零件的代码参数(唯一),然后找到此零件进行更新。
如多批更新,我们需要把OLEVairnt解成数组,然后对其进行更新。
3、所有部件要实现的功能的编码还是放在协调对象。
1.4 完全企业逻辑封装
1.4.1 达成真正企业逻辑封装,
必须让部件全部完成功能。客户只是一个功能调用而已。
如我需对人事资料进行增加、不会想到说是在客户端用一条INSERT INTO 语句完成。
因为我在客户端的时候还不知道是什么数据库,什么表格名称。即使我们的客户程
序让别人给偷看,他也无法知道我们内部的企业逻辑。这至少在基于WEB的开发的
安全问题已经达到。1.4.2 可以把一个大模块划分一个部件。
也可以把一个小模块划分一个部件。
如医院管理分为院长查询、收费、住院等。我们可以把医院管理做成一个部件,
也可以把收费作成一个部件。可大可小。1.4.3 3、客户端除了界面上的调整,
其它地方都不能反映企业逻辑。
如用户在输入部门名称的时候,部门名称不能相同,那我们一般的作法是:
procedure OnExit(sender:Tobject);
Begin
Select deptname from tab where deptname=txt;
If found then else
End;那必需改成这样:
procedure OnExit(sender:Tobject);
var Res:boolean;
Begin
Res:=Idept.Exec_ChkDept(DeptName);
If Res=True the else
End;这样有什么好处呢?
第一, 别人如果得到你的代码,他是不知道其中的企业逻辑;
第二, 可以成为一个部件,在其它模块中使用Exec_ChkDept方法就可以检查部门名称有没有相同的。
4、密码加密绝对采用组件加密。不能在客户端进行加密解密工作。1.5 组件优化
1.5.1 线程
全部线程模式采用Apartment。
1.5.2 事务
全部采用ADOconnection的BeginTransaction、EndTransaction的事务管理。
因为到目前为止未能使用组件的事务管理。
1.5.3 存储过程
基于大量的复杂计算全部放在存储过程中实现。
1.6 组件对象模型开发
1.6.1 从传统的模块化开发转换成堆积木的形式开发。
传统的开发:
人事管理组件提供:
增加、删除、查询人事资料功能。
账务管理组件提拱:增加人事、其它功能。
那么人事管理组件编写增加人事功能。
账务管理也编写增加人事功能,不仅编码重复,生产效率低,更没有组件模型的意义存在了。1.6.2 组件=部件=对象
组件模型开发是基于一个对象而去封装它,延伸它。它是针对对象的,不是针对那一个模块。
这个我们必需理解。1.6.3 团队开发模式合作方法1》 如账务管理要提拱增加人事功能,那我们必需同人事管理开发的人员有协调工作。
2》 每个程序员所负责的模块必需编写组件功能说明文档并提交。
1.1说明 1
1.2 优点 1
1.3 MTS/COM+运作流程 1
2 接口 1
2.1接口类别 3
2.1.1原生COM接口 3
2.1.2 AUTOMATION接口 3
2.1.3 DUAL接口 3
3 GUID 1
3.1 CLSID 2
3.2 IID 3
3.3 APPID 3
3.4 CATID 3
3.5 LIBID 3
4 MTS/COM+数据模块建立与编写功能函数……………………………………...8
5 MTS/COM+ OBJECT建立与编写功能函数 5
6 客户界面调用方法 3
7 公用程序 12
1 MTS/COM+相关介绍
1.1 说明
MTS(Microsoft Transaction Server)是微软在WINDOW平台的中介软件之一,它的主要功能是让WINDOWS程序员能够开发以组件为导向的分布式应用系统。MTS本身就是COM/DCOM组件。最初NT4。0是MTS2。0,到现在的WINDOWS2000 COM+技术,即为MTS3。0,WIN2000的核心技术就是COM+技术。
MTS/COM+的基类:COCLASS。
MTS/COM+的基本接口:IUNKNOWN。(或者很多程序员说为什么要用I开口,INTERFACE即是接口的单词);任何接口可以从IUNKNOWN接口衍生下来、继承下来。
1.2 优点
MTS/COM+具体以下优点:
1、 可移值性。(可移值到WEB)
2、 资源POOLING。(可以在很多用户下使用,而不浪费资源)
3、 线程POOLING。
4、 数据库联接POOLING。
5、 允许大量的客户应用程序能够同时执行它们的工作。
6、 JIT对象激活功能。
7、 数据库事务处理。
8、 安全机制。1.3 MTS/COM+运作流程当客户应用程序需要使用一个MTS对象提供的服务时,首先必须先建立些MTS对象,然后再调用它提供的方法。但是,当MTS建立MTS对象时,并不会先真正的建立,而是先建立MTS对象的CONTEXT WRAPPER对象。然后再把这个CONTEXT WRAPPER对象回传给客户。所以MTS/COM+具体JIT功能。只有客户真正调用MTS的方法时才会建立。
名词:
CONTEXT WRAPPER:MTS对象的影像。
JIT:Just in time.Activation及时激活。
(其实SUN公司的Enterprise Javabean也使用了这种类似的概念,即客户应用程序是通过中介的一个对象和真正的Enterprise Javabean互动。)
用下面的大致图形可以看出MTS的运作,详细请参考书籍。2 接口
2.1 接口类别
分为原生COM接口、AUTOMATION接口、DUAL接口。
2.1.1 原生COM接口
以VTABLE实现技术为主,提供C++,PASCAL等具备VTABLE能力的程序语言使用。但不能在VBSCRIPT和JAVASCRIPT等脚本中使用。
2.1.2 AUTOMATION接口
Automation接口使用动态的方式调用COM/DCOM组件,这程允许C++,PASCAL,脚本语言调用。不过执行速度缓慢。
2.1.3 DUAL接口
DUAL接口是融合了前两种接口的优点为一体的接口。
MTS使用的就是DUAL接口!
3 GUID
微软公司使用来标识COM/DCOM组件的ID。
3.1 CLSID
CLASSID:代表COM/DCOM对象的类别。
3.2 IID
Iterface ID:代表一个COM/DCOM对象的接口。
3.3 APPID
APPLICATION ID:应用程序ID。
3.4 CATID
代表COM/DCOM组件实现的组件类型。
3.5 LIBID
COM/DCOM实现的TYPE LIBRARY代表的ID。4 MTS/COM+数据模块建立与编写功能函数
4.1.1 MTS/COM+数据模块的建立
打开DELPHI,新建一个ActiveX library.
新建一个MTS DATA module.输入COCLASS名称,选择一个事务方式即完成。(这里的事务方式最好采用第三种)。
4.1.2 编写函数或方法:
选择VIEW菜单,选TYPE library.点新建Method即可。如果想函数输出结果,必须用*指针变量输出。
如:
param name Type modifer
param1 long * [out,retval]
至于是方法程序就不用*了。
1、 输出数据。
放上ADOCONNECTION、ADOSTOREPROC,ADOTABLE,ADOQUERY,DATASETPROVIDER绑定数据源即可输出数据。5 MTS/COM+ OBJECT建立与编写功能函数
5.1.1 MTS/COM+ OBJECT建立
打开DELPHI,新建一个ActiveX library.
新建一个MTS OBJECT .输入COCLASS名称,选择一个事务方式即完成MTS OBJECT的建立。再新建DATAMODULE,放DCOMCONNECTION和CLIENTDATASET联接远程数据模块。
5.1.2 编写函数或方法同上。
5.1.3 利用方法程序输出数据。
采用OLEVARIANT输出。
如:
function Ttestx.getdata: OleVariant;
begin
try
fdata.clientdataset1.open;
result:=fdata.clientdataset1.data;
setcomplete;
EXCEPT
Setabort;
end
end;
5.1.4 更新数据到远程数据库。
把要更新的数据用OLEVARIANT形式送到远程数据库更新即可。
如:
procedure Ttestx.apply(vdata: OleVariant);
begin
TRY
fdata.dcomconnection1.appserver.updatedates(vdata);调用了数据模块的更新方法。setcomplete;
EXCEPT
Setabort;
End;
end;
5.1.5 要注意的地方:
必须在代码里加上:
TRY
。
SETCOMPLETE;
EXCEPT
SETABORT;
End;6 客户界面调用方法
首先要定义组件接口,然后建立组件接口。再调用接口方法即完成客户界面调用。
6.1 定义接口:
VAR 变量名:I接口名
6.2 建立接口:
变量名:=CO接口名.Create or Createremote(‘machine’);
6.3 调用接口方法
变量名.方法。
如:
VAR V_atest:Itest;
Begin
V_atest:=COtest.create;//如在客户运行,需用Createremote(‘机器名’);
机器名可在INI配置文件里取值。
Cliendataset1.data:=V_atest.getdata;
End;
上面是取数据的方法程序。如果要更新到远程服务器。就采用:
V_atest.apply(Clientdataset1.data);
即完成更新操作。7 公用程序:
7.1 自定义查询数据保存到DBF。
Procedure savequerytodbf(query:tdataset;dbfname:string);
Var
Batchmove:tbatchmove;
Table:ttable;
Begin
Table:=ttable.create(application);
Table.tablename:=dbfname;
Table.tabletype:=ttdbase;
Batchmove:=tbatchmove.create(application);
Batchmove.source:=query;
Batchmove.destinaiction:=table;
Batchmove.mode:=batcopy;
Try
Batchmove.execute;
Finally
Batchmove.free;
Table.free;
End;
End;
7.2 自定义查询数据保存到txt
Procedure savequerytodbf(query:tdataset;dbfname:string);
Var
Batchmove:tbatchmove;
Table:ttable;
Begin
Table:=ttable.create(application);
Table.tablename:=dbfname;
Table.tabletype:= ttASCII;
Batchmove:=tbatchmove.create(application);
Batchmove.source:=query;
Batchmove.destinaiction:=table;
Batchmove.mode:=batcopy;
Try
Batchmove.execute;
Finally
Batchmove.free;
Table.free;
End;
End;7.3 自定义查询数据保存到paradox数据。
Procedure savequerytodbf(query:tdataset;dbfname:string);
Var
Batchmove:tbatchmove;
Table:ttable;
Begin
Table:=ttable.create(application);
Table.tablename:=dbfname;
Table.tabletype:= ttParadox;
Batchmove:=tbatchmove.create(application);
Batchmove.source:=query;
Batchmove.destinaiction:=table;
Batchmove.mode:=batcopy;
Try
Batchmove.execute;
Finally
Batchmove.free;
Table.free;
End;
End;7.4 自定义关闭FORM;
function closef:boolean;
var
msg,title:string;
begin
msg:='你确定要退出吗?';
title:='退出';
if application.messagebox(pchar(msg),pchar(title),mb_yesno)=id_yes then begin
result:=true;
end
else
result:=false;
end;procedure TForm1.button1onclick(Sender: TObject);
begin
if closef then close;
end;7.5 CHAR转换为STRING;
FUNCTION buf2txt(buf:array of char;len:integer):string;
Var I:integer;
Begin
Result:=’’;
For I=0 to len do begin
Result:=result+buf[I];
End;
End;
7.6 CHAR转换为HEX;
Function buf2hex(buf:array of char;len:integer);string;
Var I:integer;
Str:string;
Begin
For I=0 to len do begin
Str:=inttohex(ord(buf[I]),2);
Result:=result+str;
End;
End;
7.7 ASCII转换为HEX
Function Asctohex(p_source:string):string;
VAR
L_const,l_buf:string;
L_I,l_len:integer;
L_asc:byte;
BEGIN
L_const:=’0123456789abcdef’;
Result:=’’;
l_len:=length(p_source);
for l_I:=1 to l_len do begin
l_asc:=ord(p_source[l_I]);
result:=result_+L_const[(l_asc div 16)+1]+L_const[(l_asc mod 16)+1]+’’;
end;
end;
7.8 字符空格函数
function space(p_len:word):string;
begin
result:=stringofchar(‘ ’,p_len);
end;7.9 字符重复COPY函数
function replicate(p_char:char;p_count:word):string;
begin
result:=stringofchar(p_char,p_count);
end;7.10 字符SUBSTR函数
function substr(p_str:string;p_index,p_count:word):string;
begin
result:=copy(p_str,p_index,p_count);
end;
7.11 combox或Dbcombox的Item自定义函数
function comb_item(dbname,tabname,grpfield:string):tstrings;
var
query:tquery;
v_str:tstrings;
begin
v_str:=tstringlist.create;v_str.clear;
query:=tquery.create(application);
query.DatabaseName:=dbname;
query.close;query.sql.clear;
query.sql.text:='select '+grpfield+' from '+tabname+' group by '+grpfield;
query.open;
while not query.eof do begin
v_str.add(query[pchar(grpfield)]);
query.next;
end;
result:=v_str;
query.free;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
combobox1.items.Assign(comb_item('DBDEMOS','clients.dbf','city'));
end;
楼上好像说不是我自已写的,
是不?