2008年1月18日金曜日

C#でSingletonパターン

「結婚したら、2人の収入は1つの財布(家計)へ」

共働きの家庭ではよくある話ですが、今回はそんな話。(どんなだ?)


あるクラスのインスタンスが1つしか存在しないことが重要になることがあります。

上記の例だと、財布が1つしか無いことは重要です。

"へそくり"があったら大変ですよね(笑)

「財布は1つであって欲しい("財布"というクラスのインスタンスは1つであって欲しい)」

このように、インスタンスが唯一の存在であることを保証するために、
"Singleton"パターンが有効となります。

"Singleton"パターンを実現するには
  1. インスタンスが1つしか存在しない

  2. 別のクラスから直接インスタンスを生成することが不可

  3. インスタンスへは公開されたポイントからのみアクセス可能
を満たさなければなりません。

実際に"Singleton"パターンを用いてみます。
(コンソールアプリケーションプロジェクトを作成して下さい。)

クラスを1つ追加します。名前は例にならって"Wallet.cs"にします。

/*** Wallet.cs ***/
// 1. 唯一のインスタンスを格納する変数
private static Wallet instance = null;

// 2. 直接インスタンス生成できないように、コンストラクタを不可視に
private Wallet() { }

// 3. インスタンスへのアクセスが可能なアクセスポイント
public static Wallet GetInstance()
{
if (instance == null)
instance = new Wallet();
return instance;
}

ソースコードを見ればわかる通り、他のクラスからは③へのみ、
アクセスすることが許されています。

そして"GetInstance"メソッドでは、変数"instance"が初期値の"null"である場合、
コンストラクタを呼び出すことでインスタンスを生成しています。

コンストラクタは"private"修飾子により、他のクラスからは呼び出せません。

これにより、インスタンスが1つしか生成されないことを保証しています。

全てのクラスは、"GetInstance"メソッドを通じて共通の"instance"変数へ
アクセスすることになります。

これが"Singleton"パターンです。


実際に、"instance"変数へアクセスする様子を見てみましょう。

/*** Wallet.cs ***/
// 財布に入ってるお金
private int money = 0;

// 財布にお金を入れる
public void Income(int value)
{
this.money += value;
}

// 財布の中を見る
public int Look()
{
return money;
}

/*** Program.cs ***/
static void Main(string[] args)
{
new Thread(new ThreadStart(Man)).Start();

new Thread(new ThreadStart(Woman)).Start();
}

private static void Man()
{
Wallet.GetInstance().Income(5000);

Console.WriteLine("僕たちの財布には" + Wallet.GetInstance().Look() + "円入ってるよ。");

Thread.Sleep(5000);
}

private static void Woman()
{
Wallet.GetInstance().Income(2000);

Console.WriteLine("私たちの財布には" + Wallet.GetInstance().Look() + "円入ってるわ。");

Thread.Sleep(5000);
}

実行します。




出力のための遅延や、"Income"メソッドと"Look"メソッドのタイミングによって、
表示している画像の結果と多少違うかも知れませんが、
同じインスタンスへアクセスしていることがわかると思います。

このように、"Singleton"パターンを用いることで、
インスタンスが唯一であることを保証することができました。

モジュール結合度と共通結合
モジュールを分割する基準の1つとして、モジュールの独立性が用いられます。
独立性が高いほど、他のモジュールの影響を受けにくく、拡張性などが高くなります。

モジュールの独立性を評価する指標の1つに、モジュール結合度があります。
モジュール結合度が高いほど、そのモジュール同士の独立性は低くなります。
逆に、結合度が低いほど、独立性は高くなります。

例で挙げたメソッド"Man"と"Woman"は、唯一の"instance"インスタンスが持つ
共通の"money"を参照しています。
このように、共通領域のデータを参照するモジュール同士の場合、
その結合方法は"共通結合"と呼ばれます。
共通結合は、モジュール結合度は2番目に高く、独立性は低めです。
例では、互いに"Income"メソッドを呼び出すことで、共通のデータ"money"へ
影響を与えています。

"Singleton"パターンは共有結合の代表的な実装方法です。



今後も、有用なデザインパターンは随時、記事にしていきたいと思います。

1 件のコメント:

匿名 さんのコメント...

C#初心者です。
Singleton パターンの解説が大変分かりやすかったです。ぜひ、違うパターンの解説もお願いします。