zoukankan      html  css  js  c++  java
  • 三层中关于接口转换,业务封装

    在服务器端建立一个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

  • 相关阅读:
    关于celery django djangocelery搭配报错问题及解决方法
    django 1048错误原因及解决思路
    CSS ::Selection
    Win7编程:在按钮中加入管理员权限运行盾牌图标转载
    VisualStudioVS2010统计代码行数
    在套用母版页的页面中应用input file上传图片
    Asp.Net Url 传值出现乱码的解决方法(包括js传值)
    JS验证码刷新无反应原因
    AspnetPager
    fckeditor2.6在IE9下的弹出窗口报错问题解决
  • 原文地址:https://www.cnblogs.com/railgunman/p/1888376.html
Copyright © 2011-2022 走看看