我看过一篇文章(好象是超级猛料的作者kingrom写的),里面提到在Delphi中界面和代码分离的技术,看后很受启发。后来我想,如果我做一个数据维护基类,它是一个单元(Unit),代码可能如下:
  TDataManage = class
  provite
     FDataContent:TDataSet;
  protected
     procedure PostData;virtual;
  public   
     ...
  end; 
  TadoDataManage = class(TDataManage)
  provite
  pulbic
    constructor Create(SQLQuery:string);
    ...
  end;
  ...
  现在我遇到的一个问题是,如果我要在TDataManage类里的FDataContent的BeforePost执行一个函数,这个函数的功能就是检查保存时所要的数据是否完整,这个函数可以被具体的实例重新实现。所它不能声明为一个虚方法,因为具体的界面只是这个TDataManage类的实例,所以我想传界面单元定义这个函数,然后传递这个函数的方法指针到基类,再由基类去执行它,然而这个怎么去实现呢?
例如我有一个学生信息维护界面,我可能需要检查学生信息是否完整然后再保存,就在这个界面上定义这个函数去检查学生信息的完整性,同样,我也有一个教师信息维护界面也需要检查数据完整性。但对于我基类TDataManage的保存方法,我仅希望调用一个函数CheckData返回一个真和假的值进行操作,如果为真就保存否则就报告数据不完整!
不知道大家理解了我的意思没有!

解决方案 »

  1.   

    http://www.csdn.net/develop/Read_Article.asp?Id=23222
    上面的文章不完整,我找不到原文
    里面写了有关你的问题的解决方法
    ------------
    不理解你说的 数据完整
    估计是 某些字段不能为空,或者特殊规则
    不过我觉得那是 商业规则,强烈建议不要让基类去检查商业规则
      

  2.   

    那个文档不是有个出处吗?
    http://codecentral.borland.com/codecentral/ccweb.exe/home
    不过我也没仔细看,呵呵
      

  3.   

    作为一个管理类,管理不同的对象,但对象的某个过程回过来调用管理类中的方法的确不多见,但还是有方法的!你可以在管理类中定义一个方法,然后通过管理类的create方法定义:
    FDataContent.BeforePost:=PostData;
    不知道我是否正确理解你的意思!?
      

  4.   

    谢谢各位,应该说 sundytu(晶晶)的理解和我的比较接近。
    现在我想,如晶晶所说,你的PostData方法是在TDataManage定义成一个虚方法呢?还是应该定义成一个方法指针呢?
    如果定义成一个虚方法,我的界面上又不能重载这个方法,所以我偏向于定义成一个方法指针!实际的过程是:
    界面代码可能如下:
    uses
      ...,DataManage;
    Interface
    type
      Form1 = class(TForm)
      private
      public
        Function PostCheck:Boolean;//问题是我怎么把这个方法传给TDataManage?
      end;
    var
      ManaInstance:TDataManage; 
    implementationfunction TForm1.PostCheck:Boolean;
    begin
      result := true;
      if eName.text='' then
        result := false;
    end;procedure TForm1.FormCreate(Sender: TObject);
    begin
      ManaInstance := TDataManage.Create();
    //  ManaInstance.CheckPost := @PostCheck;  关键是这里,我想把Form的PostCheck方法传给TDataManage让它调用,我想用方法指针传给TDataManage类,我应该怎么做?
    end;
      

  5.   

    To:cll007
       我正是因为不愿把商业规则写在基类里面所以才烦恼,我是想把商业规则写在具体的界面里,让具体的实例去做这些商业规则,把检测的结果告诉给基类,让基类好根据检测结果而继续做其他的事啊!
      

  6.   

    再定义一个界面/窗口基类,定一个CheckData虚方法(也可以在CheckData里面调用TDataManager的相关方法如PostData等),不同的窗口重载CheckData方法即可?
    理解我的意思了没有?:)
      

  7.   

    还有一种方法,就是数据基类声明相关的Property,界面和基类通过Property来交流数据也可阿
      

  8.   

    应该可以的,你在你的管理类中使用一个方法指针(如果想在界面上可以直接调用的话),然后在管理类的方法中调用它就可以了!
    uses
      ...,DataManage;
    Interface
    type
      TMyFunc=function :boolean of object;//这个类型根据你实际需要可能要改动一下;
      TDataManage = class
      private
        FDataContent:TDataSet;
      protected
        FPostData:TMyFunc;
        //function logic:boolean;这个函数就是界面上定义的相应函数
      public
        procedure PostDataResult;//这里其实就可以定义为virtual方法的;
      published
        property PostDate:TMyFunc read FPostData write FPostData;
      end;
    var
      ManaInstance:TDataManage; 
    implementation{function TDateManege.logic:boolean;
    begin
    result:=false;
    ................
    end;}function TDataManage.PostDataResult;
    begin
      if assigned(FPostDate) then
      begin
        if  FPostDate() then//根据不同的返回值显示不同的信息
            //显示正确的对话框
        else
            //显示错误对话框
       end;
    end;procedure TDataManage.FormCreate(Sender: TObject);
    begin
      FDataContent.BeforePost:=PostDataResult;
      
    end;
    只要你将在界面上指定ManaInstance.PostDate为Form的一个同类型的方法就可以得到结果的,我在本机上测试过,没有问题的!
      

  9.   

    不赞成定义成 方法指针
    也不赞成 在界面检查 业务逻辑,它们毫不相干
    水平有限,只能写一点东东
    ----------------------
    unit Base;
    {包含对数据库对象的各种管理等,实现比较复杂,还要数据感知的支持才能完成
    具体内部包含的东东,我也还没搞清楚}
    interface
    type
      //所有 具体 业务实体的基类
      TDataBase=class(TObject)
      private
        FObjectID: string;
        procedure SetObjectID(const Value: string);
      protected
        procedure CheckNew();virtual;
        //这个ID是提交时,数据库内部自动产生,所以不需要公布
        //
        property ObjectID:string read FObjectID write SetObjectID;
      end;  //数据库的资料在内存中的映射,很有用的东东
      TDataListBase=class(TObject)
      protected  end;  //负责与数据库交互,
      TDataManageBase =class(TObject)
      protected
        procedure Save(AObject:TDataBase);virtual;
        procedure Edit(AObject:TDataBase);virtual;
        procedure Delete(AObject:TDataBase);virtual;
      end;  //组件,拥有数据感知,如同DataSouce一样
      TDataLink=class(TObject)  end;
    implementation{ TDataManageBase }procedure TDataManageBase.Save(AObject: TDataBase);
    begin
      //这个检查可以不放在这里,也可以让程序员在 界面里面写  AObject.CheckNew;
      //Post
    end;procedure TDataManageBase.Delete(AObject: TDataBase);
    beginend;procedure TDataManageBase.Edit(AObject: TDataBase);
    beginend;{ TDataBase }procedure TDataBase.CheckNew;
    beginend;procedure TDataBase.SetObjectID(const Value: string);
    begin
      FObjectID := Value;
    end;end.
    ------------------------
    例子:Customer类
    unit CustomerU;
    {Problem Domain}
    {这个里面基础部分可以自动生成,
    程序员主要集中精力实现 业务规则 部分
    与界面部分没有关系,不过其在界面部分之前产生}
    interfaceuses Base;type
      TCustomer=class(TDataBase)
      private
        FSex: integer;
        FBirthday: string;
        FName: string;
        procedure SetBirthday(const Value: string);
        procedure SetName(const Value: string);
        procedure SetSex(const Value: integer);  public
        procedure CheckNew();override;
      published
        property Name:string  read FName write SetName;
        property Sex:integer  read FSex write SetSex;
        property Birthday:string  read FBirthday write SetBirthday;
      end;  TCustomerDataManage=class(TDataManageBase)
      public
        procedure Save(AObject:TDataBase);override;
        procedure Edit(AObject:TDataBase);override;
        procedure Delete(AObject:TDataBase);override;
      end;
    implementationuses SysUtils;{ TCustomer }procedure TCustomer.CheckNew;
    begin
      inherited;
      //在此处写一部分 业务逻辑
      if (Trim(FName)='') or (FSex<=0) then
        Raise Exception.Create('数据不完整');
    end;procedure TCustomer.SetBirthday(const Value: string);
    begin
      FBirthday := Value;
    end;procedure TCustomer.SetName(const Value: string);
    begin
      FName := Value;
    end;procedure TCustomer.SetSex(const Value: integer);
    begin
      FSex := Value;
    end;{ TCustomerDataManage }procedure TCustomerDataManage.Delete(AObject: TDataBase);
    begin
      inherited;end;procedure TCustomerDataManage.Edit(AObject: TDataBase);
    begin
      inherited;end;procedure TCustomerDataManage.Save(AObject: TDataBase);
    begin
      inherited;end;end.
    --------------------
    窗口类,界于窗口基类和最终窗口之间
    unit CustomerF;
    {这里可以做一个Customer窗体类的基类,是可以自动产生的,
    最好如同Base单元一样做个总的基类,根据传入的 具体业务类,自动产生界面
    ,当然,界面中控件也可以自动,不过最终程序员只需要调整控件位置就可以了}
    {我的这个例子窗口类比较接近程序员写的那部分,最终的程序员接触的窗体里面,
    只包含单纯的几个控制类方法,窗口类不负责业务逻辑检查,最多负责其他的检查,
    所以界面类很干净}
    interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, ExtCtrls,CustomerU;type
      TCustomerEditF = class(TForm)
        lblEdtCustomerName: TLabeledEdit;
        lblEdtCustomerSex: TLabeledEdit;
        lblEdtCustomerBirthday: TLabeledEdit;
        btnCustomerSave: TButton;
        btnInsert: TButton;
        procedure btnInsertClick(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure btnCustomerSaveClick(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
      private
        //最终的窗口类是没有下面的东西的
        FCustomer:TCustomer;
        FCustomerDataManage:TCustomerDataManage ;
      public
        { Public declarations }
      end;var
      CustomerEditF: TCustomerEditF;implementation{$R *.dfm}procedure TCustomerEditF.btnInsertClick(Sender: TObject);
    begin
      //如果借助数据感知功能,下面的Button可用,也可以从界面抽离
      btnCustomerSave.Enabled:=not btnCustomerSave.Enabled;
    end;procedure TCustomerEditF.FormCreate(Sender: TObject);
    begin
      //此模拟  管理对象的创建
      FCustomerDataManage:=TCustomerDataManage.Create;  //此模拟  被管理对象的创建
      FCustomer:=TCustomer.Create;  btnCustomerSave.Enabled:=not btnCustomerSave.Enabled;
    end;procedure TCustomerEditF.btnCustomerSaveClick(Sender: TObject);
    begin
      //如果借助数据感知功能,下面的赋值,也可以抽离Customer对应的界面基类
      FCustomer.Name:=lblEdtCustomerName.Text;
      FCustomer.Sex:=strtoint(lblEdtCustomerSex.Text);
      FCustomer.Birthday:=lblEdtCustomerName.Text;  //最终Save里面只剩下一句话
      FCustomerDataManage.Save(FCustomer);  //如果借助数据感知功能,下面的Button不可用,也可以从界面抽离
      (Sender as TButton).Enabled:=not (Sender as TButton).Enabled;end;procedure TCustomerEditF.FormDestroy(Sender: TObject);
    begin
      FreeAndNil(FCustomer);
    end;end.
      

  10.   

    个人设计的思路和风格一般很难统一的你们说不赞成传递事件或方法,但是delphi却提供这些东西,何解?
    是不是对Delphi的对象模型没有理解透?很多东西,VCL已经封装好了,然后我们又不用,然后自己又弄一套
    结果还是和VCL提供的没有两样,何必?不明白,有必要弄个TDataManager或者其他的什么吗?
    TDataSet不是有BeforePost吗?何必另外再弄个PostData?
    然后子类再override或者传递一个和beforepost/beforedelete根本没两样的东西?对于cll700兄弟提的,尤其是CustomerF,把简单的问题复杂化了?看这里,
    >>
    >>  //如果借助数据感知功能,下面的赋值,也可以抽离Customer对应的界面基类
    >>  FCustomer.Name:=lblEdtCustomerName.Text;
    >>  FCustomer.Sex:=strtoint(lblEdtCustomerSex.Text);
    >>  FCustomer.Birthday:=lblEdtCustomerName.Text;我觉得放三个TDBEdit,把属性设好就完事了,但是这里的手脚就很多了
    还要
    >>  //最终Save里面只剩下一句话
    >>  FCustomerDataManage.Save(FCustomer);那么FCustomer.CheckData和TsqlCustomer.BeforePost有什么两样呢?
    我们只要在beforepost()里检查一下,然后Post()就完事了放着现成的组件不用,我觉得我们走远了
      

  11.   

    实在不理解你说的什么,举例子下次不要举这种数据库相关的了,我数据库这个TDataSet还没有用过!不知道你说什么,但是你的意思是不是说你定义的那个数据管理类中公布出来的虚方法实际上是在FDataContent.BeforePost事件中执行?如果是,那么你还是声明为虚方法,但是你在TDataManage类中就去实现FDataContent成员的BeforePost事件,在里面调用虚方法PostData,这样的设计我个人认为比你使用函数指针要好很多!
      

  12.   

    还有就是alphax(多喝了三五杯)说到的问题,我认为,如果Delphi组件本身提供了这样的功能,除非你要根据已有组件建立自己特有功能的新组件,这么做你可以按照你的思想去做,如果不是,仅仅是为了所谓的面向对象设计,那么你的路走错了,面向对象的精髓就是要把复杂问题简单化,如果你反而去把原本简单的问题复杂化,那么这个方向就不对
      

  13.   

    http://www.csdn.net/develop/Read_Article.asp?Id=23222
    上面的文章不完整,我找不到原文
    里面写了有关你的问题的解决方法
    ------------
    不理解你说的 数据完整
    估计是 某些字段不能为空,或者特殊规则
    不过我觉得那是 商业规则,强烈建议不要让基类去检查商业规则
    --------------------
    to:Linux2001(恋人不如自恋) and 
    alphax(多喝了三五杯)
    -----------
    如果二位能够看看我所说的那篇文章的完整版本(一定在Borland网站上,不过我E文太差,找不到),并且能看懂,相信一定能够理解我所说的东东
    我说的只是很小的一部分(限于水平),它其实是一套Framework(好象在Borland网站应该有下的 ),由外国人写的,我看过部分原始代码,很多不懂,我也看过有人在这个FrameWork上发展的东西,根据观察的结果看,程序的代码结构相当清楚,做到界面与业务的分离;并且由低级程序员写的代码相当少(主要精力放在业务层),大部分由数据向导(高级程序员写,长时间稳定)自动产生,限于某些原因,无法公布这些代码
    本人水平有限(接触Delphi10个月),并且我也不是程序员,我对数据库面向对象认识,也只限于以上说出的部分,说出来以期达到抛砖引玉的作用
    ------------------
    Delphi提供的的确有数据感知控件,但是却没有提供 自动功能,比如业务逻辑结构、界面界面上用到控件的,现在的效果就是 数据字典 输入后,将自动产生以上内容,程序员所做的 就是把控件的位置摆整齐、业务逻辑搞正确
      

  14.   

    谢谢各位的讨论,至于应该不应该把这些封装起来,我想我有充分的理由这样做!今天在这里我得到了晶晶和cll007(gazo) 的大力支持,我这两天就把我想做做出来,给楼上各位我认为需要发的各寄一份源码(我会以短消息的方式通知大家)。
        现在我来说说我为什么要这样做的道理:
        1。我们知道B/S结构的程序大多都是无连接数据源,即一获取数据就与数据源断开了。并且很少与数据库关联,界面就是界面,代码就是代码,很清晰的结构,而且效率与数据库感知的程序来说,我个人认为要高些。因为每一个客户端与数据库之间都要维持着一个连接会话。后来我在做Delphi三层应用时,界面上的控件基本上都不用感知了,都有一个获取数据和更新数据的操作。所以我需要封装一个数据操作类来对数据进行维护以减轻我的工作量
        2。我已经做了一个通用的基类,由它派生的子类可以不需要编写一行代码就可以实现新增删除保存,值检测及打印等操作了,用户只要布置它的界面就可以了。只是我个人认为它还有需要改进的地点。我的目标是想建立这样一个类库:
    。它能连接上不同的数据库,这样我只要创建一个这个类的实例就可以操作这个数据库了
    。以不同的驱动连接数据库。如以BDE方式,以ADO方式,以DBExpress方式等。
    好了,感谢大家参与,现在要做事了,请大家继续讨论!
      

  15.   

    好像问题发生变化了,变成了要不要对象模型,要不要framework了
    我是盲人摸象
    好,等有鬼大大的说明性源码,用代码来说明和讨论是最好的了另,cll007大大,
    你所提的文章在下面的地址中有一份副本http://www.howtodothings.com/showarticle.asp?article=157
      

  16.   

    ------------------
    |界面元素(如窗口)|<------------------|3.组件再调用窗口元素注册到组件的方法
     ------------------           --------------------
           |1.某个操作           |管理类(如你的组件)|<------
           |激活某个组件的事件    --------------------        |
           |                                                  |
           |            -------------------  2。调用管理类中  |
            ---------->|数据组件(如DataSet|------------------
                        -------------------    注册到组件的事件
      

  17.   

    yczyk(有鬼:泪眼问花花不语,乱红飞过千秋去):
    希望你也能传一份源码给我,到时一起与你们探讨。
      

  18.   

    纠正一下,猛料的作者是kingron,不是kingrom
    :)
      

  19.   

    unit UitB_SingleTable;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, UitBase, ImgList, ComCtrls, ToolWin, ExtCtrls, Grids,
      DB, ADODB, ActnList, StdCtrls, Buttons;type
      TPopedom = set of (pdQuy, pdPrt, pdAdd, pdDel, pdMod);
      TDBState = set of (dsNone, dsAdd, dsMod, dsDel);  TFrmB_SingleTable = class(TFrmBase)
        Pnl_Title: TPanel;
        ImgList: TImageList;
        Ds_Main: TDataSource;
        ActList: TActionList;
        ActQuy: TAction;
        ActPrt: TAction;
        ActAdd: TAction;
        ActDel: TAction;
        ActMod: TAction;
        Tbar_Maintenance: TToolBar;
        ToolButton6: TToolButton;
        Tbtn_Save: TToolButton;
        ToolButton2: TToolButton;
        Tbtn_Add: TToolButton;
        ToolButton4: TToolButton;
        Tbtn_Delete: TToolButton;
        ToolButton11: TToolButton;
        Tbtn_Mod: TToolButton;
        ToolButton12: TToolButton;
        Tbtn_Cancel: TToolButton;
        ToolButton10: TToolButton;
        Tbtn_Prior: TToolButton;
        ToolButton8: TToolButton;
        Tbtn_Next: TToolButton;
        ToolButton7: TToolButton;
        Tbtn_Close: TToolButton;
        Tbtn_Query: TToolButton;
        ToolButton3: TToolButton;
        ToolButton5: TToolButton;
        Tbtn_Print: TToolButton;
        Pnl_Edit: TPanel;
        Pnl_Query: TPanel;
        Sb_Query: TSpeedButton;
        Stbar_Single: TStatusBar;
        procedure Tbtn_CloseClick(Sender: TObject);
        procedure Tbtn_PriorClick(Sender: TObject);
        procedure Tbtn_NextClick(Sender: TObject);
        procedure FormShow(Sender: TObject);
        procedure Tbtn_SaveClick(Sender: TObject);
        procedure Tbtn_CancelClick(Sender: TObject);
        procedure ActQuyExecute(Sender: TObject);
        procedure ActPrtExecute(Sender: TObject);
        procedure Tbtn_QueryClick(Sender: TObject);
        procedure ActDelExecute(Sender: TObject);
        procedure ActAddExecute(Sender: TObject);
        procedure ActModExecute(Sender: TObject);
        procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
        procedure Ds_MainStateChange(Sender: TObject);
      private
        function ControlButton(): string;
        function BuildHint(): string;
        procedure ModDataSetState(ModType: TDBState);
      public
        function BuildSQL(): string; virtual; abstract;
        procedure DataPrint; virtual; abstract;
        procedure InitializeForm; virtual; abstract; //初始化权限,打开数据集    //--------------------------------------------------------------//
        procedure SetDataSetState(DataSet: TDataSet; const ReadOnly: Boolean = true);
        procedure OperDataSet(DataSet: TDataSet; SQLText: string; const OpenDataSet: Boolean = true);
        //--------------------------------------------------------------//
      end;var
      FrmB_SingleTable: TFrmB_SingleTable;
      Popedom: TPopedom;
      MainTableName: string;
      DataBaseState: TDBState;
    implementationuses Math;{$R *.dfm}procedure TFrmB_SingleTable.Tbtn_CloseClick(Sender: TObject);
    begin
      inherited;
      Close;
    end;procedure TFrmB_SingleTable.Tbtn_PriorClick(Sender: TObject);
    begin
      inherited;
      if (Ds_Main.DataSet <> nil) and (Ds_Main.DataSet.State <> dsInactive) then
        Ds_Main.DataSet.Prior;
    end;procedure TFrmB_SingleTable.Tbtn_NextClick(Sender: TObject);
    begin
      inherited;
      if (Ds_Main.DataSet <> nil) and (Ds_Main.DataSet.State <> dsInactive) then
        Ds_Main.DataSet.Next;
    end;procedure TFrmB_SingleTable.FormShow(Sender: TObject);
    begin
      inherited;
      DataBaseState := [dsNone];
      if Ds_Main.DataSet <> nil then
        (Ds_Main.DataSet as TADOQuery).LockType := ltBatchOptimistic;
      Tbtn_Save.Enabled := False;  if not Tbtn_Query.Down then
      begin
        Pnl_Edit.Visible := true;
        Pnl_Query.Visible := False;
      end
      else
      begin
        Pnl_Edit.Visible := False;
        Pnl_Query.Visible := true;
      end;  try
        InitializeForm;
        Pnl_Title.Caption := Self.Caption;
        if MainTableName='' then
          Application.MessageBox('主数据库表名没有设置!', '系统出错', MB_ICONERROR + Mb_OK);
        if Popedom=[] then
          Application.MessageBox('权限没有设置!', '系统出错', MB_ICONERROR + Mb_OK);
      except
        on E: Exception do
        begin
          if E.message = 'Abstract Error' then
            Application.MessageBox('窗口初始化出错!', '系统出错', MB_ICONERROR + Mb_OK);
        end;
      end;
      Tbtn_CancelClick(nil);
      Stbar_Single.Panels[0].Text := ControlButton;
      SetDataSetState(Ds_Main.DataSet);end;procedure TFrmB_SingleTable.Tbtn_SaveClick(Sender: TObject);
    begin
      inherited;
      if (Ds_Main.DataSet <> nil) and (Ds_Main.DataSet.State <> dsInactive) then
      begin
        Tbtn_Save.Enabled := true;
        try
          if (Ds_Main.DataSet is TADOQuery) and ((Ds_Main.DataSet as TADOQuery).Connection <> nil) then
            (Ds_Main.DataSet as TADOQuery).Connection.BeginTrans;      if Ds_Main.DataSet is TADOQuery then
            (Ds_Main.DataSet as TADOQuery).UpdateBatch(arall);      if (Ds_Main.DataSet is TADOQuery) and ((Ds_Main.DataSet as TADOQuery).Connection <> nil) then
            (Ds_Main.DataSet as TADOQuery).Connection.CommitTrans;      Application.MessageBox(Pchar('对数据操作保存成功!'), '系统提示', MB_ICONINFORMATION + Mb_OK);
          SetDataSetState(Ds_Main.DataSet);
          Tbtn_Save.Enabled := False;
          DataBaseState := [dsNone];
        except
          on E: Exception do
          begin
            if (Ds_Main.DataSet is TADOQuery) and ((Ds_Main.DataSet as TADOQuery).Connection <> nil) then
              (Ds_Main.DataSet as TADOQuery).Connection.RollbackTrans;
            Application.MessageBox(Pchar('数据没有保存成功! 错误原因如下:' + Chr(10) + E.message), '系统出错', MB_ICONERROR + Mb_OK);
          end;
        end;
      end;
    end;procedure TFrmB_SingleTable.OperDataSet(DataSet: TDataSet;
      SQLText: string; const OpenDataSet: Boolean);
    begin
      with DataSet do
      begin
        Close;
        if DataSet is TADOQuery then
        begin
          (DataSet as TADOQuery).SQL.Clear;
          (DataSet as TADOQuery).SQL.Add(SQLText);
          if OpenDataSet then
            (DataSet as TADOQuery).Open
          else
            (DataSet as TADOQuery).ExecSQL;
        end;
      end;
    end;procedure TFrmB_SingleTable.Tbtn_CancelClick(Sender: TObject);
    begin
      inherited;
      try
        if Ds_Main.DataSet <> nil then
        begin
          if ((dsMod in DataBaseState) or (dsDel in DataBaseState)) and (not (dsAdd in DataBaseState)) then
            (Ds_Main.DataSet as TADOQuery).CancelBatch(arall)
          else if dsAdd in DataBaseState then
            OperDataSet(Ds_Main.DataSet, 'Select * from ' + MainTableName + ' where 1=2');
        end;
        ControlButton;
        Tbtn_Save.Enabled := False;
        DataBaseState := [dsNone];
        SetDataSetState(Ds_Main.DataSet);
      except
        on E: Exception do
        begin
          Application.MessageBox(Pchar('取消出错! 错误原因如下:' + Chr(10) + E.message), '系统出错', MB_ICONERROR + Mb_OK);
        end;
      end;
    end;
      

  20.   

    function TFrmB_SingleTable.ControlButton(): string;
    var
      Str_Result: string;
    begin
      Str_Result := '『权限』:';
      if pdQuy in Popedom then
      begin
        ActQuy.Enabled := true;
        Str_Result := Str_Result + '【查询】'
      end
      else
        ActQuy.Enabled := False;  if pdPrt in Popedom then
      begin
        ActPrt.Enabled := true;
        Str_Result := Str_Result + ',【打印】'
      end
      else
        ActPrt.Enabled := False;  if pdAdd in Popedom then
      begin
        ActAdd.Enabled := true;
        Str_Result := Str_Result + ',【增加】'
      end
      else
        ActAdd.Enabled := False;  if not (pdDel in Popedom) then
        ActDel.Enabled := False
      else
        Str_Result := Str_Result + ',【删除】';  if not (pdMod in Popedom) then
        ActMod.Enabled := False
      else
        Str_Result := Str_Result + ',【修改】';  result := Str_Result;
    end;procedure TFrmB_SingleTable.ActQuyExecute(Sender: TObject);
    begin
      inherited;
      if BuildHint <> 'None' then
      begin
        if Application.MessageBox(Pchar(BuildHint + ',是否保存这些操作?'), '系统提示', MB_ICONQUESTION + MB_YESNO) = IDYES then
        begin
          Tbtn_SaveClick(nil);
          DataBaseState := [dsNone];
        end
        else
          DataBaseState := [dsNone];
      end;  try
        if Ds_Main.DataSet <> nil then
        begin
          OperDataSet(Ds_Main.DataSet, BuildSQL);
          ActDel.Enabled := true;
          ActMod.Enabled := true;
          SetDataSetState(Ds_Main.DataSet);
        end;
      except
        on E: Exception do
        begin
          if E.message = 'Abstract Error' then
            Application.MessageBox('查询参数无效!', '系统出错', MB_ICONERROR + Mb_OK);
        end;
      end;
      ControlButton;
    end;function TFrmB_SingleTable.BuildHint: string;
    begin
      if DataBaseState = [dsAdd, dsMod, dsDel] then
        result := '对数据库中的表进行【增加,删除,修改】操作'
      else if DataBaseState = [dsAdd, dsMod] then
        result := '对数据库中的表进行【增加,修改】操作'
      else if DataBaseState = [dsAdd, dsDel] then
        result := '对数据库中的表进行【增加,删除】操作'
      else if DataBaseState = [dsMod, dsDel] then
        result := '对数据库中的表进行【删除,修改】操作'
      else if DataBaseState = [dsAdd] then
        result := '对数据库中的表进行【增加】操作'
      else if DataBaseState = [dsMod] then
        result := '对数据库中的表进行【修改】操作'
      else if DataBaseState = [dsDel] then
        result := '对数据库中的表进行【删除】操作'
      else
        result := 'None';
    end;procedure TFrmB_SingleTable.ActPrtExecute(Sender: TObject);
    begin
      inherited;
      try
        DataPrint;
      except
        on E: Exception do
        begin
          if E.message = 'Abstract Error' then
            Application.MessageBox('打印接口无效!', '系统出错', MB_ICONERROR + Mb_OK);
        end;
      end;
    end;
    procedure TFrmB_SingleTable.Tbtn_QueryClick(Sender: TObject);
    begin
      inherited;
      if not Tbtn_Query.Down then
      begin
        Pnl_Edit.Visible := true;
        Pnl_Query.Visible := False;
      end
      else
      begin
        Pnl_Edit.Visible := False;
        Pnl_Query.Visible := true;
      end;
    end;procedure TFrmB_SingleTable.SetDataSetState(DataSet: TDataSet;
      const ReadOnly: Boolean);
    var
      Int_i: Integer;
    begin
      if DataSet <> nil then
        for Int_i := 0 to DataSet.FieldCount - 1 do
        begin
          if ReadOnly then
            DataSet.Fields[Int_i].ReadOnly := true
          else
            DataSet.Fields[Int_i].ReadOnly := False;
        end;
    end;procedure TFrmB_SingleTable.ActDelExecute(Sender: TObject);
    begin
      inherited;
      try
        if (Ds_Main.DataSet <> nil) and (Ds_Main.DataSet.State <> dsInactive) then
          if not Ds_Main.DataSet.IsEmpty then
          begin
            //SetDataSetState(Ds_Main.DataSet, False);
            Ds_Main.DataSet.Delete;
            ModDataSetState([dsDel]);
            Tbtn_Save.Enabled := true;
          end;
      except
      end;
      if Ds_Main.DataSet.IsEmpty then
      begin
        ActDel.Enabled := False;
        ActMod.Enabled := False;
      end;
    end;procedure TFrmB_SingleTable.ModDataSetState(ModType: TDBState);
    begin
      if ModType = [dsAdd] then
      begin
        if DataBaseState = [dsNone] then
          DataBaseState := [dsAdd]
        else
          DataBaseState := DataBaseState + [dsAdd];
      end
      else if ModType = [dsMod] then
      begin
        if DataBaseState = [dsNone] then
          DataBaseState := [dsMod]
        else
          DataBaseState := DataBaseState + [dsMod];
      end
      else if ModType = [dsDel] then
      begin
        if DataBaseState = [dsNone] then
          DataBaseState := [dsDel]
        else
          DataBaseState := DataBaseState + [dsDel];
      end;
    end;procedure TFrmB_SingleTable.ActAddExecute(Sender: TObject);
    begin
      inherited;
      try
        if (Ds_Main.DataSet <> nil) and (Ds_Main.DataSet.State <> dsInactive) then
        begin
          if (DataBaseState = [dsAdd]) or (DataBaseState = [dsAdd, dsDel]) or
            (DataBaseState = [dsAdd, dsMod]) or (DataBaseState = [dsAdd, dsDel, dsMod]) then
          begin
            SetDataSetState(Ds_Main.DataSet, False);
            Ds_Main.DataSet.Append;
            Tbtn_Save.Enabled := true;
            ModDataSetState([dsAdd]);
          end
          else if DataBaseState = [dsNone] then
          begin
            if MainTableName <> '' then
              OperDataSet(Ds_Main.DataSet, 'Select * From ' + MainTableName + ' Where 1=2');
            SetDataSetState(Ds_Main.DataSet, False);
            Ds_Main.DataSet.Append;
            Tbtn_Save.Enabled := true;
            ModDataSetState([dsAdd]);
          end
          else if (DataBaseState = [dsMod]) or (DataBaseState = [dsDel]) or (DataBaseState = [dsMod, dsDel]) then
          begin
            Application.MessageBox(Pchar('先保存好' + BuildHint + ',才能增加数据!'), '系统提示', MB_ICONINFORMATION + Mb_OK);
          end;
        end;
      except
      end;
    end;procedure TFrmB_SingleTable.ActModExecute(Sender: TObject);
    begin
      inherited;
      try
        if (Ds_Main.DataSet <> nil) and (Ds_Main.DataSet.State <> dsInactive) then
          if not Ds_Main.DataSet.IsEmpty then
          begin
            SetDataSetState(Ds_Main.DataSet, False);
            Ds_Main.DataSet.Edit;
            Tbtn_Save.Enabled := true;
            ModDataSetState([dsMod]);
          end;
      except
      end;
    end;procedure TFrmB_SingleTable.FormCloseQuery(Sender: TObject;
      var CanClose: Boolean);
    begin
      if BuildHint <> 'None' then
      begin
        ControlClose := true;
        HintINF := BuildHint + ',是否不保存这些操作就退出?';
      end;
      inherited;
    end;procedure TFrmB_SingleTable.Ds_MainStateChange(Sender: TObject);
    begin
      inherited;
      case Ds_Main.State of
        dsInactive:
          begin
            Tbtn_Save.Enabled := False;
            ActAdd.Enabled := False;
            ActPrt.Enabled := False;
            ActDel.Enabled := False;
            ActMod.Enabled := False;
          end;
        dsBrowse:
          begin
            ActAdd.Enabled := true;
            ActPrt.Enabled := true;
            if not Ds_Main.DataSet.IsEmpty then
            begin
              ActDel.Enabled := true;
              ActMod.Enabled := true;
            end;
          end;
        dsEdit, dsInsert:
          begin
            Tbtn_Save.Enabled := true;
            ActAdd.Enabled := true;
            ActPrt.Enabled := true;
            if not Ds_Main.DataSet.IsEmpty then
            begin
              ActDel.Enabled := true;
              ActMod.Enabled := true;
            end;
            Tbtn_Query.Down := False;
            Tbtn_QueryClick(nil);
          end;
      end;
      ControlButton;
    end;end.
      

  21.   

    同意:cll007(gazo) 他的观点在三层应用中及多层应用中,优势很明显,多层决不可能是简单的增加,删除,修改
    还有效率。
      

  22.   

    采用事件的形式也可以啊。
    type
      TCanPost = function (Sender: TDataSet): Boolean;  TData... =  class
        FOnCanPost: TCanPost;
      public
        OnCanPost: TCanPost read FOnCanPost write FOnCanPost;//这样也可以啊。
      end;
      

  23.   

    //既然逻辑与界面已经分开了,那么那个代码有必要写在界面里面吗?
    //如果要分开,就干脆每种单据的业务逻辑再定义一个类来处理!!
    TDataManage = class
      provite
         FDataContent:TDataSet;
      protected
         procedure PostData;virtual;
         function CanPost: Boolean; virtual;
      public   
         ...
      end; 
      TadoDataManage = class(TDataManage)//处理单据的一些共性化的处理
      provite
      pulbic
        constructor Create(SQLQuery:string);
        function CanPost: Boolean; override;
      end;  TDJ1 = class(TadoDataManage)//具体单据的个性处理
      public
        function CanPost: Boolean; override;
      end;impl...function TDataManage.CanPost: Boolean;
    begin
      Result:=True;
    end;function TadoDataManage.CanPost: Boolean;
    begin
      Result:=inherited CanPost;;
      if {条件} then
        Result:=False;
    end;procedure TDataManage.PostData;
    begin
      if not CanPost then
        raise Exception.Create('不能保存!');
      //开始事务
      try
        //fdasfasdf
        //提交事务
      except
        //回滚事务
        raise;
      end;
    end;
      

  24.   

    折中:
    type
    TCanPost = function (Sender: TDataSet): Boolean;TDataManage = class
      provite
         FDataContent:TDataSet;
      protected
         procedure PostData;virtual;
         function CanPost(Sender: TDataSet): Boolean; virtual;
      public   
         ...
         property OnCanPost read...
      end; 
      TadoDataManage = class(TDataManage)
      provite
      pulbic
        constructor Create(SQLQuery:string);
        function CanPost(Sender: TDataSet): Boolean; override;
      end;impl...constructor TDataManage.Create(...);
    begin
      OnCanPost:=CanPost;
    end;function TDataManage.CanPost(Sender: TDataSet): Boolean;
    begin
      Result:=True;
    end;function TadoDataManage.CanPost(Sender: TDataSet): Boolean;
    begin
      Result:=inherited CanPost;;
      if {条件} then
        Result:=False;
    end;procedure TDataManage.PostData;
    begin
      if not CanPost(DataSet) then
        raise Exception.Create('不能保存!');
      //开始事务
      try
        //fdasfasdf
        //提交事务
      except
        //回滚事务
        raise;
      end;
    end;
      

  25.   

    呵呵,lzgctgc(极度困惑)不要着急嘛!最近思路变的比较频繁,所以一直在理清头绪中!
    type
       TConnType = (ctSQLServer,ctAccess,ctOracle,ctMySQL);
       TIDataKernel = class(TObject)
       public
          constructor Create(const DBSvr,DBUser,DBPsw,DBName:string;ConnType:TConnType);virtual;abstract;
          function GetValueBySQL(const QueryStr:string;const QueryIndex:Integer=0):OleVariant;virtual;abstract;
          function GetDataSetBySQL(const QueryStr:string):oleVariant;virtual;abstract;
          function ExecuteSQL(const ExecStr:string):Integer;virtual;abstract;
          procedure ExecuteMulSQL(const ExecStr:Variant);virtual;abstract;
          procedure UpdateUserData(const UserData:oleVariant;out ErrorCount:Integer);virtual;abstract;
       end;
    这是一个虚拟类的声明,实现了部分,还在考虑加入新的东西!
      

  26.   

    楼主: 如此的代码可否:
    type
      IPostChecker = interface
      function PostCheck:Boolean;
      end;//in your form
    type
      Form1 = class(TForm, IPostChecker)
      private
      public
        Function PostCheck:Boolean;
      end;而具体的data manager:
    TCustomDataManager = class
    private
      FOwner: TComponent;
      ...
    public
      constructor Create(AOwner: TComponent);//内部self.FOwner := AOwner;赋值进去
      ...
    end;
    //创建的时候
    customDataManager  := TCustomDataManager.Create(Form1);//调用界面上的代码:
    (customDataManager.FOwner as IPostChecker).PostCheck;
      

  27.   

    //
    太多了,没看完。定义一个event属性,直接=就可以了////////////////////////////////////////////
    谢谢各位,应该说 sundytu(晶晶)的理解和我的比较接近。
    现在我想,如晶晶所说,你的PostData方法是在TDataManage定义成一个虚方法呢?还是应该定义成一个方法指针呢?
    如果定义成一个虚方法,我的界面上又不能重载这个方法,所以我偏向于定义成一个方法指针!实际的过程是:
    界面代码可能如下:
    uses
      ...,DataManage;
    Interface
    type
      Form1 = class(TForm)
      private
      public
        Function PostCheck:Boolean;//问题是我怎么把这个方法传给TDataManage?
      end;
    var
      ManaInstance:TDataManage; 
    implementationfunction TForm1.PostCheck:Boolean;
    begin
      result := true;
      if eName.text='' then
        result := false;
    end;procedure TForm1.FormCreate(Sender: TObject);
    begin
      ManaInstance := TDataManage.Create();
    //  ManaInstance.CheckPost := @PostCheck;  关键是这里,我想把Form的PostCheck方法传给TDataManage让它调用,我想用方法指针传给TDataManage类,我应该怎么做?
    //-------------
      ManaInstance.CheckPost := PostCheck;  关键是这里,我想把Form的PostCheck方法传给
    //----------------
    end;
    ////////////////////////////////////////////
    type TDataManage =
    ...
     property checkpost: Tevent
    end;
    type Tevent = ...