OOP在三层系统中的应用

OOP在三层系统中的应用

 

   在数据库应用的三层结构中,客户端能够取得应用程序服务器的接口方法来获取与更新数据.
我所作的尝试是,在中间层运用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;

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

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值