OOP在三层系统中的应用

作者 : smokingroom
标题 : OOP在三层系统中的应用.
关键字: OOP COM COM+ 接口模型
分类 : 开发技巧
密级 : 参赛
 

(评分:★★★ , 回复: 24, 阅读: 2049)
  在数据库应用的三层结构中,客户端能够取得应用程序服务器的接口方法来获取与更新数据.
我所作的尝试是,在中间层运用OOP设计方法,将数据封装在一些对象中,在客户端获取这些对象的
接口的引用来实现获取数据及更新数据.
  由于COM+提供了事务处理能力,并且能轻易实现Object Pooling技术,所以我选择了COM+组件
形式作为中间层.
  第一步,新建一个ActiveX Library,新增一个Transactional Data Module,名为UserMgr.意思为用户管理模块,提供的接口为IUserMgr.在数据模块中放入一个TADOConnection(ADOConnection1), 一个TADOQuery(ADOQuery1). 设置ADOConnection1的连接属性,将ADOQuery1
的Connection属性设为ADOConnection1.在UserMgr的OnActive与OnDeactive事件中处理据库连
接:
  procedure TUserMgr.MtsDataModuleActivate(Sender: Tobject);
  begin
   ADOConnection1.Connected:=True;
  end;
  procedure TUserMgr.MtsDataModuleDeactivate(Sender: Tobject);
  begin
   ADOConnection1.Connected:=False;
  end;
  
  第二步,开始我们的OOP设计了.由于这个模块的目的是用户管理,所以写封装用户信息的
Tuser类,由于这个类需要被客户端引用.所以我们采用一个新的基类-----TAutoIntfObject.
先回到Type Library,新增一个接口,名为Iuser,在这个接口下新增以下几个属性:
  UserID: long read only;
  LoginID:BSTR read write;
  UserName:BSTR read write;
  Password;BSTR read write;
  Updated:Variant_Bool read write;  //记录是否被修改
  Deleted; Variant_Bool read write; //记录是否被删除
再新增一个Unit名为uUser,在里面写上Tuser的声明与实现,该单元如下:
unit uUser;

interface

uses
 Classes, SysUtils, TestServer_TLB, ComObj, ComServ, uUserMgr;

type
 Tuser=class(TAutoIntfObject,Iuser)
 private
  FUserID:Integer;  //FUserID=-1时代表为新增用户
  FLoginID:string;
  FUserName:string;
  Fpassword:string;
  Fupdated:Boolean;
  Fdeleted:Boolean;
 protected
  function Get_UserID: Integer; safecall;
  function Get_LoginID: WideString; safecall;
  procedure Set_LoginID(const Value: WideString); safecall;
  function Get_UserName: WideString; safecall;
  procedure Set_UserName(const Value: WideString); safecall;
  function Get_Password: WideString; safecall;
  procedure Set_Password(const Value: WideString); safecall;
  function Get_Updated: WordBool; safecall;
  procedure Set_Updated(Value: WordBool); safecall;
  function Get_Deleted: WordBool; safecall;
  procedure Set_Deleted(Value: WordBool); safecall;
 public
  constructor Create(AUserID:Integer=-1;ALoginID:string=''; AUserName:string=''; 
   Apassword:string='');reintroduce;
 end;

implementation

{ Tuser }

constructor Tuser.Create(AUserID: Integer; ALoginID, AUserName,
 Apassword: string; Aadmin: Boolean);
begin
 inherited Create(ComServer.TypeLib,Iuser);
 FUserID:=AUserID;
 FLoginID:=ALoginID;
 FUserName:=AUserName;
 Fpassword:=Apassword;
end;

function Tuser.Get_Deleted: WordBool;
begin
 Result:=Fdeleted;
end;

function Tuser.Get_Updated: WordBool;
begin
 Result:=Fupdated;
end;

function Tuser.Get_LoginID: WideString;
begin
 Result:=FLoginID;
end;

function Tuser.Get_Password: WideString;
begin
 Result:=Fpassword;
end;

function Tuser.Get_UserID: Integer;
begin
 Result:=FUserID;
end;

function Tuser.Get_UserName: WideString;
begin
 Result:=FUserName;
end;

procedure Tuser.Set_Deleted(Value: WordBool);
begin
 Fdeleted:=Value;
end;

procedure Tuser.Set_Updated(Value: WordBool);
begin
 Fupdated:=Value;
end;

procedure Tuser.Set_LoginID(const Value: WideString);
begin
 if FLoginID <> Value then
 begin
  FLoginID:=Value;
  Fupdated:=True;
 end;
end;

procedure Tuser.Set_Password(const Value: WideString);
begin
 if Fpassword <> Value then
 begin
  Fpassword:=Value;
  Fupdated:=True;
 end;
end;

procedure Tuser.Set_UserName(const Value: WideString);
begin
 if FUserName <> Value then
 begin
  FUserName:=Value;
  Fupdated:=True;
 end;
end;
由于Tuser实现了Iuser,而Iuser继承自Idispatch,所以客户端可以通过获取Iuser的接口来访问
位于中间层的Tuser.

  第三步, 由于用户不止一个,我们将创建一个用户列表类TUserList,实现新增或者删除一个用
户,修改一个用户的信息等. 先也是回到Type Library,新增一个接口IUserList, 再新增两个性:
  Item[Index]:Iuser; 它为只读属性. 注意, 在这里用到了Iuser接口,用户将可以通过Item
来获取一个Tuser对象的接口.
  Count:Long ; 它也为只读属性. 代表用户的个数.
再增加三个方法:
  New: New的返回类型为Iuser,它可以实现新增一个用户.
  Delete: 传入一个参数Index:long, 它用来实现删除一个用户;
  ApplyUpdates:更新当前所有已经修改(包括新增,删除)的用户信息;
同样的,TUserList也是继承自TAutoIntfObject,实现IUserList接口.
以下是TUserList的代码:
unit uUserList;

interface

uses
 Classes, SysUtils, ComObj, ComServ, TestServer_TLB, uUser, uUserMgr, Dialogs;

type
 TUserList=class(TAutoIntfObject,IUserList)
 private
  FDM:TUserMgr;
  Fref:Iunknown;
  Flist:TInterfaceList;
  procedure Initialize;
 protected
  function Get_Item(Index: Integer): Iuser; safecall;
  function Get_Count: Integer; safecall;
  procedure Delete(Index: Integer); safecall;
  function New: Iuser; safecall;
  function ApplyUpdates: Integer; safecall;
 public
  constructor Create(ADM:TUserMgr; Aref:Iunknown);reintroduce;
  destructor Destroy;override;
 end;

implementation

{ TUserList }

function TUserList.ApplyUpdates: Integer;
var
 I:Integer;
 User:Iuser;
begin
 Result:=0;
 with FDM, ADOQuery1 do
 try
  for I:=0 to Flist.Count-1 do
  begin
   User:=Iuser(Flist.Items[I]);
   if User.Deleted and (User.UserID>0) then //删除
     begin
    Close;
    SQL.Clear;
    SQL.Add('DELETE Users WHERE UserID=' + IntToStr(User.UserID));
    ExecSQL;
    Inc(Result);
   end else
   if User.Updated and (User.UserID=-1) then //新增
   begin
    Close;
    SQL.Clear;
    SQL.Add('INSERT INTO Users(LoginID,UserName,Password)');
    SQL.Add('VALUES(:LoginID,:UserName,:Password)');
    Parameters[0].Value:=User.LoginID;
    Parameters[1].Value:=User.UserName;
    Parameters[2].Value:=User.Password;
    ExecSQL;
    Inc(Result);
   end else
   if User.Updated and (User.UserID > 0) then //修改
     begin
    Close;
    SQL.Clear;
    SQL.Add('UPDATE Users SET LoginID=:LoginID, UserName=:UserName,');
    SQL.Add('         Password=:Password);
    SQL.Add('WHERE UserID=:UserID');
    Parameters[0].Value:=User.LoginID;
    Parameters[1].Value:=User.UserName;
    Parameters[2].Value:=User.Password;
    Parameters[3].Value:=User.UserID;
    ExecSQL;
    Inc(Result);
   end;
  end;
  Initialize;
  SetComplete;
 except
  SetAbort;
  Result:=-1;
 end;
end;

constructor TUserList.Create(ADM: TUserMgr; Aref:Iunknown);
begin
 inherited Create(ComServer.TypeLib,IUserList);
 FDM:=ADM;
 Fref:=Aref; 
 Flist:=TInterfaceList.Create;
 Initialize;
end;

procedure TUserList.Delete(Index: Integer);
begin
 Iuser(Flist.Items[Index]).Deleted:=True; //作删除标记
end;

destructor TUserList.Destroy;
begin
 Fref:=nil; 
 Flist.Clear;
 Flist.Free;
 inherited;
end;

function TUserList.Get_Count: Integer;
begin
 Result:=Flist.Count;
end;

function TUserList.Get_Item(Index: Integer): Iuser;
begin
 Result:=Flist.Items[Index] as Iuser;
end;

procedure TUserList.Initialize;
var
 User:Iuser;
begin
 Flist.Clear;
 with FDM,ADOQuery1 do
 try
  Close;
  SQL.Clear;
  SQL.Add('SELECT UserID, LoginID, UserName, Password');
  SQL.Add('FROM Users');
  Open;
  while not Eof do
  begin
   User:=Tuser.Create(FieldByName('UserID').AsInteger,
             FieldByName('LoginID').AsString,
             FieldByName('UserName').AsString,
             FieldByName('Password').AsString)
   Flist.Add(User);
   Next;
  end;
  Close;
  SetComplete;
 except
  SetAbort;
 end;
end;

function TUserList.New: Iuser;
begin
 Result:=Tuser.Create;
 Flist.Add(Result);
end;

end.
这段代码里,肯定有不少人会不明白为什么创建这个对象时要加上一个Aref:Iunknown参数吧?
这是一个技巧,因为这个对象是通过一个服务器的一个方法输出,方法执行完后,将进入Deactive
状态,一段时间后,COM+对象若再无客户端连接的话,它会释放,这样,对象Tuser中的FDM引用将无效,无法更新数据.所以,在创建这个TUserList对象时,我们人为地引用一次COM+服务器对象.
直到TUserList释放时,再释放该引用.

  第四步, 在数据模块中新增一个方法,让用户可以通过该方法获取IUserList接口.也是先回到
Type Library Editor,在IUserMgr接口下新增一个GetUserList方法.刷新后写上以下实现代码:
 function TUserMgr.GetUserList: IUserList;
 begin
  _AddRef;
  Result:=TUserList.Create(Self, Self as Iunknown);
  _Release;
 end;
你一定会问,为什么要加上_AddRef与_Release呢,因为Self as Iunknown会增加一次引用,若不人
为用_AddRef来增加一次引用,将导致一退出该过程,TUserList就将自动释放,用户端无法引用该
对象.

  OK,到这里为止,服务器部分算是完成了,那么客户端如何使用这个对象呢? 在连接服务器上,
我比较喜欢先期连接,即客户端直接引用服务器的Type Library,这样编程方便,速度性能好.
以下是客户端部分代码:
uses
 ….,TestServer_TLB;

var
 UserList:IUserList;

procedure TForm1.FormCreate(Sender: Tobject);
var
 I:Integer;
 Item:TListItem;
 User:Iuser;
begin
 FUserList:=CoUserMgr.CreateRemote('192.168.1.1').GetUserList;
 Lv.Clear;  //Lv为TListView
 for I:=0 to FUserList.Count-1 do
 begin
  User:=FUserList.Item[I];
  Item:=Lv.Items.Add;
  Item.Caption:=User.LoginID;
  Item.SubItems.Add(User.UserName);
 end;
 if Lv.Items.Count>0 then
  Lv.ItemIndex:=0;
end;

  后记:这可是我这一次发表的"长篇大论", 其实自己也觉得没什么高深的技术,新各位看后不
要见笑,有什么更好的看法大家多多交流,呵呵.
  
  


2003-5-12 20:19:00

 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值