我看过一篇文章(好象是超级猛料的作者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返回一个真和假的值进行操作,如果为真就保存否则就报告数据不完整!
不知道大家理解了我的意思没有!
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返回一个真和假的值进行操作,如果为真就保存否则就报告数据不完整!
不知道大家理解了我的意思没有!
上面的文章不完整,我找不到原文
里面写了有关你的问题的解决方法
------------
不理解你说的 数据完整
估计是 某些字段不能为空,或者特殊规则
不过我觉得那是 商业规则,强烈建议不要让基类去检查商业规则
http://codecentral.borland.com/codecentral/ccweb.exe/home
不过我也没仔细看,呵呵
FDataContent.BeforePost:=PostData;
不知道我是否正确理解你的意思!?
现在我想,如晶晶所说,你的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;
我正是因为不愿把商业规则写在基类里面所以才烦恼,我是想把商业规则写在具体的界面里,让具体的实例去做这些商业规则,把检测的结果告诉给基类,让基类好根据检测结果而继续做其他的事啊!
理解我的意思了没有?:)
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的一个同类型的方法就可以得到结果的,我在本机上测试过,没有问题的!
也不赞成 在界面检查 业务逻辑,它们毫不相干
水平有限,只能写一点东东
----------------------
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.
是不是对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()就完事了放着现成的组件不用,我觉得我们走远了
上面的文章不完整,我找不到原文
里面写了有关你的问题的解决方法
------------
不理解你说的 数据完整
估计是 某些字段不能为空,或者特殊规则
不过我觉得那是 商业规则,强烈建议不要让基类去检查商业规则
--------------------
to:Linux2001(恋人不如自恋) and
alphax(多喝了三五杯)
-----------
如果二位能够看看我所说的那篇文章的完整版本(一定在Borland网站上,不过我E文太差,找不到),并且能看懂,相信一定能够理解我所说的东东
我说的只是很小的一部分(限于水平),它其实是一套Framework(好象在Borland网站应该有下的 ),由外国人写的,我看过部分原始代码,很多不懂,我也看过有人在这个FrameWork上发展的东西,根据观察的结果看,程序的代码结构相当清楚,做到界面与业务的分离;并且由低级程序员写的代码相当少(主要精力放在业务层),大部分由数据向导(高级程序员写,长时间稳定)自动产生,限于某些原因,无法公布这些代码
本人水平有限(接触Delphi10个月),并且我也不是程序员,我对数据库面向对象认识,也只限于以上说出的部分,说出来以期达到抛砖引玉的作用
------------------
Delphi提供的的确有数据感知控件,但是却没有提供 自动功能,比如业务逻辑结构、界面界面上用到控件的,现在的效果就是 数据字典 输入后,将自动产生以上内容,程序员所做的 就是把控件的位置摆整齐、业务逻辑搞正确
现在我来说说我为什么要这样做的道理:
1。我们知道B/S结构的程序大多都是无连接数据源,即一获取数据就与数据源断开了。并且很少与数据库关联,界面就是界面,代码就是代码,很清晰的结构,而且效率与数据库感知的程序来说,我个人认为要高些。因为每一个客户端与数据库之间都要维持着一个连接会话。后来我在做Delphi三层应用时,界面上的控件基本上都不用感知了,都有一个获取数据和更新数据的操作。所以我需要封装一个数据操作类来对数据进行维护以减轻我的工作量
2。我已经做了一个通用的基类,由它派生的子类可以不需要编写一行代码就可以实现新增删除保存,值检测及打印等操作了,用户只要布置它的界面就可以了。只是我个人认为它还有需要改进的地点。我的目标是想建立这样一个类库:
。它能连接上不同的数据库,这样我只要创建一个这个类的实例就可以操作这个数据库了
。以不同的驱动连接数据库。如以BDE方式,以ADO方式,以DBExpress方式等。
好了,感谢大家参与,现在要做事了,请大家继续讨论!
我是盲人摸象
好,等有鬼大大的说明性源码,用代码来说明和讨论是最好的了另,cll007大大,
你所提的文章在下面的地址中有一份副本http://www.howtodothings.com/showarticle.asp?article=157
|界面元素(如窗口)|<------------------|3.组件再调用窗口元素注册到组件的方法
------------------ --------------------
|1.某个操作 |管理类(如你的组件)|<------
|激活某个组件的事件 -------------------- |
| |
| ------------------- 2。调用管理类中 |
---------->|数据组件(如DataSet|------------------
------------------- 注册到组件的事件
希望你也能传一份源码给我,到时一起与你们探讨。
:)
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;
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.
还有效率。
type
TCanPost = function (Sender: TDataSet): Boolean; TData... = class
FOnCanPost: TCanPost;
public
OnCanPost: TCanPost read FOnCanPost write FOnCanPost;//这样也可以啊。
end;
//如果要分开,就干脆每种单据的业务逻辑再定义一个类来处理!!
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;
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;
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;
这是一个虚拟类的声明,实现了部分,还在考虑加入新的东西!
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;
太多了,没看完。定义一个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 = ...