兩種Delphi實現Singleton模式方法 (轉)

gugu99發表於2007-12-28
兩種Delphi實現Singleton模式方法 (轉)[@more@]

兩種實現Singleton方法

haozi


摘  要 本文描述了兩種Singleton模式的Delphi實現方式,並做了對比分析。
關鍵字 設計模式,Singleton

Singleton(單件)模式是一種很有用的設計模式。它的意圖的是:僅僅建立一個類的例項,並提供一個訪問它的全域性訪問點。全域性變數使的一個易被訪問,但不能防止你例項化多個物件。單件模式的目的就是確保在的生命週期內只有一個例項存在。
看下面的程式碼:
procedure TForm1. Button1Click(Sender: T);
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模式的幾種方法。

一.基於overr兩個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實現在網上還能查著其他的一些實現方式,本文的兩種方法上

最常見的和簡單的。同時其它方法的思路也很跟以上兩方法很相似。

以上例子均沒有考慮多執行緒的情況。
作者E:">wuch@km169.net

參考文獻:
1.《Creating a real singleton class in Delphi》 by Lasse
2.《Delphi 5 開發人員指南》機械工業出版社 2000.7
3.《設計模式》機械工業出版社 2000.9
4.《UML和模式應用》機械工業出版社 2002.1等


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-995947/,如需轉載,請註明出處,否則將追究法律責任。

相關文章