三层中关于接口转换,业务封装 ( 积分: 100 )

  • 主题发起人 主题发起人 martinwang
  • 开始时间 开始时间
M

martinwang

Unregistered / Unconfirmed
GUEST, unregistred user!
在服务器端建立一个remoteDataModule,MyTest,定义一个方法Testing,客户端可以通过
DcomConnection1.Appserver.Testing调用此远程方法!在服务器端的type library里手工建立一个接口,ITest1,然后,建立一数据模块,
TDataModule3 = class(TDataModule,ITest1)
进行业务的封装!在IMytest下面再定义一个方法,
procedure TMyTest.Test(var Param1: ITest1);
begin
TTest:=TDataModule3.Create(nil);
Param1:=TTest;
end;
此时,客户端可以进行调用
var a:ITest1;
begin
// a:=nil;
SocketConnection1.AppServer.Test(a);
这样就得到了ITest1了,接着客户就可以调用此接口中的方法了!
但是,调用的时候会出现unsupported variant type:4009!不知道是何原因?
这样的调用方法是否正确?如果用户需要新增加业务的话,需要增加接口,并在Appserver里增加调用接口方法,这样还是比较麻烦,有什么好的方法吗?如果把业务封装进DLL,就是把接口的输出放在DLL的输出函数里面有什么好处?
========================================
如果在type library里声明一接口ITest1,而不被前面定义的MyTest实现,那么客户端是否能够调用此接口??(ITest1(IDispatch(SocketConnection1.AppServer)).Testing(a)这样的形式,应该不可以吧,iTest1的parent interface是IDispatch)
欢迎大家讨论一下,各自的封装方法??谢谢!!!
 
在服务器端建立一个remoteDataModule,MyTest,定义一个方法Testing,客户端可以通过
DcomConnection1.Appserver.Testing调用此远程方法!在服务器端的type library里手工建立一个接口,ITest1,然后,建立一数据模块,
TDataModule3 = class(TDataModule,ITest1)
进行业务的封装!在IMytest下面再定义一个方法,
procedure TMyTest.Test(var Param1: ITest1);
begin
TTest:=TDataModule3.Create(nil);
Param1:=TTest;
end;
此时,客户端可以进行调用
var a:ITest1;
begin
// a:=nil;
SocketConnection1.AppServer.Test(a);
这样就得到了ITest1了,接着客户就可以调用此接口中的方法了!
但是,调用的时候会出现unsupported variant type:4009!不知道是何原因?
这样的调用方法是否正确?如果用户需要新增加业务的话,需要增加接口,并在Appserver里增加调用接口方法,这样还是比较麻烦,有什么好的方法吗?如果把业务封装进DLL,就是把接口的输出放在DLL的输出函数里面有什么好处?
========================================
如果在type library里声明一接口ITest1,而不被前面定义的MyTest实现,那么客户端是否能够调用此接口??(ITest1(IDispatch(SocketConnection1.AppServer)).Testing(a)这样的形式,应该不可以吧,iTest1的parent interface是IDispatch)
欢迎大家讨论一下,各自的封装方法??谢谢!!!
 
来人啊:)
 
大家都忙啥去了??
 
全错
remoteDataModule已经帮你建立好接口
自己添加接口干什么?
方法参数中传递接口,到你非常理解这种工作方式后再来
看你现在水平还不是时候
 
谢谢楼上的!!!
remoteDataModule能够添加接口,但是type library能提供手工添加接口的功能,我添加接口以后,用一个datamodule来封装一个业务规则,对客户端来说只能看到一个接口,就是用remotedatamodule建立的!!!

方法参数中传递接口,到你非常理解这种工作方式后再来
看你现在水平还不是时候
===============================================
不知道需要满足什么条件才可以进行呢??
 
正常的业务逻辑抽象方法,根本不需要用到接口传递,如果你只想玩技术游戏,这就给你个例子(我重来没有用过这种方法):
服务的建立:
new Activex library
new AutoMation Object
coclass name 为IntfTest
保存工程(project1.dpr,unit1.pas)
打开 tlb编辑器
新增接口 IInterface1
新增 CoClass ,改名为 Interface1
在 Interface1 的 Implements 页,右键 Insert Interface ,把 IInterface1插入
在接口 IInterface1下增加一个方法
ChildMethed ,返回一个widestring 的值
即:HRESULT _stdcall ChildMethed ([out, retval] BSTR * retval );
在IIntfTest 下面增加一个方法 ,名为 Method1
返回 IntfTest** 参数名为Intfparam
即:HRESULT _stdcall Method1([out] IInterface1 ** Intfparam /*Warning: unable to validate structure name: */ );
回到unit1.pas
定义接口对象:
TIntfObj=class( TAutoObject,IInterface1)
function ChildMethed: WideString;
safecall;
end;
实现ChildMethed方法:
function TIntfObj.ChildMethed: WideString;
safecall;
begin
result:='You Get it';
end;

实现主接口方法:
procedure TIntfTest.Method1(out Intfparam: IInterface1);
begin
Intfparam:= TIntfObj.create //返回刚才自定义的接口
end;

在initilization 段添加对象工厂实现:
TAutoObjectFactory.Create(ComServer, TIntfTest, Class_IntfTest,
ciMultiInstance, tmApartment);
上面是生成的,下面为自己添加的:
TAutoObjectFactory.Create(ComServer, TIntfObj, CLASS_Interface1,
ciMultiInstance, tmApartment);
编译并注册工程,保存退出
新建一个客户程序,包含服务单元Project1_TLB
保存为 project2.dpr, unit2.pas
Unit2.pas use 单元Project1_TLB
procedure TForm1.Button1Click(Sender: TObject);
var server:IIntfTest ;
intf:IInterface1;
begin
server:=CoIntfTest.Create as IIntfTest ;
server.Method1(intf);
showmessage(intf.ChildMethed);
end;

这就是你要的?
给分
不过,我实在看不出这有什么意义


 
呵,谢谢张兄的代码!!如果用Midas怎么进行接口传递呢??搜过以前的贴子,没有找到十分明确的答案!!!

这倒不是很重要,问题的关键是怎么把业务封装在中间层里,而客户端根本不用写SQL!!以前看到别人是这么写的,由于刚接触三层不是很长时间,看了李维的书,所以,对三层中中间层的协调对象,实体对象等在实际应用中不是十分明了,我想有好多人都会有同样的疑问!!因为没有一本书讲的特别的全面,问过几个人,都说是在实践中总结出来的!!所以,只有自己不断的摸索了,公司正在用的就是完全仿照李维护书上讲的方法做的,有的连函数名,变量名都不差!!!感觉不爽自己想仔细研究一下!!还是谢谢张兄的回复,谢谢!!!能留下你的QQ或e-Mail吗?
 
你的类型库在客户端注册一下看看
 
to 迷糊:
是说我的那个吗?我的是在一台机器上运行的!!!有什么好的建议不防说出来,知道你是高手,呵:)
 
IDispatch接口付给一个olevariant再传递到客户端调用,这个可以的我做过
 
谢谢迷糊!哦,今天试不了了,明天试试看,业务的封装方面你是如果做的呢??
 
//这是我写的一个简单bbs服务的代码
//用于教学的,基于com+/webservice/client,下面是com+的核心,所谓的业务逻辑封装,就是这些。如果感兴趣,可以给全套代码
抽象了几个主要方法:
//获得帖子内容
procedure GetDetail(id: Integer;
out vDetail: OleVariant);
safecall;
//获得主帖标题
procedure GetRootSubjects(PageNo: Integer;
out vData: OleVariant;
out EndofFile: WordBool);
safecall;
//获得某个主帖的所有子帖
procedure GetChildDetail(pId: Integer;
out vChildDetail: OleVariant;
PageNo: Integer;
out EndofFile: WordBool);
safecall;
//跟贴
procedure AddChild(pid: Integer;
const Subject, Content,
Submiter: WideString);
safecall;
//加主帖
procedure AddRoot(const Subject, Content, Submiter: WideString);
safecall;
procedure GetRootDetail(var vRootDetail: OleVariant;
PageNo: Integer;
out EndofFile: WordBool);
safecall;
unit BBSImpl;
{$WARN SYMBOL_PLATFORM OFF}
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ComServ, ComObj, VCLCom, StdVcl, bdemts, DataBkr, DBClient,
MtsRdm, Mtx, PMtsBBS_TLB, Provider, DB, DBTables;
type
TBBS = class(TMtsDataModule, IBBS)
qryDetailById: TQuery;
Session1: TSession;
Database1: TDatabase;
dspDetailByID: TDataSetProvider;
qryRootSubjects: TQuery;
dspRootSubjects: TDataSetProvider;
cdsRootSubjects: TClientDataSet;
qryChildDetail: TQuery;
dspChildDetail: TDataSetProvider;
cdsChildDetail: TClientDataSet;
qryInsertRec: TQuery;
qryUpdateFollowTime: TQuery;
qryRootDetail: TQuery;
dspRootDetail: TDataSetProvider;
cdsRootDetail: TClientDataSet;
private
{ Private declarations }
procedure UpdateFollowTime(pId:integer;dTime:TDateTime);
protected
class procedure UpdateRegistry(Register: Boolean;
const ClassID, ProgID: string);
override;
procedure GetDetail(id: Integer;
out vDetail: OleVariant);
safecall;
procedure GetRootSubjects(PageNo: Integer;
out vData: OleVariant;
out EndofFile: WordBool);
safecall;
procedure GetChildDetail(pId: Integer;
out vChildDetail: OleVariant;
PageNo: Integer;
out EndofFile: WordBool);
safecall;
procedure AddChild(pid: Integer;
const Subject, Content,
Submiter: WideString);
safecall;
procedure AddRoot(const Subject, Content, Submiter: WideString);
safecall;
procedure GetRootDetail(var vRootDetail: OleVariant;
PageNo: Integer;
out EndofFile: WordBool);
safecall;
public
{ Public declarations }
end;

var
BBS: TBBS;
implementation
{$R *.DFM}
uses Math;
class procedure TBBS.UpdateRegistry(Register: Boolean;
const ClassID, ProgID: string);
begin
if Register then
begin
inherited UpdateRegistry(Register, ClassID, ProgID);
EnableSocketTransport(ClassID);
EnableWebTransport(ClassID);
end else
begin
DisableSocketTransport(ClassID);
DisableWebTransport(ClassID);
inherited UpdateRegistry(Register, ClassID, ProgID);
end;
end;

procedure TBBS.GetDetail(id: Integer;
out vDetail: OleVariant);
begin
// qryDetailByID.ParamByName('id').AsInteger:=id;
// qryDetailByID.Open;
qryDetailByID.Params[0].AsInteger:=id;
qryDetailByID.Open;
vDetail:=dspDetailByID.Data ;
qryDetailByID.Close;
end;

procedure TBBS.GetRootSubjects(PageNo: Integer;
out vData: OleVariant;
out EndofFile: WordBool);
var FirstRec,i,j:integer;
FieldsArray:Array[0..3] of variant;
begin
EndofFile:=false;
cdsRootSubjects.Open;
FirstRec:= max(0,(PageNo-1)*10);
cdsRootSubjects.MoveBy(FirstRec);
with TClientDataSet.Create(nil)do
try
FieldDefs:=cdsRootSubjects.FieldDefs;
CreateDataSet;
for i:=0 to 9do
begin
with cdsRootSubjectsdo
for j:=0 to FieldCount -1do
FieldsArray[j]:=Fields[j].value;
InsertRecord([FieldsArray[0],FieldsArray[1],FieldsArray[2],FieldsArray[3]]);
cdsRootSubjects.Next;
if cdsRootSubjects.Eof then
begin
EndofFile:=true;
Break;
end;
end;
vData:=Data;
finally
Free;
cdsRootSubjects.Close;
end;
end;

procedure TBBS.GetChildDetail(pId: Integer;
out vChildDetail: OleVariant;
PageNo: Integer;
out EndofFile: WordBool);
var FirstRec,i,j:integer;
FieldsArray:array[0..6] of variant;
begin
EndofFile:=false;
qryChildDetail.Params[0].AsInteger:=pId;
cdsChildDetail.Open;
FirstRec:= max(0,(PageNo-1)*10);
cdsChildDetail.MoveBy(FirstRec);
with TClientDataSet.Create(nil)do
try
FieldDefs:=cdsChildDetail.FieldDefs;
CreateDataSet;
if cdsChildDetail.Eof then
EndofFile:=true
else
begin
for i:=0 to 9do
begin
with cdsChildDetaildo
for j:=0 to FieldCount -1do
FieldsArray[j]:=Fields[j].value;
InsertRecord([FieldsArray[0],FieldsArray[1],FieldsArray[2],FieldsArray[3],FieldsArray[4],FieldsArray[5],FieldsArray[6]]);
cdsChildDetail.Next;
if cdsChildDetail.Eof then
begin
EndofFile:=true;
Break;
end;
end;
end;
vChildDetail:=Data;
finally
Free;
cdsChildDetail.Close;
end;
end;

procedure TBBS.AddChild(pid: Integer;
const Subject, Content,
Submiter: WideString);
var dTime:TDateTime;
begin
{pid,subject,content,submiter,SubmitTime}
dTime:=now;
with qryInsertRecdo
begin
params[0].AsInteger:=pid;
params[1].AsString:=Subject;
params[2].AsString:=Content;
params[3].AsString:=Submiter;
params[4].AsDateTime:=dTime;
params[5].AsDateTime:=dTime;
ExecSql;
end;
UpdateFollowTime(pid,dTime);
end;

procedure TBBS.AddRoot(const Subject, Content, Submiter: WideString);
var dTime:TDateTime;
begin
{pid,subject,content,submiter,SubmitTime}
dTime:=now;
with qryInsertRecdo
begin
params[0].AsInteger:=0;
params[1].AsString:=Subject;
params[2].AsString:=Content;
params[3].AsString:=Submiter;
params[4].AsDateTime:=dTime;
params[5].AsDateTime:=dTime;
ExecSql;
end;
end;

procedure TBBS.UpdateFollowTime(pId:integer;dTime: TDateTime);
begin
qryUpdateFollowTime.ParamByName('id').asInteger:=pId;
qryUpdateFollowTime.ParamByName('LastFollowTime').asDateTime:=dTime;
qryUpdateFollowTime.ExecSQL;
if pId<=0 then
Exit;
with qryDetailByIDdo
begin
Close;
Params[0].AsInteger:=pId;
Open;
if not qryDetailByID.eof then
if (fieldByName('Pid').AsInteger>0) and (fieldByName('Pid').AsInteger<>fieldByName('id').AsInteger) then
UpdateFollowTime(fieldByName('Pid').AsInteger,dTime);
Close;
end;
end;

procedure TBBS.GetRootDetail(var vRootDetail: OleVariant;
PageNo: Integer;
out EndofFile: WordBool);
var FirstRec,i,j:integer;
FieldsArray:array[0..6] of variant;
begin
EndofFile:=false;
cdsRootDetail.Open;
FirstRec:= max(0,(PageNo-1)*10);
cdsRootDetail.MoveBy(FirstRec);
with TClientDataSet.Create(nil)do
try
FieldDefs:=cdsRootDetail.FieldDefs;
CreateDataSet;
for i:=0 to 9do
begin
with cdsRootDetaildo
for j:=0 to FieldCount -1do
FieldsArray[j]:=Fields[j].value;
InsertRecord([FieldsArray[0],FieldsArray[1],FieldsArray[2],FieldsArray[3],FieldsArray[4],FieldsArray[5],FieldsArray[6]]);
cdsRootDetail.Next;
if cdsRootDetail.Eof then
begin
EndofFile:=true;
Break;
end;
end;
vRootDetail:=Data;
finally
Free;
cdsRootDetail.Close;
end;
end;

initialization
TComponentFactory.Create(ComServer, TBBS,
Class_BBS, ciMultiInstance, tmApartment);
end.
 
十分谢谢张兄!!!
IBBS里面定义一些与论坛相关的一些方面,然后Client通过WebConnection进行调用iBBS的这些方面!!!!但是如果在比较大的系统里,比如ERP,会有很多业务逻辑,增加一个业务逻辑,需要在接口处增加方法,供CLIENT调用!!如何通过OO思想对业务进行抽象,抽象出比较通用的基类,比如数据的一些增、删、改、查可以定义一个基类或是一个接口,通过继承或实现完成其子类!!!这方面确实没有多少的经验,请有经验的朋友来传道受业解惑,3Q!!!!!:)
 
我比较倾向于在数据库层封装业务逻辑,每个业务典型地,是一个存储过程
对于Oracle,可以用包来对存储过程进行逻辑分类,以适应面向对象设计方法
对于MsSql,可以使用命名前缀区分不同类的逻辑对象。
每种数据库特性不同。应该充分使用数据库提供的优良性能,在客户或中间层
任以传递sql语句,除了逻辑不清的问题外,对于性能调优非常不利。
一般来说,对于大型应用,作数据库平台无关的应用是不切实际的
好了,因为逻辑都在数据库,那么,中间层抽象业务逻辑的功能也
减弱了,那么,也没必要协调对象再封装一次。
一家之言,仅供参考
 
比如ERP,会有很多业务逻辑,增加一个业务逻辑,需要在接口处增加方法,供CLIENT调用!!
============
这是必然的
你看一下金蝶ERP
约40个Com+组件包,每个包近100个对象,每个对象有n多方法
这些组件及方法,基本上描述了企业逻辑
如果嫌麻烦,还是不要用这种技术
任何数据库应用,最后都可以抽象为增删改,要这样的话,也就没必要
作什么业务抽象了
 
张兄的观点:
每增加一个业务从数据库端进行业务逻辑的实现,即一个或多个存储过程的总体,客户端调用一个方法完成业务的实际!!以前做两层的时候,数据库是MSSQL时基本采用这种方式,用户在修改业务逻辑的时候,只需要在数据库端把对应的存储过程改动一下即可,用的也是比较的爽!!
 
martinwang兄,看来低估你了,多有得罪
现在我考虑重新评价一下你的设计思想
原来,我一直认为,纯粹的oop技术在客户界面和客户逻辑的实现上,更有用武之地
在详细考虑你的设计思想前,我还是认为
作为工程的组织者,重要的是制订技术规范并监督技术规范的实施
不要使用过于复杂的技术
把成熟技术教条化后,水平再低的程序员也可以按部就班机械地实现
可以保证整个系统的质量
 
to 张兄:其实我就是个新手,呵!!!
做软件开发也做了一段时间,在原来的一个公司里产生了一种思想:公司原来做的是数据库收费系统(电力方面的,他们有的事钱,呵!!)那时刚进入公司,一切都是从零开始,系统已经上线了,是用C++ BUILDER5+MsSql2000开发的,二层结构,那时根本不懂什么是两层,三层!!!:)然后,开始对这个收费系统进行维护,这段时间可能是提高比较快的一段时间,从语法到数据库的表、存储过程等的学习过程,感觉非常的爽!!这样持续了半年左右的时间,基本上对整个系统有了一个全面的认识,其实这个系统就是几个不太懂软件的人做的,大量的代码重用,到现在看基本上就是一个“孩子”!
过了那段时间后就开始对工作有了消及待工的情绪!因为重复的做一样的工作,没有什么新意!从那个时候到前不久我都有一个同样的想法,觉得做软件行业能把前辈们总结出来的规则,也可以说是设计思想运用的很好就相当不错了,而自己走一条新的路,基本上都是弯路(因为自己的水平确实不怎么样:)),所以什么都是一个想法,到一个公司只要根据公司的设计模式走就可以了,其它那些区体功能的实现上就更不用再意了!但是,不幸的是我到的这个公司的设计也是一踏糊涂,基本没有流程控制或是根本没有软件工程的思想,真的是很失望!!难道是天下乌鸦一样黑吗??所以现在不能指望到了别的公司以后再去适应公司了,应该是让自己迎合公司!!!
 
张大侠怎么不来了??:)
 
后退
顶部