Singleton(單件)模式是一種很有用的設計模式。它的意圖的是:僅僅創建一個類的實例,並提供一個訪問它的全局訪問點。全局變量使的一個對象易被訪問,但不能防止你實例化多個對象。單件模式的目的就是確保在程序的生命周期內只有一個實例存在。
看下面的代碼:
procedure TForm1. Button1Click(Sender: TObject);
var lS1 : TSingleton; l
S2 : TSingleton;
begin
try lS1 := TSingleton.Create; ////調用類的構造器
lS2 := TSingleton.Create; ////調用類的構造器
//// ...別的代碼
finally
lS1.Free; ////釋放對象
lS2.Free; ////釋放對象
end;
end;
在上面的代碼中第一次調用Create函數時TSingleton類被實例化,lS1指向一個內存存放對象的地址,當第二次調用TSingleton.Create函數時又重新實例化了TSingleton對象,lS2指向內存分配的另一個地址。Singleton模式就是讓類自己負責保存他的唯一實例。
在上面的代碼中就是讓lS2創建的時候也指向lS1指向的對象(也就是被分配同一個內存地址),同樣我們在釋放lS1時必須防止內存被釋放,因為單件對象還被lS2所引用。從而保證在程序的生命周期內有且只有一個類實例。
《設計模式》C++的示例代碼是使用C++的靜態成員變量保存實例的,同時使用protected的構造器函數。但是在Delphi中由於沒有靜態成員變量,所以不能原樣的使用該單件模式示例的方法。以下我們分析兩種Delphi實現Singleton模式的幾種方法。
一.基於override兩個Tobject虛擬函數的方法
class function NewInstance: TObject; virtual;
procedure FreeInstance; virtual;
NewInstance函數負責類對象創建的時候為對象分配內存,FreeInstance則相反釋放內存
。
前者在對象構造時調用,後者在對象析構時調用。
我們使用兩個全局變量來保存單件對象和對象的引用記數。
var Instance : TSingleton = nil;
RefCount : Integer = 0;
TSingleton類的單元:
////---------------------------------------------------------------------------
////
unit uSingleton;
interface
type
TSingleton = class(TObject)
public
class function NewInstance: TObject; override; ////覆蓋基類函數
procedure FreeInstance; override; ////覆蓋基類函數
class function RefCount: Integer; ////返回當前引用記數
end;
//// Declaration global variables
var
Instance: TSingleton = nil;
RefCount: Integer = 0;
implementation
{ TSingleton }
procedure TSingleton.FreeInstance;
begin
Dec( RefCount ); ////減少引用記數
if ( RefCount = 0 ) then ////是否為0,是則釋放內存
begin
Instance := nil;
//// 釋放單件類的私有變量
////…
inherited FreeInstance;
end;
end;
class function TSingleton.NewInstance: TObject;
begin
if ( not Assigned( Instance ) ) then
begin
Instance := TSingleton(inherited NewInstance);
////初始化私有變量 例子:
//// Instance.VariableName := Value;
end;
Result := Instance ;
Inc( RefCount );
end;
class function TSingleton.RefCount: Integer;
begin
Result := RefCount;
end;
end.
////---------------------------------------------------------------------------
////
當調用TSingleton的構造器的時候,會調用我們override的NewInstance函數,由NewInstance分配內存並返回給構造器,這樣通過override的NewInstance函數我們確保了Create函數只可能實例化一個TSingleton對象(無論調用多少次Create只返回第一次分配的內存地址)。同時RefCount變量保存我們有幾個到對象的引用。
我們在來看測試代碼
procedure TForm1.Button1Click(Sender: TObject);
var
lS1, lS2: TSingleton;
Ob1, Ob2: Tobject;
begin
lS1 := TSingleton.Create;
ShowMessage(IntToStr(RefCount)); //// Ref_Count = 1
lS2 := TSingleton.Create;
ShowMessage(IntToStr(RefCount)); //// Ref_Count = 2
Ob1 := TObject.Create;
Ob2 := Tobject.Create;
if lS1 = lS2 then
ShowMessage('地址相等') //// lS1 = lS2
else
ShowMessage('地址不相等');
if Ob1 = Ob2 then
ShowMessage('地址相等')
else
ShowMessage('地址不相等'); //// Ob1 <> Ob2
end;
當程序調用析構器的時候(就是調用FREE函數的時候),析構器會調用FreeInstance函數釋放被構造器分配的內存。Override的FreeInstance函數保證引用記數為零的時候才釋放單件模式對象的內存。
下面是我們的測試代碼:
var
lS1 : TSingleton;
lS2 : TSingleton;
begin
try
lS1 := TSingleton.Create; ////調用類的構造器
lS2 := TSingleton.Create; ////調用類的構造器
//// ...別的代碼
finally
lS1.Free; ////這裡會首先調用我們覆蓋定義的FreeInstance,
////由於這時RefCount在減1後為1,單件對象沒有被釋放
lS2.Free; ////dec(RefCount)= 0 釋放單件對象
end;
end;
上面這種單件模式實現方法很好地實現了由類自身來負責保存自己的唯一實例(通過截取創建新對象的請求——參考《設計模式》。它對TSingleton類的使用沒有特殊的限制——程序員可以隨意的調用Create和Free函數。
本模式的缺點是:該TSingleton類不能作為父類繼承生成子類。如果繼承生成兩個子類,Create時只產生一個對象。
procedure TForm1.Button1Click(Sender: TObject);
var
lS1 : 子類一;
lS2: 子類二;
begin
lS1 := 子類一.Create;
lS2 := 子類二.Create; ////不會創建子類二,lS2將指向lS1指向的內存,
////也就是 lS1 = lS2end;
二.《設計模式》上示例的Delphi實現
《設計模式》的實現示例是通過私有構造器函數來實現控制只產生一個對象實例。但該給出的C++代碼實現未給出對象如何釋放。Delphi裡面不能實現Create函數的私有化,我們新定義一個函數來代替Create函數,同時屏蔽父類的Create函數。代碼如下
:
////---------------------------------------------------------------------------
////
unit uSingletonUnit;
interface
uses
Classes, SysUtils;
type
TCSingleton = class(TComponent) ////從Tcomponent類繼承來。
private
constructor CreateInstance(AOwner: TComponent); ////傳遞Owner參數
//// 這樣TCSingleton類對象就會隨Owner一起銷毀(擁有者負責銷毀TCSingleton對象)
public
constructor Create(AOwner: TComponent); override;
class function Instance(AOwner: TComponent): TCSingleton;
end;
var
gCSingleton: TCSingleton; //// Global variables
implementation
{ TCSingleton }
constructor TCSingleton.Create(AOwner: TComponent);
begin
////屏蔽Create函數的功能
raise Exception.CreateFmt('Access class %s through Instance only',
[ClassName]);
end;
constructor TCSingleton.CreateInstance(AOwner: TComponent);
begin
////新定義的構造函數Private型的
inherited Create(AOwner);
end;
class function TCSingleton.Instance(AOwner: TComponent): TCSingleton;
begin
if not Assigned(gCSingleton) then
gCSingleton := TCSingleton.CreateInstance(AOwner);
Result := gCSingleton;
end;
end.
////--------------------------------------------------------------------------/
/
上面的實現類使用過程中,程序員不用考慮單件模式對象的銷毀問題。只是不能調用Create,必須調用Instance函數來獲得對象的實例,同時把單件擁有者作為參數傳遞到函數裡。這種實現方法可以作為基類被繼承,用在狀態模式的單件裡(參見參考文獻4),實現執行時的多態。
三.結束語
Singleton 模式的Delphi實現在網上還能查著其他的一些實現方式,本文的兩種方法上
最常見的和簡單的。同時其它方法的思路也很跟以上兩方法很相似。