はじめに
本記事は、デザインパターン学習中の私が学習中に詰まったポイント、間違えてしまったポイントをもとに学んだことを書いたものです。
個人の理解に基づいておりますため、間違いがあれば教えていただけると幸いです。
★9ヶ月前くらいにデザインパターンの勉強のために執筆し、社内で公開していた記事をこちらにも投稿したものとなります。
Singletonのそっくりさん?
Singletonパターンを学習していると、「Monostateパターン」というパターンとの違いを問われることがあります。
どちらもオブジェクトの唯一性を保証するパターンですが、ざっくりいえば、
- Singletonはインスタンスが一個しか作れない
- Monostateはインスタンスが複数作れるけれど全部のインスタンスが同じ振る舞いをする
という違いがあります。
ところで、このMonostateパターンは、GoFのデザインパターン23種の中には含まれていません。自分も、Singletonパターンの学習時に調べて初めて知り、
「フーン……でも23パターンの中にないしな……」
と終わりにするところでした。
ですが、デザインパターンとは 「設計の問題にぶつかったときに対処するためのノウハウ」。ノウハウはいっぱい知っていて損はないはず。
そこで、脇道に反れるようですが、Monostateパターンについて自分なりに調べてみました。
全部のインスタンスが同じ振る舞いをする
ってどういうこと?
これは、具体的にいえばクラスのもつフィールドがすべてstaticであるということです。
イメージは以下のような感じでしょうか。
public class MonostateSample
{
/// <summary>
/// 言語
/// </summary>
private static string language = "ja-JP";
/// <summary>
/// フォントサイズ
/// </summary>
private static int fontSize = 14;
/// <summary>
/// 言語を設定する
/// </summary>
public void SetLanguage(string language)
{
this.language = language;
}
// ...
}
最初にも述べた通り、MonostateパターンはSingletonパターンとは異なり、インスタンスを複数作ることができます。そのため、上記のMonostateSampleクラスのインスタンスはいくらでも作ることができます。
ただし、どれだけインスタンスを作成しても、s_Languageフィールドおよびs_FontSizeフィールドの値はどのインスタンスにおいても同じになります。
例えば、とあるMonostateSampleクラスのインスタンスについて、SetLanguageメソッドを使用してs_Languageフィールドの値を変更すると、すべてのMonostateSampleクラスのインスタンスのs_Languageフィールドの値が同様に変更されます。
ちなみに、上のコードをSingletonパターンに書き換えると以下のようになります。
public class SingletonSample
{
/// <summary>
/// インスタンス
/// </summary>
private static SingletonSample singletonSample;
/// <summary>
/// 言語
/// </summary>
private string language = "ja-JP";
/// <summary>
/// フォントサイズ
/// </summary>
private int fontSize = 14;
/// <summary>
/// コンストラクタ
/// </summary>
private SingletonSample()
{
}
/// <summary>
/// インスタンスを取得する
/// </summary>
public static SingletonSample GetInstance()
{
return singletonSample;
}
/// <summary>
/// 言語を設定する
/// </summary>
public void SetLanguage(string language)
{
this.language = language;
}
// ...
}
コード上の違いとしては、
- privateかつstaticな、クラス自身の型のフィールド(singletonSampleフィールド)がある
- privateなコンストラクタ(インスタンスを作成するときに自動的に呼ばれるメソッド)がある
- インスタンスを取得するためのstaticメソッド(GetInstanceメソッド)がある
の3点が挙げられます。
この3点がSingletonパターンの本質といってもよいと思います。他から勝手にクラスのインスタンスを作られず(コンストラクタがprivateのため、当クラス内でのみインスタンスを作成できる)、インスタンスを使いたいときは公開されているGetInstanceメソッド(フィールドに保持している当クラスのインスタンスを返す)を必ず呼ぶ必要があります。この構造が、「クラスのインスタンスが1つしかない」ことを保証しているのです。
まとめ
パターンの違いは、それぞれのクラスを継承した場合にもあります。
- Monostateパターンのクラスを継承した場合、staticにしたフィールドは派生クラスでも親クラスと同じ振る舞いをさせることができる(前章の例だとフィールドをprivateにしているので、できない。publicやprotectedにアクセシビリティを変更すれば可能)
- Singletonパターンのクラスを継承した場合、派生クラスもSingletonになるわけではない(Singletonにしたければ、派生クラスにもSingletonの構造を作ってあげる必要がある)
派生先まで「同じ振る舞いをする」というところが特徴的ですね。
Monostateパターンは、Singletonパターンのように構造的にインスタンスの数を1つに強制するのではなく、すべてのインスタンスに同じ振る舞いを強制することで、実質的にオブジェクトが1つの状態しかとり得ないようにしているんですね。
◆ ◆ ◆
Monostateパターンについて説明する記事にしたかったのですが、結局Singletonパターンについてもかなり行数を割くこととなりました。また、どちらのパターンにも静的メンバ(static)が登場するため、改めて「staticってどういう動作するんだっけ」と調べ直すことにもなりました。
記事を書くことは本当に復習の良い機会になりますね。
最後まで読んでいただき、ありがとうございました。
以下は前回までのデザインパターンの記事です。よければ合わせてどうぞ。
【デザインパターン】悪戦苦闘!デザインパターン ~State・Strategy編~
【デザインパターン】悪戦苦闘!デザインパターン ~Proxy・Flyweight編~
【デザインパターン】悪戦苦闘!デザインパターン ~Mediator・Observer編~
【デザインパターン】悪戦苦闘!デザインパターン ~SingletonとMonostate編~