シングルトンパターンを活用したアプリケーション設計ガイド

2024年10月29日

本資料では、シングルトンパターンの特徴とその活用方法について説明します。シングルトンパターンは、アプリケーション全体で共有されるリソースやデータを一元管理するために使用されます。特に、遅延初期化やスレッドセーフ性が求められる場面で有効です。

目的

シングルトンパターンの利点と具体的な利用シナリオを理解し、アプリケーション設計において適切にパターンを適用するための知識を提供します。

シングルトンパターンの特徴

  • インスタンスの制御: クラスのインスタンスを一つに制限します。これにより、アプリケーション全体で一貫した状態やリソースを管理できます。
  • 遅延初期化: インスタンスは必要になるまで作成されず、リソースを無駄にしません。
  • スレッドセーフ性: 適切な実装を行うことで、マルチスレッド環境でも安全に使用できます。
  • モック可能: インターフェースを使用することで、モックを作成しやすく、テストが容易です。

1. 設定管理(Configuration Manager)

シナリオ

アプリケーション全体で共通の設定を管理する場合に、設定情報を一元的に管理します。設定は起動時に一度だけ読み込まれ、必要に応じてアクセスされます。

実装例

public class ConfigurationManager
{
    private static ConfigurationManager instance;
    private static readonly object lockObject = new object();
    public Dictionary<string, string> Settings { get; private set; }

    private ConfigurationManager()
    {
        Settings = new Dictionary<string, string>
        {
            { "AppName", "My Application" },
            { "Version", "1.0.0" },
            { "MaxUsers", "100" }
        };
    }

    public static ConfigurationManager Instance
    {
        get
        {
            if (instance == null)
            {
                lock (lockObject)
                {
                    if (instance == null)
                    {
                        instance = new ConfigurationManager();
                    }
                }
            }
            return instance;
        }
    }

    public string GetSetting(string key)
    {
        if (Settings.ContainsKey(key))
        {
            return Settings[key];
        }
        return null;
    }
}

利点

  • 一度だけ読み込まれるため、リソースの無駄を防ぎます。
  • グローバルにアクセス可能で、スレッドセーフです。

2. キャッシュ管理(Cache Manager)

シナリオ

計算結果や取得したデータをキャッシュとして保存し、再利用する場合に使用します。

実装例

public class CacheManager
{
    private static CacheManager instance;
    private static readonly object lockObject = new object();
    private Dictionary<string, object> cache;

    private CacheManager()
    {
        cache = new Dictionary<string, object>();
    }

    public static CacheManager Instance
    {
        get
        {
            if (instance == null)
            {
                lock (lockObject)
                {
                    if (instance == null)
                    {
                        instance = new CacheManager();
                    }
                }
            }
            return instance;
        }
    }

    public void AddToCache(string key, object value)
    {
        if (!cache.ContainsKey(key))
        {
            cache[key] = value;
        }
    }

    public object GetFromCache(string key)
    {
        if (cache.ContainsKey(key))
        {
            return cache[key];
        }
        return null;
    }
}

利点

  • キャッシュデータの一貫した管理が可能です。
  • スレッドセーフなキャッシュ管理により、同時アクセスが安全です。

3. リソースプール管理(Resource Pool Manager)

シナリオ

データベース接続などのリソースを効率的に管理し、必要に応じて再利用する場合に使用します。

実装例

public class DatabaseConnectionPool
{
    private static DatabaseConnectionPool instance;
    private static readonly object lockObject = new object();
    private Queue<DatabaseConnection> pool;

    private DatabaseConnectionPool()
    {
        pool = new Queue<DatabaseConnection>();
        for (int i = 0; i < 5; i++)
        {
            pool.Enqueue(new DatabaseConnection());
        }
    }

    public static DatabaseConnectionPool Instance
    {
        get
        {
            if (instance == null)
            {
                lock (lockObject)
                {
                    if (instance == null)
                    {
                        instance = new DatabaseConnectionPool();
                    }
                }
            }
            return instance;
        }
    }

    public DatabaseConnection GetConnection()
    {
        lock (lockObject)
        {
            if (pool.Count > 0)
            {
                return pool.Dequeue();
            }
            else
            {
                return new DatabaseConnection();
            }
        }
    }

    public void ReturnConnection(DatabaseConnection connection)
    {
        lock (lockObject)
        {
            pool.Enqueue(connection);
        }
    }
}

public class DatabaseConnection
{
    // 実際の接続の処理
}

利点

  • リソースの効率的な利用が可能です。
  • スレッドセーフなリソース管理により、競合を防ぎます。

4. イベント管理(Event Manager)

シナリオ

アプリケーション全体でイベントの発行や購読を一元管理し、複数のモジュール間でのイベントのやり取りを簡単に行いたい場合に使用します。

実装例

public class EventManager
{
    private static EventManager instance;
    private static readonly object lockObject = new object();
    private Dictionary<string, Action<object>> eventTable;

    private EventManager()
    {
        eventTable = new Dictionary<string, Action<object>>();
    }

    public static EventManager Instance
    {
        get
        {
            if (instance == null)
            {
                lock (lockObject)
                {
                    if (instance == null)
                    {
                        instance = new EventManager();
                    }
                }
            }
            return instance;
        }
    }

    public void Subscribe(string eventName, Action<object> listener)
    {
        if (!eventTable.ContainsKey(eventName))
        {
            eventTable[eventName] = listener;
        }
        else
        {
            eventTable[eventName] += listener;
        }
    }

    public void Unsubscribe(string eventName, Action<object> listener)
    {
        if (eventTable.ContainsKey(eventName))
        {
            eventTable[eventName] -= listener;
            if (eventTable[eventName] == null)
            {
                eventTable.Remove(eventName);
            }
        }
    }

    public void Publish(string eventName, object data)
    {
        if (eventTable.ContainsKey(eventName))
        {
            eventTable[eventName]?.Invoke(data);
        }
    }
}

利点

  • イベントの発行と購読が一貫して管理でき、コードの複雑さを減らします。
  • スレッドセーフにイベントを扱うことが可能です。

結論

シングルトンパターンは、特定のリソースや状態をアプリケーション全体で一元管理し、効率的かつ安全に利用するための強力な設計パターンです。設定管理、キャッシュ管理、リソースプール、イベント管理など、様々な場面で活用することができます。これらの特徴を理解し、適切にパターンを適用することで、堅牢で拡張性の高いアプリケーションを設計することができます。