2014年08月15日

シングルトン

シングルトン

シングルトンとは、あるクラスのインスタンスが一つであるようにする実装パターン。
外部ファイルのような特定のリソースにアクセスする場所を一つにしたい場合や、
固定値のように複数あることに意味がないものを定義する場合などに使用する。

静的クラス

C#(.NET)では、静的クラスという言語仕様レベルでサポートされている機能がある。

    // static指定する。メンバも全員がstatic。
    public static class SingletonSample1
    {
       // constはstaticと違うけど行ける
        public const string Name = "サンプル";

        public static int Number { get { return 0; } }

        public static string DisplayYear(DateTime datetime)
        {
            return datetime.ToString("yyyy年");
        }
    }

実装パターン

シングルトンの実装は、ほとんど上記の静的クラスを用いることで事足りる。
しかし、いくつかの場合においては静的クラスを用いない方法が必要となる場合もある。
 継承を使いたい場合:静的クラスはクラスやインタフェースを継承できない。
 ライフタイムを管理したい場合:静的クラスはメンバのいづれかかが最初に参照またはメソッドコールされた場合にインスタンス化され、プロセス終了まで生存するため任意の契機で生成・消滅させることができない。
 インスタンスを代替したい場合:特定の時機においてインスタンスは最大1だが、なんらかの契機でそれを入れ代える必要がある場合。
 インスタンスが代替可能性があることをコード上で示唆したい場合:機能として静的クラスでもよいのだが、拡張性や可読性を考慮した場合。

    interface ISampleContext
    {
        void DoSomething();
    }

    public class SingletonContext : ISampleContext
    {
        // インスタンス保持領域
        private static SingletonContext _context;

        // 静的コンストラクタ
        static SingletonContext() { _context = new SingletonContext(); }

        // コンストラクタ ※プライベート指定としてクラス外がらのインスタンス化を抑止する
        private SingletonContext(){}

        // 本クラス外からのメンバの使用はこの公開プロパティを介して行う
        public static SingletonContext Instance{get { return _context; }}
 
        public void DoSomething()
        {
            throw new NotImplementedException();
        }
    }


※上記コードの間違いを修正しました。18行目にstaticの指定が抜けてました。(2016/12/23)

基本的な実装は上記のようにコンストラクタをPrivate指定することにより自クラス外からのインスタンス化を抑止し、Staticフィールドにより一つだけインスタンスを保持してそれを外部からアクセスできるように実装する。新しい文法だと上記コードは以下のようにも記述できる。

    interface ISampleContext
    {
        void DoSomething();
    }

    public class SingletonContext : ISampleContext
    {
        // コンストラクタ ※プライベート指定としてクラス外がらのインスタンス化を抑止する
        private SingletonContext(){}

        // 本クラス外からのメンバの使用はこの公開プロパティを介して行う
        public static SingletonContext Instance{ get;} = new SingletonContext();
 
        public void DoSomething()
        {
            throw new NotImplementedException();
        }
    }

別案

文法上の制約とはならないが、インスタンスの生成管理を行うファクトリクラスを別途実装して、必ずそこからインスタンスを取得するようにする。ファクトリでは同一のTYPEに対し複数のインスタンスを生成しないように、二度目以降はキャッシュしてあるインスタンスを返却する。
実装例
    public static class Factory
    {
        private static Dictionary<Type, object> _dic = new Dictionary<Type, object>();

        public static T GetInstance<T>() where T : new()
        {
            T obj;

            if(_dic.TryGetValue(typeof(T),out obj))
            {
                return obj;
            }
            else
            {
                obj = new T();

                _dic.Add(typeof(T), obj);

                return obj;
            }
        }
    }
posted by RR at 18:26 | Comment(0) | デザインパターン | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

※ブログオーナーが承認したコメントのみ表示されます。