三层中关于接口转换,业务封装

在服务器端建立一个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)
欢迎大家讨论一下,各自的封装方法??谢谢!!! 


正常的业务逻辑抽象方法,根本不需要用到接口传递,如果你只想玩技术游戏,这就给你个例子(我重来没有用过这种方法):
服务的建立:
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!!以前看到别人是这么写的,由于刚接触三层不是很长时间,看了李维的书,所以,对三层中中间层的协调对象,实体对象等在实际应用中不是十分明了,我想有好多人都会有同样的疑问!!因为没有一本书讲的特别的全面,问过几个人,都说是在实践中总结出来的!!所以,只有自己不断的摸索了,公司正在用的就是完全仿照李维护书上讲的方法做的,有的连函数名,变量名都不差!!!感觉不爽自己想仔细研究一下!!


//这是我写的一个简单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 9 do
    begin
      with cdsRootSubjects do
        for j:=0 to FieldCount -1 do
          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 9 do
      begin
        with cdsChildDetail do
          for j:=0 to FieldCount -1 do
            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 qryInsertRec do
  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 qryInsertRec do
  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 qryDetailByID do
  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 9 do
    begin
      with cdsRootDetail do
        for j:=0 to FieldCount -1 do
           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时基本采用这种方式,用户在修改业务逻辑的时候,只需要在数据库端把对应的存储过程改动一下即可,用的也是比较的爽!!

现在我考虑重新评价一下你的设计思想
原来,我一直认为,纯粹的oop技术在客户界面和客户逻辑的实现上,更有用武之地
在详细考虑你的设计思想前,我还是认为
作为工程的组织者,重要的是制订技术规范并监督技术规范的实施
不要使用过于复杂的技术
把成熟技术教条化后,水平再低的程序员也可以按部就班机械地实现
可以保证整个系统的质量 


做软件开发也做了一段时间,在原来的一个公司里产生了一种思想:公司原来做的是数据库收费系统(电力方面的,他们有的事钱,呵!!)那时刚进入公司,一切都是从零开始,系统已经上线了,是用C++ BUILDER5+MsSql2000开发的,二层结构,那时根本不懂什么是两层,三层!!!:)然后,开始对这个收费系统进行维护,这段时间可能是提高比较快的一段时间,从语法到数据库的表、存储过程等的学习过程,感觉非常的爽!!这样持续了半年左右的时间,基本上对整个系统有了一个全面的认识,其实这个系统就是几个不太懂软件的人做的,大量的代码重用,到现在看基本上就是一个“孩子”!
    过了那段时间后就开始对工作有了消及待工的情绪!因为重复的做一样的工作,没有什么新意!从那个时候到前不久我都有一个同样的想法,觉得做软件行业能把前辈们总结出来的规则,也可以说是设计思想运用的很好就相当不错了,而自己走一条新的路,基本上都是弯路(因为自己的水平确实不怎么样:)),所以什么都是一个想法,到一个公司只要根据公司的设计模式走就可以了,其它那些区体功能的实现上就更不用再意了!但是,不幸的是我到的这个公司的设计也是一踏糊涂,基本没有流程控制或是根本没有软件工程的思想,真的是很失望!!难道是天下乌鸦一样黑吗??所以现在不能指望到了别的公司以后再去适应公司了,应该是让自己迎合公司!!! 


IDispatch接口付给一个olevariant再传递到客户端调用,这个可以的我做过  
==========================================
  像你说的,服务器端定义一个函数,参数类型改为OleVariant
服务器端:
  var iTest:ITest1
  procedure TMyTest.Test(var a: OleVariant);
begin
   iTest:=TDataModule3.Create(nil);
   a:= iTest ;//到这里都OK
end;//这里报错,怀疑是接口生存期问题????
  客户端:
var a:OleVariant;
begin
   SocketConnection1.AppServer.Test(a);
报“一般性拒绝访问错误”!!跟了一下到服务器,注释!!
不知道是何原因,帮忙:)谢谢!!!:)


我们编写Com+数据服务模块的一般方法是,
新建一个ActiveX Library,
新建一个Transaction DataModule
然后,在这个工程的类型库编辑器中增加自定义的接口方法,
实现这些接口方法.
  一旦我们写了很多这类服务,会发现,我们总是重复类似的服务接口,同时也重复编写
类似的实现代码,自然会想到继承。
  首先,我们考虑到接口的继承,非常不幸,在类型库编辑器中,从接口的Parent Interface下拉列表中,除了系统预定义的标准接口可以继承外,我们似乎找不到自己的接口基类。
  另一方面,实现单元,对象继承自TMtsDataModule,单元的Initilization部分使用了对象工厂创建自动化对象,这种方式,让我们很难用传统方法使用实现继承。
  针对实现继承的需求,我们建立的服务基类,不要继承自Transactional DataModule,而是使用Transactional Object,配合一个普通的DataModule,来实现类似Transactional DataModule的功能,这样,普通DataModule的实现代码就可以被继承了。
  当然,这种方法牺牲了IAppServer预定义的As_*这类接口,事实上,我从来不在客户程序中调用这些接口。
  理解了上述基本思路后,对于熟悉Com+数据服务模块编写方法者,从以下文档可以理解我的基本设计思想:
本基类提供子母表datamodule的基础框架和接口框架
被继承的数据模块:
dMasterDetailBase.pas
被继承的接口声明:
mtsMsterDetailBase_TLB
继承后的mts实现单元代码模板:
MasterDetailImpl.pas
下面是使用这个基类来继承子母表服务的基本方法:
1、准备工作:
为了使用这个基类,做好以下准备:
regsvr32 mtsMsterDetailBase.dll -----使这个接口可以被类型库编辑器继承
2、继承的基本步骤:
2.1 建立服务工程文件:
新建一个Active Libruay
保存为 mtsXXX
在工程中加入单元:dMasterDetailBase.pas,mtsMsterDetailBase_TLB
dMasterDetailBase.TdmMasterDetail是准备被继承的datamodule
工程属性设置(最好设为默认):
  Packages:钩选Build with Run Time Package ,仅包含 vcl;rtl;vclx
  编译路径:    ..\..\发行包\server
2.2 继承数据模块(TdmMasterDetail):
file/new/other
找到本工程页,选择dmMasterDetail,按ok继承它
这时,一个datamodule被创建,把它保存为dXXX.pas
datamodule对象命名为dmXXX
这时,我们就可以修改这个datamodule中各个query的sql语句,使它访问新的模块的树据库对象
2.3继承Com接口:
为了不重复写类型库中的接口方法声明,可以继承IMasterDetail接口
继承IMasterDetail接口的步骤:
file/new/other/ActiveX/Transactional Object
CoClass Name: XXX
支持事务
把这个文件命名为XXXImpl.pas
在类型库编辑器中,点击根节点,打开Uses页
右键,Show All Type libruaries
找到mtsMasterDetailBase,打勾(如果找不到,是你没有注册mtsMsterDetailBase.dll)
右键,Show Selected
在类型库编辑器中点击IXXX节点,在Attributes页,将Parent Interface改为IMasterDetail
刷新类型库
这样,IMasterDetail接口的所有方法都被继承了
关闭类型库编辑器
2.4 实现服务接口:
下面,来实现XXXImpl.pas中的对象方法,基本上是抄MasterDetailImpl.pas中的样本代码
为了方便抄写代码,可以在IDE中打开MasterDetailImpl.pas,但不要把它加到工程中
首先,在XXXImpl.pas中的interface部分的uses中加入dMasterDetailBase
在对象 Txxx的私有部分加入以下声明:
  private
    FDM:TdmMasterDetail;  
在公有部分加入以下声明:
  public
    //声明下列方法并实现他们
    procedure Initialize; override;
    destructor Destroy;override;
实现:
procedure TXXX.Initialize中
FDM 使用你继承后的datamodule来创建,即,
  FDM:=TMasterDetailBase.Create(nil);
改为:FDM:=TdmXXX.Create(nil);
为了上面的代码能够调试通过,你还要在实现部分uses dXXX
其他实现代码照抄MasterDetailImpl.pas中的相应部分
至此,一个继承的masterdetail服务模型就出来了
2.5 细化数据模块的逻辑
   这个工作主要是在Query组件中编写各个SQL代码
为了方便调试DataModule中的细节,可以新建一个测试exe文件,加入dMasterDetailBase和dXXX.pas
在执行文件中调试数据模块就比较容易了
2.6 编译mts服务
   数据模块细化并调试成功后,简单打开mt工程,编译它就可以了
其他说明:
如果在新的服务中需要扩充接口,可以在类型库中自行扩充

来源:http://www.delphibbs.com/delphibbs/dispq.asp?lid=2956362

转载于:https://www.cnblogs.com/railgunman/archive/2010/11/26/1888376.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值