程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 兩種Delphi實現Singleton模式方法

兩種Delphi實現Singleton模式方法

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

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

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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved