🔥

Adapterパターン/Singletonパターン

2024/10/14に公開

デザインパターンの中で、AdapterパターンとSingletonパターンはよく使われる重要なパターンです。今回は、この2つのパターンを簡単な例と共に記述していきます。

Adapterパターン

Adapterパターンは、異なるインターフェース同士をつなぐ役割を持ちます。オブジェクトの動作は望み通りであっても、使いたいインターフェースが違うときに新たなインターフェースを作成して対応することが目的です。

具体例

コンセントで考えてみましょう。日本のコンセントは2本のピンですが、海外の一部では3本ピンのコンセントが必要です。ここでAdapterが登場し、3本ピンを2本ピンに変換する役割を果たします。
プログラムでも同じように、既存のクラス(Adaptee)が期待されるインターフェース(Target)と異なる場合に、その橋渡しを行うのがAdapterパターンです。

// 期待されるインターフェース(Target)
public interface ITarget
{
    void Request();
}

// 既存のクラス(Adaptee)
public class Adaptee
{
    public void SpecificRequest()
    {
        Console.WriteLine("Adaptee: SpecificRequest() called.");
    }
}

// Adapterクラス:Targetを実装し、Adapteeを内部に持つ
public class Adapter : ITarget
{
    private Adaptee _adaptee;

    public Adapter(Adaptee adaptee)
    {
        _adaptee = adaptee;
    }

    public void Request()
    {
        // Adapteeのメソッドを呼び出して対応
        _adaptee.SpecificRequest();
    }
}

// 使用例
class Program
{
    static void Main(string[] args)
    {
        Adaptee adaptee = new Adaptee();
        ITarget target = new Adapter(adaptee);
        target.Request();  // Adapteeのメソッドが呼ばれる
    }
}

この例では、AdapterがAdapteeのSpecificRequestメソッドをRequestメソッドに変換し、クライアントはITargetを使ってAdapteeの機能を間接的に利用できます。

Singletonパターン

Singletonパターンは、クラスのインスタンスがシステム内でただ一つであることを保証し、それにアクセスするためのグローバルな方法を提供します。例えば、アプリケーション全体で共有する設定情報などに適しています。

具体例

設定ファイルやデータベース接続など、複数のインスタンスが生成されると矛盾や無駄が発生するものにSingletonを使うと便利です。1つのインスタンスを作り、それを再利用することでリソースの節約やデータの一貫性を保つことができます。

public class Singleton
{
    // 自身のインスタンスを保持するフィールド
    private static Singleton _instance;

    // 外部からインスタンス化できないようにコンストラクタをprivateに
    private Singleton() { }

    // インスタンスを取得するためのメソッド
    public static Singleton Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new Singleton();
            }
            return _instance;
        }
    }

    public void Logic()
    {
        Console.WriteLine("Singletonクラスが呼ばれました");
    }
}

// 使用例
class Program
{
    static void Main(string[] args)
    {
        Singleton singleton1 = Singleton.Instance;
        Singleton singleton2 = Singleton.Instance;

        // 同じインスタンスであることが確認できる
        if (singleton1 == singleton2)
        {
            Console.WriteLine("同じインスタンスです");
        }

        singleton1.Logic();
    }
}

スレッドセーフな実装

もし、マルチスレッド環境でこのパターンを使用する場合は、インスタンス生成時に排他制御が必要です。以下のようにlockを使った実装が推奨されます。

public class Singleton
{
    private static Singleton _instance;
    private static readonly object _lock = new object();

    private Singleton() { }

    public static Singleton Instance
    {
        get
        {
            lock (_lock)
            {
                if (_instance == null)
                {
                    _instance = new Singleton();
                }
                return _instance;
            }
        }
    }
}

staticクラスを使った実装

ここまで長々と書いてきましたが、SingletonはC#やJavaではstaticクラスを実装すればそれでSingletonとなります。しかし、staticだと継承できなかったりと若干のデメリットもありますので、利用の際は気をつけましょう。

Discussion