如何应用OOP思想,实现代码(接口)重用,化繁为简?(200分) 请NicroSoft也进来看看,谢谢! (200分)

  • 主题发起人 Opportunity
  • 开始时间
O

Opportunity

Unregistered / Unconfirmed
GUEST, unregistred user!
做基础参数配置模块时,实现对特定表的“增、删、改”是最基础的功能,几乎每一个配置子单元都会用到。而且,这些功能实现的代码,大同小异。如果就这么“平铺直叙”的,这些“重复”功能的编码实现,几乎是先COPY、PLASTER再做相应的修改。当配置子单元很多时,其工作将是极其枯燥乏味而又漫长的!
  如何应用OOP思想(封装、继承、多态),实现代码(接口)重用,化繁为简呢?
  以下面的代码为例,看各路高手如何各显神通! 
  (代码比较长,但逻辑很简单)

unit uMainCfg; {主界面}
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ActnList, Db, DBTables, Grids, DBGrids, Menus;
type
TFormCfg = class(TForm)
ActionList1: TActionList;
ActAIns: TAction;
ActAUpd: TAction;
ActADel: TAction;
Query: TQuery;
DataSourceA: TDataSource;
QueryA: TQuery;
DBGridA: TDBGrid;
PopupMenu1: TPopupMenu;
ActAIns1: TMenuItem;
ActAUpd1: TMenuItem;
ActADel1: TMenuItem;
procedure ActAInsExecute(Sender: TObject);
procedure ActAUpdExecute(Sender: TObject);
procedure ActADelExecute(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
FormCfg: TFormCfg;

implementation

uses
uDlgA; {实现增、改的配置界面}
{$R *.DFM}

{增加}
procedure TFormCfg.ActAInsExecute(Sender: TObject);
const
Script = 'INSERT INTO TAB_A (ID, NAME, WAGE,DATE) '+#13#10+
' VALUES :)ID, :NAME, :WAGE, :DATE) ';
var
iID: Integer;
begin
inherited;
try
FmDlgA:= TFmDlgA.Create(Self);
with FmDlgA do
begin
iDlgActID := 1; // 增加

// 参数初始化
giID:= GetNextvalTabA;

ShowModal;
if ModalResult= mrOK then begin
with Query do
begin
Close;
Sql.Text:= Script;
iID:= StrToInt(Trim(EditID.Text));
ParamByName('ID').AsInteger:= iID;
ParamByName('NAME').AsString:= Trim(EditName.Text);
ParamByName('WAGE').AsFloat:= StrToFloat(Trim(EditWage.Text));
ParamByName('DATE').AsDateTime:= DateTimePickerDate.DateTime;
try
ExecSql;
Commit;      // 数据提交
RefreshTabA(iID); // 刷新配置表
except
on E: Exception do
begin
Rollback; // 数据回退
EMsgBox('增加××时出错!', '错误消息:'+E.Message);
Exit;
end;
end;
end;
end;
end;
finally
FmDlgA.Free;
end;
end;

{修改}
procedure TFormCfg.ActAUpdExecute(Sender: TObject);
const
Script = 'UPDATE TAB_A '+#13#10+
' SET NAME = :NAME, '+#13#10+
' WAGE = :WAGE, '+#13#10+
' DATE = :DATE '+#13#10+
' WHERE ID = :ID ';
var
iID: Integer;
begin
inherited;
try
FmDlgA:= TFmDlgA.Create(Self);
with FmDlgA do
begin
iDlgActID := 0; // 修改

// 参数初始化
SetParaTabA(giID, gsName, gfWage, gdDate);

ShowModal;
if ModalResult= mrOK then begin
with Query do
begin
Close;
Sql.Text:= Script;
iID:= QueryA.FieldByName('ID').AsInteger;
ParamByName('ID').AsInteger:= iID;
ParamByName('NAME').AsString:= Trim(EditName.Text);
ParamByName('WAGE').AsFloat:= StrToFloat(Trim(EditWage.Text));
ParamByName('DATE').AsDateTime:= DateTimePickerDate.DateTime;
try
ExecSql;
Commit;
RefreshTabA(iID);
except
on E: Exception do
begin
Rollback;
EMsgBox('修改××时出错!', '错误消息:'+E.Message);
Exit;
end;
end;
end;
end;
end;
finally
FmDlgA.Free;
end;
end;

{删除}
procedure TFormCfg.ActADelExecute(Sender: TObject);
const
Script = 'DELETE FROM TAB_A '+ #13#10 +
' WHERE ID = :ID ';
var
iID: Integer;
begin
inherited;
if Not MsgConfirm('确定要删除该××吗?') then
Exit;

with Query do
begin
Close;
Sql.Text:= Script;
ParamByName('ID').AsInteger:= iID;
try
ExecSql;
Commit;
RefreshTabA(0);
except
on E: Exception do
begin
Rollback;
EMsgBox('删除××时出错!', '错误消息:'+E.Message);
Exit;
end;
end;
end;
end;

end.

/=============================================================================/

unit uDlgA;{实现增、改的配置界面}
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ComCtrls, StdCtrls;
type
TFmDlgA = class(TForm)
BtnOK: TButton;
BtnCancel: TButton;
EditID: TEdit;
EditName: TEdit;
EditWage: TEdit;
DateTimePickerDate: TDateTimePicker;
procedure BtnOKClick(Sender: TObject);
procedure BtnCancelClick(Sender: TObject);
procedure FormShow(Sender: TObject);
private
{ Private declarations }
public
iDlgActID: Integer;
  // 交互的参数
giID: Integer;
gsName: string;
gfWage: Double;
gdDate: TDateTime;

{ Public declarations }
end;

var
FmDlgA: TFmDlgA;

implementation

{$R *.DFM}

procedure TFmDlgA.FormShow(Sender: TObject);
begin
Case iDlgActID of
0:
begin
Caption := '修改××';
EditID.Text:= IntToStr(giID);
EditName.Text:= gsName;
EditWage.Text := FloatToStr(gfWage);
DateTimePickerDate.Date := gdDate;
end;
1:
begin
Caption := '增加××';
EditID.Text:= IntToStr(giID);
end;
end;
EditID.SetFocus;
end;

procedure TFmDlgA.BtnOKClick(Sender: TObject);
begin
if Not CheckValue then //用于数据稽核,核查通过为真
Exit;
ModalResult:= mrOK;
end;
procedure TFmDlgA.BtnCancelClick(Sender: TObject);
begin
ModalResult:= mrCancel;
end;
end.
 
之前拜读过 NicroSoft 大侠,有关OOP编程思想的几篇文章。
《创建良好设计的代码(基于Delphi/VCL)》、
《如何将界面代码和功能代码分离(基于Delphi/VCL)》、
《“多态”三部曲》等
很精彩!!
所以,很想看看NicroSoft大侠对这个问题解答,谢谢!
 
把你的大部分基本功能写在一个窗体里,保存。然后继承它。
继承的方法是:new -> newItems ->找到以你的工程名命名的选项卡 ->
找到你想继承的窗体 -> Ok
这样就实现了继承。
 
TO YFeral
感谢第一个回贴的兄弟!
 你所说方法是最简单的窗体继承。
 其实在配置模块中,我通常会设置两个用于继承的母版(FormCust-主配置模版, DlgCust-对话框模版)用于封装简单的功能(如登录日志等)
 但上面的“增、删、改”操作中,涉及的变量比较多,这种简单的继承不易实现代码的重用。
 涉及的变量至少有这些:
 1.增、改界面(如:FmDlgA); (TDlgClass = Class of TDlgCust; )
2.FmDlgA中需初始化的参数; (Varray of Variant)
 2.TFormCust 中用于“其他处理”(如:刷新表、数据定位等)的过程; ( TProcOthers = procedure(Param) of Object; )
 3.SQL 的脚本、动态参数、参数值
 
你至少得把显示功能和数据处理功能先分离开啊,你现在是把Query直接放Form上吧。
应该将查询修改功能放在数据模块的接口上.
 
我想还是专门定义一个类,把一些基本的对数据集的操作定应义为方法,
这样的话,在程序中就可以重复使用吧,
象上面这样继承FORM,感觉意义不大.
//关注此题,时刻关注.
 
TO xeen:
实际中对表操作的Query(固定SQL),是放在数据模版DataModule中的。上述的Query 只是个SQL的临时的处理环境而已。
 
TO 唐太宗
我也是这个想法,用自定义的类实现对数据成员和方法的封装。
  希望能有OOP高手相助,,,
//你名字好酷啊:D
 
To Opportunity:
是啊,应该这样作.你还应该作到在Form所在的单元根本无法直接访问TQuery组件,而在
数据处理类的Public成员方法中提供这些数据处理功能.
 
TO xeen:
  谢谢,你的热心!
  实现界面代码和功能代码的分离,这点我也在考虑。
  (积习难改,是该下决心了,,,)
 
我也搞了一个类似的东西,但还不是很成熟,
我想要实现界面和代码分离,少不了数据库的支持。
可以考虑建立一些辅助数据库表格,结合一个一个模板(可继承),
本人正在做着方面的工作,愿意与大家一起交流,APZHANG@CHINALANYA.COM
 
界面与代码分离,其实还是不难的,难的是因为数据库的程序,因为业务规则变化多,
所以关键是如何定义出基类能够复用,才是最关键的,不然就是一般的代码与界面
分离,意义不大,呵呵,自我感觉是这样的,
//继续关注.
 
to ALL:
谈到这里大家的一致愿望是用纯粹的OOP封装自己的业务逻辑。这样做的结果是程序
实现了OOP的思想但实现起来可能麻烦,我学过Java全是自学的,也过了认证
(应用不多,可能理解有偏差,请高手指点)。有人说Java是完全面向对象的语言,
我也这样认为,也希望有一天自己写的程序完全能体现出Java的OOP精髓。但是OOP这
东西不是说行就行的,是一个思想、理论他对入门者要求比较高,但他的实际用处确实
比较大!可是目前在国内用面向过程的思想就能搞定的事没有多少人非得用OOP重写,
因为大家的目标是实用!
说了这么多无非是OOP是好的东西!但它的使用要区分环境、人群、应用。对于在
Delphi的丰富的VCL的支持下我们根本没有必要去重写我们的业务逻辑,因为那是没
有必要的。至少是小型的应用。
 
是啊,进行“纯粹的”OOP之前,应当先得OOA,OOD。。OOP不可能是孤立存在的。

本论题,是想就实现上述“增、删、改”功能,如何尽可能的融入OO思想,通过使用封装、继承、多态等方法,实现代码(接口)重用,化繁为简?

谢谢各位,对这个论题的关注!
大家畅所欲言:)



 
我想是必需定义一个基类,然后其它的操作再从它继承的,基本上这样比较合理,不过
具体的代码我也没有实现过,等忙完这阵来写写.
 
界面与代码分离之后,它们的接口怎么实现呢?
因为数据库有很多商业规则要进行判断,而进行这些判断就好的地方是字段级的事件。
因为字段相对于界面是“固定”的,而相对于代码是“动态”的,如果字段与判断逻辑分离了之后,
两者之间的接口怎么实现?
 
个人认为,我基本上不会容忍自己的程序中有"添加和修改"这样两个基本上完全相同
的过程单独存在,这一点可以和OOP没有太多关系,不过我想应该会简单很多.
我甚至觉得还可以让删除过程也和添加修改过程合并在一起,它们所不同的是只不过
添加修改需要弹出一个对话框来输入数据,添加数据后的部份基本上是一样的:

过程开始:

如果是删除:提示并确认,如果确认否,则退出;

如果是修改或添加:
begin
创建对话框
try
打开对话框并输入数据,如果取消数据输入,则退出,否则记录操作的数据;
Finally
释放对话框.
end;
end;

构造一个Query(这个Query不应该放在Form上),设置DatabaseName及另外相关属性;

分别按不同的操作赋SQL属性;

分别按不同的操作赋Params属性;

try
执行SQL并提交事务;
except
回滚并提示.
end;
 
另外应用OOP思想,TFmDlgA域中不应该是变量而应该是属性,并在属性的写接口
对相应的输入框赋值,而在属性的读接口直接取相应的输入框的值,另外在上面
合并在一起的过程中,不应直接取TFmDlgA的输入框的文本属性,而应该取TFmDlgA的
相关属性.
 
顶部