Delphi-数据库应用的特定控件解耦设计

    在Delphi里有个特殊的模块DataModule,这是一个运行时不可见的模块,主要用来放与数据库交互的控件,例如数据库连接控件TConnection。其他模块需要与数据库交互的话,就引入这个单元。例如吧界面上的Query的Connection指定为DataModule里的Connection控件。

    Delphi早期版本本身带了BDE版本的数据库操作控件集,然后又提供了封装了微软的ADO的控件集(以前就叫ADO,XE之后改名为dbGo),后来又提供了dbExpress控件集,以及FDAC控件集。还有众多的第三方数据库控件集,如uniDAC等。这些控件集提供了自己特别的一组控件,连接数据库的Connection,操作数据表的Table或Query等;这些控件都只能跟自己家控件集里的其他控件交互,ADO的Query无法使用BDE或者FDAC的Connection。

    竟然存在这么多的数据库控件集!!那么问题来了,如果我们平时采用拖拽控件的方式进行设计,某天基于某个特别的理由,我们想要更换一个数据库控件集,那该怎么办?这里可能就要调整下原来的设计方式了。我们可能无法再使用面向数据集编程的快速开发方式了;这也将失去Delphi强大的快速开发,快速搭建一个可以操作的用户UI的能力。

   不过,再仔细分析下,其实我们也只是失去了数据集控件的直接Post提交数据修改的能力。界面上我们可以通过内存表(早期版本可以用TClientDataSet,有FDAC的版本可以使用TFDMemTable)来提供映射数据表的能力;然后对提交数据修改结果部分稍作修改,写成执行SQL语句(Insert、Update,Delete)的方式,也并没有失去太多的Delphi的快速开发能力。如果把执行SQL语句的功能封装在一个基类或者辅助类里,其实多花费的时间也就是设计一个SQL辅助类;这个辅助类很容易就可以实现,估计花个半天时间就可以编写及测试完毕。

  好了,有了设计思路,DataModule提供数据库交互能力(与具体的数据库控件集绑定),UI上放内存表控件(TClientDataSet或TFDMemTable,Delphi后期版本建议使用TFDMemTable,数据操作更方便简单)。

    新的问题来了,我们仍然需要在UI单元里引用DataModule单元,否则无法与数据库实现交互;但是如果我们引用了DataModule单元,也意味着我们与具体的数据库控件集也绑定了,后期想切换数据库控件集,也需要大量修改UI的DataModule引用。为了避免这种问题发生,我们可以采用接口的设计方式,设计一个IDatabaseIntf,声明select,execute, transaction manage等数据库交互功能,然后在DataModule里加入IDatabaseIntf的实现。同时在接口单元里提供一个Database工厂类DatabaseFactory,实现一个getDatabase方法,返回IDatabaseIntf。用户UI单元里引用这个IDatabaseIntf单元就可以了。

unit common.IDatabaseIntf;

uses
  Data.DB;

type
  IDatabaseIntf = interface
    function  select(sql: string): TDataSet;
    procedure execute(sql: string);
    
    procedure beginTrans;
    procedure commitTrans;
    procedure rollbackTrans;
  end;

  TDatabaseFactory = class
  private
   fisDefault: booelan;
   fDatabase: TDatabaseIntf;
   
    function getDefaultDatabase: IDatabaseIntf;
  public
    destructor Destroy; override;

    //获取数据库操作接口(前端调用)
    function getDatabase: TDatabaseIntf;
    
    //注册数据库接口
    procedure registerDatabase(database: IDatabaseIntf);  
  end;

var
  DatabaseFactory: TDatabaseFactory;   //数据库接口工厂

implemention

uses
  common.database.ado; 

{ TDatabaseFactory  }

destructor TDatabaseFactory.Destroy; 
begin
  if fisDefault then
    fDatabase := nil;
  inherited;
end;

functon TDatabaseFactory.getDatabase: TDatabaseIntf;
begin
  if not assigned(fDatabase) then
    fDatabase := getDefaultDatabase;
  result := fDatabase;
end;

procedure TDatabaseFactory.registerDatabase(database: IDatabaseIntf);
begin
  fDatabase := database;
end;

function TDatabaseFactory.getDefaultDatabase: IDatabaseIntf;
begin
  result := TdatabaseADO.Create(nil);  //databaseADO是全局变量,在ADO数据库交互单元里声明
  fisDefault := true;
end;

initialization
  DatabaseFactory := TDatabaseFactory.Create;
finalization
  DatabaseFactory.Free;

end.

工厂类里提供一个registerDatabase方法,这样就不需要在接口单元引入具体的数据库实现单元了(默认实现单元必须要引入),也就达到了UI单元与具体的数据库操作单元解耦的目的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱编程的老五

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值