2009-09-11 32 views
10

我正在尋找在Delphi中創建單例。在使用舊版本的Delphi之前,我已經完成了這個工作,最後使用了全局變量(在實現部分),並使用初始化和終結來處理實例。也無法阻止用戶創建實例,因爲您無法隱藏標準構造函數。我想知道是否有任何新的功能,如類構造函數和析構函數,以及類變量(好,不是那麼新),也許泛型,可以幫助創建一個通用的單例類。我還沒有設法讓我滿意。使用D2009和D2010的新功能在Delphi中創建單例

回答

3

有可能通過覆蓋在Delphi中TRUE分配器和釋放器的方法,NewInstance方法FreeInstance來管理這一點。Delphi中的構造函數和析構函數只分別初始化和終止,它們不分配或解除分配內存,所以試圖隱藏構造函數總是有點誤導。

即有可能允許自由使用的任何和所有構造函數,只要你推翻NewInstance方法,使得它永遠只能返回的類的內存單一分配的參考。

但是試圖在基類中強制使用/行爲模式是一個錯誤。並非所有模式都是或需要特定類來封裝模式。

在這種情況下,您最終會創建一些不必要的複雜內容,並且複雜性會吸引我的經驗和練習對象中的錯誤,然後嘗試在模式實現中發現缺陷,然後嘗試實施針對這些模式的保護措施缺點,而不是完成單身人士課程應該完成的實際工作。

遠遠更遠,更簡單,更有效地記錄類的用法。

文檔作爲實現這種模式的技術已經完美地工作了15年的應用屏幕在VCL對象,例如,何況我在那些年裏創造了無數的其他單身。

3

對於單身人士,您可以重寫NewInstance方法。並使用類變量。您在第一次調用時創建變量並將指針返回給彼此呼叫的類。

你只需要找到一些最終銷燬它(可能使用finalize)。

+0

或D2010類的析構函數。 –

8

在德爾福2010年迄今爲止,最好和最安全的方式是使用類構造函數。請參閱here - 特別閱讀名爲的段落改進封裝

HTH。

+1

很遺憾,他們沒有打算正確記錄此功能,或將其包含在幫助中的「新增功能」中。 – IanH

7

我更喜歡在需要單例時使用接口,並在實現部分隱藏接口的實現。

利於

  • 自動銷燬的程序終止時。
  • 沒辦法意外地創建一個TMySingleton。

弊端

  • 有人可能決定實現自身IMySingleton。

注意:我相信單身人士的使用應該保持在絕對最低限度。總而言之,單身人士不僅僅是榮耀的全球變數。如果並且當你開始單元測試你的代碼時,它們會變成討厭的東西。

unit uSingleton; 

interface 

type 
    ISingleton = interface 
    ['{8A449E4B-DEF9-400E-9C21-93DFA2D5F662}'] 
    end; 

function Singleton: ISingleton; 

implementation 

uses 
    SyncObjs; 

type 
    TSingleton = class(TInterfacedObject, ISingleton); 

var 
    Lock: TCriticalSection; 

function Singleton: ISingleton; 
const 
    _singleton: ISingleton = nil; 
begin 
    if not Assigned(_singleton) then 
    begin 
    Lock.Acquire; 
    try 
     if not Assigned(_singleton) then 
     _singleton := TSingleton.Create(); 
    finally 
     Lock.Release; 
    end; 
    end; 
    Result := _singleton; 
end; 

initialization 
    Lock := TCriticalSection.Create; 
finalization 
    Lock.Free; 

end. 
+0

+1表示單身上的斜體部分。 – mghie

+0

@mghie:你不同意模式的實現? –

+0

我同意你的觀點,Singleton模式的大部分用途與全局變量相比沒有進步。但是如果有人想要擁有它們(無論出於何種原因),那麼就需要一個線程安全的通用解決方案。你的絕對不是,所以你可能最終得到不止一個實例。至少它不會泄漏內存,這是我在大型商業庫中看到的,其中對象用於單例實現而不是接口。莫里茨的解決方案看起來可能是線程安全的,但必須徹底檢查。 – mghie

12

如果你只需要一個簡單的單,最簡單的方法是使用類構造函數和類方法通過plainth的建議。但是,如果您需要按需構建單例(即第一次訪問),泛型非常有用。

以下代碼來自我的一個公用事業單位;它基本上爲德爾福2009年以後提供了一個通用的單體工廠。

interface 

type 
    {$HINTS OFF} 
    { TSingletonInstance<> implements lazy creation, which is sometimes useful for avoiding 
    expensive initialization operations. 
    If you do not require lazy creation and you target only Delphi 2010 onwards, you should 
    use class constructors and class destructors instead to implement singletons. } 
    TSingletonInstance<T: class, constructor> = record 
    private 
    FGuard: IInterface; 
    FInstance: T; 
    function GetInstance: T; 
    function CreateInstance: TObject; 
    public 
    property Instance: T read GetInstance; 
    end; 
    {$HINTS ON} 
    TSingletonFactoryFunction = function: TObject of object; 

{ Private symbols (which are in the interface section because of known limitations of generics) } 
procedure _AllocateSingletonInstance (InstanceRecord: Pointer; Factory: TSingletonFactoryFunction); 

implementation 

{ TSingleton } 

var 
    SingletonCriticalSection: TRTLCriticalSection; 

type 
    TSingletonGuard = class (TInterfacedObject) 
    private 
    FSingletonInstance: TObject; 
    public 
    constructor Create (AInstance: TObject); 
    destructor Destroy; override; 
    end; 

    PUntypedSingletonInstance = ^TUntypedSingletonInstance; 
    TUntypedSingletonInstance = record 
    FGuard: IInterface; 
    FInstance: TObject; 
    end; 

    // TODO: is a lock required for multiple threads accessing a single interface variable? 
procedure _AllocateSingletonInstance (InstanceRecord: Pointer; Factory: TSingletonFactoryFunction); 
var 
    USI: PUntypedSingletonInstance; 
begin 
    USI := PUntypedSingletonInstance (InstanceRecord); 
    EnterCriticalSection (SingletonCriticalSection); 
    if USI.FInstance = nil then 
    begin 
    USI.FInstance := Factory(); 
    USI.FGuard := TSingletonGuard.Create (USI.FInstance); 
    end; 
    LeaveCriticalSection (SingletonCriticalSection); 
end; 

constructor TSingletonGuard.Create (AInstance: TObject); 
begin 
    FSingletonInstance := AInstance; 
end; 

destructor TSingletonGuard.Destroy; 
begin 
    FSingletonInstance.Free; 
    inherited; 
end; 

function TSingletonInstance<T>.GetInstance: T; 
var 
    Factory: TSingletonFactoryFunction; 
begin 
    if FInstance = nil then 
    begin 
    Factory := Self.CreateInstance; // TODO: associate QC report 
    _AllocateSingletonInstance (@Self, Factory); 
    end; 
    Result := FInstance; 
end; 

function TSingletonInstance<T>.CreateInstance: TObject; 
begin 
    Result := T.Create; 
end; 

initialization 
    InitializeCriticalSection (SingletonCriticalSection); 
finalization 
    DeleteCriticalSection (SingletonCriticalSection); 

用法如下:

type 
    TMySingleton = class 
    public 
    constructor Create; 
    class function Get: TMySingleton; static; 
    end; 

var 
    MySingletonInstance: TSingletonInstance<TMySingleton>; 

class function TMySingleton.Get: TMySingleton; 
begin 
    Result := MySingletonInstance.Instance; 
end; 
+2

偉大的代碼莫里茨。 –

+0

謝謝。事實上,如果我可以刪除D2009的解決方法,它會更漂亮:) –

0

我更喜歡使用代碼生成器創建單例類。泛型的問題在於,所有代碼都是在內存中生成的,而不是在源文件中生成的。這會增加調試的難度。

4

有一種方法可以隱藏TObject的繼承的「創建」構造函數。儘管無法更改訪問級別,但可以使用另一個具有相同名稱的公共無參數方法隱藏:「創建」。這極大地簡化了Singleton類的實現。看到代碼的簡單:

unit Singleton; 

interface 

type 
    TSingleton = class 
    private 
    class var _instance: TSingleton; 
    public 
    //Global point of access to the unique instance 
    class function Create: TSingleton; 

    destructor Destroy; override; 
    end; 

implementation 

{ TSingleton } 

class function TSingleton.Create: TSingleton; 
begin 
    if (_instance = nil) then 
    _instance:= inherited Create as Self; 

    result:= _instance; 
end; 

destructor TSingleton.Destroy; 
begin 
    _instance:= nil; 
    inherited; 
end; 

end. 

我添加了細節,我原來的職位:http://www.yanniel.info/2010/10/singleton-pattern-delphi.html

相關問題