Delphi でシングルトン モードを実装する 2 つの方法
ハオジ
要約 この記事では、シングルトン モードの 2 つの Delphi 実装について説明し、比較分析を行います。
キーワードデザインパターン、シングルトン
Singleton パターンは非常に便利なデザイン パターンです。その目的は、単にクラスのインスタンスを作成し、そのインスタンスへのグローバル アクセス ポイントを提供することです。グローバル変数を使用すると、オブジェクトに簡単にアクセスできますが、複数のオブジェクトのインスタンス化が妨げられるわけではありません。シングルトン パターンの目的は、プログラムのライフサイクル中にインスタンスが 1 つだけ存在するようにすることです。
以下のコードを見てください。
手順 TForm1.Button1Click(送信者: TObject);
var lS1 : TSingleton;
S2: Tシングルトン;
始める
try lS1 := TSingleton.Create ////クラスのコンストラクターを呼び出します。
lS2 := TSingleton.Create; ////クラスのコンストラクターを呼び出します。
//// ...他のコード
ついに
lS1.Free ////オブジェクトを解放します。
lS2.Free ////オブジェクトを解放します。
終わり;
終わり;
上記のコードでは、Create 関数が初めて呼び出されたときに、TS1 がメモリ ストレージ オブジェクトのアドレスを指し、TSSingleton.Create 関数が 2 回目に呼び出されたときに TSingleton クラスがインスタンス化されます。 -インスタンス化された IS2 は、メモリに割り当てられた別のアドレスを指します。シングルトン パターンでは、クラス自体にその唯一のインスタンスの保存を担当させます。
上記のコードでは、ls2 が作成されると、ls1 が指すオブジェクトも指します (つまり、同じメモリ アドレスが割り当てられます)。同様に、ls1 を解放するときにメモリが解放されないようにする必要があります。オブジェクトは ls2 によっても参照されます。これにより、プログラムのライフサイクル中にクラス インスタンスが 1 つだけ存在することが保証されます。
「デザイン パターン」の C++ のサンプル コードでは、C++ の静的メンバー変数を使用してインスタンスを保存し、保護されたコンストラクター関数を使用します。ただし、Delphi には静的メンバ変数が存在しないため、このシングルトン モード例の方法をそのまま使用することはできません。以下では、DELPHI がシングルトン モードを実装するためのいくつかの方法を分析します。
1つ。 2 つの Tobject 仮想関数のオーバーライドに基づくメソッド
クラス関数 NewInstance: TObject 仮想;
プロシージャ FreeInstance;
NewInstance 関数は、クラス オブジェクトの作成時にオブジェクトにメモリを割り当てる役割を果たしますが、FreeInstance は逆にメモリを解放します。
。
前者はオブジェクトが構築されるときに呼び出され、後者はオブジェクトが破棄されるときに呼び出されます。
2 つのグローバル変数を使用して、シングルトン オブジェクトとオブジェクトの参照カウントを保持します。
varInstance: TSingleton = nil;
RefCount : 整数 = 0;
TSingleton クラスのユニット:
////---------------------------------------------- -- ------------------------
////
ユニット uSingleton;
インタフェース
タイプ
TSingleton = クラス(TObject)
公共
クラス関数 NewInstance: TObject オーバーライド;
プロシージャ FreeInstance; ////基本クラス関数をオーバーライドします。
class function RefCount: Integer;////現在の参照カウントを返す
終わり;
//// グローバル変数の宣言
変数
インスタンス: TSingleton = nil;
RefCount: 整数 = 0;
実装
{TSシングルトン}
プロシージャ TSingleton.FreeInstance;
始める
Dec( RefCount );////参照カウントを減らす
if (RefCount = 0) then////0 ですか、そうであればメモリを解放します
始める
インスタンス := nil;
//// シングルトンクラスのプライベート変数を解放する
////…
継承された FreeInstance;
終わり;
終わり;
クラス関数 TSingleton.NewInstance: TObject;
始める
if ( Assigned( Instance ) ではない) then
始める
インスタンス := TSingleton(継承された NewInstance);
////プライベート変数の初期化の例:
//// インスタンス.変数名 := 値;
終わり;
結果 := インスタンス ;
Inc( RefCount );
終わり;
クラス関数 TSingleton.RefCount: 整数;
始める
結果 := RefCount;
終わり;
終わり。
////---------------------------------------------- -- ------------------------
////
TSingleton のコンストラクターが呼び出されると、オーバーライド NewInstance 関数が呼び出され、NewInstance がメモリを割り当ててコンストラクターに返します。このようにして、オーバーライド NewInstance 関数を通じて、Create 関数が TSingleton オブジェクトのみをインスタンス化できるようにします。 (何回呼び出しても) Create は最初に割り当てられたメモリ アドレスのみを返します)。同時に、RefCount 変数は、オブジェクトに対する参照の数を保持します。
テストコードを見てみましょう
プロシージャ TForm1.Button1Click(送信者: TObject);
変数
lS1、ls2: Tシングルトン;
Ob1、Ob2: オブジェクト。
始める
lS1 := TSingleton.Create;
ShowMessage(IntToStr(RefCount)); //// Ref_Count = 1
lS2 := TSingleton.Create;
ShowMessage(IntToStr(RefCount)); //// Ref_Count = 2
Ob1 := TObject.Create;
Ob2 := Tobject.Create;
lS1 = ls2 の場合
ShowMessage('アドレスが等しい') //// lS1 = lS2
それ以外
ShowMessage('アドレスが等しくない');
Ob1 = Ob2 の場合
ShowMessage('アドレスが等しい')
それ以外
ShowMessage('アドレスが等しくない') //// Ob1 <> Ob2
終わり;
プログラムがデストラクターを呼び出すとき (つまり、FREE 関数が呼び出されるとき)、デストラクターは FreeInstance 関数を呼び出して、コンストラクターによって割り当てられたメモリを解放します。 Override の FreeInstance 関数は、参照カウントが 0 に達した場合にのみシングルトン モード オブジェクトのメモリが解放されるようにします。
テストコードは次のとおりです。
変数
lS1: Tシングルトン;
IS2: Tシングルトン;
始める
試す
lS1 := TSingleton.Create; ////クラスのコンストラクターを呼び出します。
lS2 := TSingleton.Create; ////クラスのコンストラクターを呼び出します。
//// ...他のコード
ついに
lS1.Free; ////ここでは、最初にオーバーライドによって定義された FreeInstance を呼び出します。
////この時点ではRefCountが1減算されて1なのでシングルトンオブジェクトは解放されていない。
lS2.Free; ////dec(RefCount)= 0 はシングルトン オブジェクトを解放します。
終わり;
終わり;
上記のシングルトン パターン実装メソッドは、クラス自体が一意のインスタンスを保存する責任があることを認識する良い方法です (新しいオブジェクトを作成するリクエストをインターセプトすることによって - 「デザイン パターン」を参照してください。このメソッドの使用には特別な制限はありません) TSingleton クラスの - - プログラマは、Create 関数と Free 関数を自由に呼び出すことができます。
このモードの欠点は、TSSingleton クラスを親クラスとして継承してサブクラスを生成できないことです。継承によって 2 つのサブクラスが生成される場合、作成中に生成されるオブジェクトは 1 つだけです。
プロシージャ TForm1.Button1Click(送信者: TObject);
変数
lS1: サブカテゴリ 1。
ls2: サブカテゴリ 2。
始める
lS1 := サブクラス 1.作成;
lS2 := Subclass 2.Create; ////サブクラス 2 は作成されません。lS2 は lS1 が指すメモリを指します。
////つまり、lS1 = ls2end;
二。 「デザイン パターン」の例の Delphi 実装
「デザインパターン」の実装例は、プライベートコンストラクター関数を通じてオブジェクトインスタンスを1つだけ制御する例です。ただし、指定された C++ コード実装では、オブジェクトがどのように解放されるかがわかりません。 Delphi では Create 関数をプライベート化することはできません。Create 関数を置き換える新しい関数を定義し、親クラスの Create 関数を保護します。コードは次のとおりです
:
////---------------------------------------------- -- ------------------------
////
ユニット uSingletonUnit;
インタフェース
用途
クラス、SysUtils。
タイプ
TCSingleton = class(TComponent) ////Tcomponent クラスから継承。
プライベート
コンストラクター CreateInstance(AOwner: TComponent); ////Owner パラメーターを渡します。
//// このようにして、TCSingleton クラス オブジェクトは Owner とともに破棄されます (TCSingleton オブジェクトを破棄する責任はオーナーにあります)
公共
コンストラクター Create(AOwner: TComponent);
クラス関数インスタンス(AOwner: TComponent): TCSingleton;
終わり;
変数
gCSingleton: TCSingleton; //// グローバル変数;
実装
{TCシングルトン}
コンストラクター TCSingleton.Create(AOwner: TComponent);
始める
////Create関数の関数をシールドする
raise Exception.CreateFmt('インスタンスのみを通じてクラス %s にアクセス',
[クラス名]);
終わり;
コンストラクター TCSingleton.CreateInstance(AOwner: TComponent);
始める
////新しく定義されたコンストラクターは Private です
継承された Create(AOwner);
終わり;
クラス関数 TCSingleton.Instance(AOwner: TComponent): TCSingleton;
始める
割り当てられていない場合(gCSingleton)
gCSingleton := TCSingleton.CreateInstance(AOwner);
結果 := gCSingleton;
終わり;
終わり。
////---------------------------------------------- -- ----------------------------/
/
上記の実装クラスの使用中に、プログラマはシングルトン モード オブジェクトの破棄を考慮する必要はありません。 Create を呼び出すことはできません。Instance 関数を呼び出してオブジェクトのインスタンスを取得し、シングルトンの所有者をパラメーターとして関数に渡す必要があります。この実装メソッドは、基本クラスとして継承し、状態パターンのシングルトン (参考文献 4 を参照) で使用して、実行時のポリモーフィズムを実現できます。
三つ。結論
シングルトン モードの Delphi 実装はインターネットでも見つけることができます。この記事では他にも 2 つの実装方法があります。
最も一般的でシンプルです。同時に、他の方法の考え方も上記 2 つの方法と非常に似ています。