delphi中实现数据访问层、业务逻辑层与界面层(1分)

  • 主题发起人 主题发起人 jxyyl
  • 开始时间 开始时间
J

jxyyl

Unregistered / Unconfirmed
GUEST, unregistred user!
delphi是非常不错的开发工具,尤其是非常丰富的控件资源,这是其它语言很少能比拟的。
但它也有一个非常大的缺点,太多使用了数据感知控件,这样业务逻辑与界面逻辑不能
很好地分离,系统维护成本极高。
本人参考c#的ntiergen思路,整体分为数据访问层,业务逻辑层(从数据访问层继承用于放自定义的函数),界面层,而且不使用数据感知控件,现试开发一个小的软件,总体感觉程序结构清晰,程序可控制能力强,现将一点体会与大家分享。
1、数据访问层代码:
unit uDataAccessoper;
interface
uses sysutils,Classes,SqlExpr,DBXpress;
type Toper=class
private
FId:integer;
FOperCode:string;
FOperName:string;
FOperPasswd:string;
FCreateDate:TDateTime;
FCreateOper:string;
FOrgAccredit:string;

public
constructor Load(id:integer);
procedure Save;
procedure Delete(id:integer);
{%PublicMothed%}
published
Property Id:integer read FId write FId;
Property OperCode:string read FOperCode write FOperCode;
Property OperName:string read FOperName write FOperName;
Property OperPasswd:string read FOperPasswd write FOperPasswd;
Property CreateDate:TDateTime read FCreateDate write FCreateDate;
Property CreateOper:string read FCreateOper write FCreateOper;
Property OrgAccredit:string read FOrgAccredit write FOrgAccredit;

end;

type Topers=class(TList)
constructor GetAll();
end;


implementation
uses uConnection;
{ tBussinessOrg }

procedure Toper.Delete(id: integer);
var
sql:string;
query:TSQLQuery;
begin
query:=TSQLQuery.Create(nil);
query.SQL.Clear;
query.ParamCheck:=false;
query.SQLConnection:=GetConn;
query.SQL.Add('delete from oper where id=:id');
query.Params.ParseSQL(query.SQL.Text,true);
query.ParamByName('id').AsInteger:=id;
try
query.ExecSQL;
finally
query.SQLConnection.Close;
query.Free;
end;
end;

constructor Toper.Load(id: integer);
var
query:TSQLQuery;
begin
query:=TSQLQuery.Create(nil);
query.SQL.Clear;
query.ParamCheck:=false;
query.SQLConnection:=GetConn;
query.SQL.Add('select * from oper where (id=:id)');
query.Params.ParseSQL(query.sql.text,true);
query.ParamByName('id').AsInteger:=id;
try
query.Open;
FId:=query.FieldByName('Id').asinteger;
FOperCode:=query.FieldByName('OperCode').asstring;
FOperName:=query.FieldByName('OperName').asstring;
FOperPasswd:=query.FieldByName('OperPasswd').asstring;
FCreateDate:=query.FieldByName('CreateDate').asDateTime;
FCreateOper:=query.FieldByName('CreateOper').asstring;
FOrgAccredit:=query.FieldByName('OrgAccredit').asstring;

finally
query.SQLConnection.Close;
query.Free;
end;
end;

procedure Toper.Save;
var
query:TSQLQuery;
begin
query:=TSQLQuery.Create(nil);
query.SQLConnection:=GetConn;
//insert
if self.FId=0 then
begin
try
query.SQL.Clear;
query.ParamCheck:=false;
query.SQL.Add('insert into oper(Id,OperCode,OperName,OperPasswd,CreateDate,CreateOper,OrgAccredit) values(:Id,:OperCode,:OperName,:OperPasswd,:CreateDate,:CreateOper,:OrgAccredit)');
query.Params.ParseSQL(query.SQL.Text,true);

query.ParamByName('Id').asinteger:=Id;
query.ParamByName('OperCode').asstring:=OperCode;
query.ParamByName('OperName').asstring:=OperName;
query.ParamByName('OperPasswd').asstring:=OperPasswd;
query.ParamByName('CreateDate').asDateTime:=CreateDate;
query.ParamByName('CreateOper').asstring:=CreateOper;
query.ParamByName('OrgAccredit').asstring:=OrgAccredit;

query.ExecSQL;
query.Close;
query.SQL.Clear;
query.SQL.Add('select last_insert_id() as id');
query.Params.Clear;
query.Open;
self.FId:=query.FieldValues['id'];
finally
query.SQLConnection.Close;
query.Free;
end;
end
//update
else
begin
try
query.SQL.Clear;
query.ParamCheck:=false;
query.SQL.Add('update oper set OperCode=:OperCode,OperName=:OperName,OperPasswd=:OperPasswd,CreateDate=:CreateDate,CreateOper=:CreateOper,OrgAccredit=:OrgAccredit where id=:id');
query.Params.ParseSQL(query.SQL.Text,true);

query.ParamByName('Id').asinteger:=Id;
query.ParamByName('OperCode').asstring:=OperCode;
query.ParamByName('OperName').asstring:=OperName;
query.ParamByName('OperPasswd').asstring:=OperPasswd;
query.ParamByName('CreateDate').asDateTime:=CreateDate;
query.ParamByName('CreateOper').asstring:=CreateOper;
query.ParamByName('OrgAccredit').asstring:=OrgAccredit;

query.ExecSQL;
finally
query.SQLConnection.Close;
query.Free;
end;
end;
end;

constructor Topers.GetAll;
var
query:TSQLQuery;
oper:Toper;
begin

query:=TSQLQuery.Create(nil);
query.SQLConnection:=GetConn;;
query.SQL.Add('select * from oper');
try
query.Open;
while not query.Eofdo
begin
oper:=Toper.create;
oper.FId:=query.FieldByName('Id').asinteger;
oper.FOperCode:=query.FieldByName('OperCode').asstring;
oper.FOperName:=query.FieldByName('OperName').asstring;
oper.FOperPasswd:=query.FieldByName('OperPasswd').asstring;
oper.FCreateDate:=query.FieldByName('CreateDate').asDateTime;
oper.FCreateOper:=query.FieldByName('CreateOper').asstring;
oper.FOrgAccredit:=query.FieldByName('OrgAccredit').asstring;

self.Add(oper);
query.Next;
end;
finally
query.SqlConnection.Close;
query.Free;
end;
end;

end.

2、业务逻辑层代码:
unit uBusinessoper;
interface
uses sysutils,Classes,uDataAccessoper,SqlExpr,DBXpress;
type TB_oper=class(Toper)
private
public
procedure VerifyPassword(passwd:string);
constructor GetOperByCode(code:string);
published
end;

type TB_opers=class(Topers)
public
end;


implementation
uses uConnection;
{ TB_oper }
constructor TB_oper.GetOperByCode(code: string);
var
query:TSQLQuery;
begin
query:=TSQLQuery.Create(nil);
query.SQL.Clear;
query.ParamCheck:=false;
query.SQLConnection:=GetConn;
query.SQL.Add('select * from oper where (opercode=:opercode)');
query.Params.ParseSQL(query.sql.text,true);
query.ParamByName('opercode').AsString:=code;
try
query.Open;
if query.Eof and query.Bof then
raise exception.Create('没有该操作员!');
Id:=query.FieldByName('Id').asinteger;
OperCode:=query.FieldByName('OperCode').asstring;
OperName:=query.FieldByName('OperName').asstring;
OperPasswd:=query.FieldByName('OperPasswd').asstring;
CreateDate:=query.FieldByName('CreateDate').asDateTime;
CreateOper:=query.FieldByName('CreateOper').asstring;
OrgAccredit:=query.FieldByName('OrgAccredit').asstring;

finally
query.SQLConnection.Close;
query.Free;
end;

end;

procedure TB_oper.VerifyPassword( passwd: string);
begin
if passwd <>self.OperPasswd then
raise exception.Create('密码错误!');
end;

end.
3、界面层代码(登录):
unit uLogin;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, uDialogForm, StdCtrls, ExtCtrls;
type
TfrmLogin = class(TDialogForm)
GroupBox1: TGroupBox;
cmdlogin: TButton;
cmdCancel: TButton;
Image1: TImage;
Label1: TLabel;
Label2: TLabel;
txtPasswd: TEdit;
txtUser: TEdit;
procedure cmdCancelClick(Sender: TObject);
procedure cmdloginClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
frmLogin: TfrmLogin;
function ShowForm:TModalResult;
implementation
uses uFrmMain, uBusinessoper;
{$R *.dfm}
function ShowForm:TModalResult;
begin
frmLogin:=TfrmLogin.Create(nil);
result:=frmLogin.ShowModal;
end;

procedure TfrmLogin.cmdCancelClick(Sender: TObject);
begin
inherited;
self.ModalResult:=mrCancel;
end;

procedure TfrmLogin.cmdloginClick(Sender: TObject);
begin
inherited;
frmMain.oper:=TB_oper.Create;
try
frmMain.oper.GetOperByCode(self.txtUser.Text);
frmMain.oper.VerifyPassword(self.txtPasswd.Text);
except
on e:exceptiondo
begin
showmessage(e.Message);
exit;
end
end;
self.ModalResult:=mrOk;
end;

end.
4、数据访问层与业务逻辑层可以用生成器生成,修改数据库后需要改动代码较少
欢迎大家多提意见!
 
补充:
开发速度绝对比用数据感知控件快,因为业务逻辑的可控性提高。form结构基本统一,一般界面只需复制,小量修改即可。
 
学习ing!
不过我一般是自己做好框架然后再向里添东西的。
基本方法与楼主一样。
我个人不太爱用DBEDIT等的数据感知类的控件,总认为易错且不利于多用户的并发操作数据。
 
不错,有自己的一套想法。
 
楼主有空帮忙看看这个问题,也是分层思想的:
http://www.delphibbs.com/delphibbs/dispq.asp?lid=2512713
 
你这样每次都动态生成 TSQLQUERY 控件有没有发现速度要慢一些?
是否真的快一些?我想肯定是拖控件快一些!设定一TSQLQUERY控件后然后复制,粘贴,只要改SQL语句就可以了或则写一个
RUNSQL(PQRY:TQUERY;SDBNAME,SSQL:STRING);函数,
用的时候调用她,也好简单!
我其实也想找到一个很好的业务逻辑和截面分开的方法,不过我觉得你的实用性不大,
可以讲的更详细吗?

 
我有個疑問,在有主/細資料時,如何達到DBGrid似的效果
 
TearAngel:
开发的速度可能比直接用数据感知控件慢一些,但程序修改与维护要简便多了,数据层与业务逻辑层以上代码已有了,界面层我是这样实现的
procedure ObjectToInterface;//对象-界面
begin
txtName.text:=obj.Name;
end;

procedure InteraceToObject;//将界面内容存放到对象
begin
obj.Name:=txtName.text;
end;

procedure VerifyInterface;//检查界面值
begin
if trim(txtName.text)='' then
raise exception.create('请输入名称');
end;

procedure Button1Click(sender:TObject)
begin
try
self.VerifyInterface;
self.obj.save;
except
on e:exceptiondo
begin
showmessage(e.message);
end
end;
showmessage('操作成功');
ModalResult:=mrok;
end;

核心的处理是以上的几个函数,基本所有界面多是这个框架,可以称得上是一种模式.
另外,在数据访问层与业务逻辑层数据库操作尽量用参数化处理,这样,不必考虑特殊符号得问题.
 
leidaq:
在listview的单击事件中,再次读取数据库.dbgrid也是一样的,只是它自动处理了,不用数据感知控件的目的是还程序员对系统的控制。
我曾看到delphi把程序员变懒了的文章,我原先也是非常喜欢数据感知控件的,但后来发现这样做界面层不能分离,软件维护工作量巨大。
另外,如果你做到了分离,你会发现所谓的“编程语言不是重要的”说法是正确的,因为我们没有局限于一种语言下,所有的语言都用这种模式去处理,编程就轻松多了。
 
<<<<<<<<如果你做到了分离,你会发现所谓的“编程语言不是重要的”说法是正确的,因为我们没有局限于一种语言下,所有的语言都用这种模式去处理,编程就轻松多了
哈哈,同感同感!
 
做到分离后是方便很多,现也在继续中,我感觉不用数据感知控件就是检查数据有效性与写SQL保存等这些有点烦,还有就是生成SQL保存时要判断怎么生成Insert的与Update的
 
总体思想不错,但不知你的生成器是怎么做的。还是就是对多层是如何解决的。如果可以的话可做成一数据库应用架构。
 
to jxyyl:
非常感謝你的教導,我會去試試,最近比較忙.我對這方面很有興趣,希望以後還能跟你學習,
to all;
現在才看到一些有關MM與BOLD的東西,很想了解一下,不知道大家有些什麼看法, 如果有經驗或認為是很好的東西, 是否可以共享交流,以共同提高
 
楼主:
想和你讨论这方面的东西,能否告知QQ或者MSN
我的QQ是:303362847
MSN:wangxian11@hotmail.com
想讨论此问题的也可以加我:)
 
我的msn:jxyyl@hotmail.com
 
bold 不知大家用的怎么样!我的msn:tonisqz@hotmail.com
 
你这个方法不好 效率低不说 最主要是业务太简单了一些 没有代表性
你去做个 JXC 系统 的某个功能来看看
 
后退
顶部