はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【C#】インターフェイスの変換を行うAdapterパターンを学ぶ

はじめに

今回はインターフェイスの変換を行うAdapterパターンについて紹介したいと思います。

f:id:hanaaaaaachiru:20210310225147p:plainf:id:hanaaaaaachiru:20210310232716p:plain
クラス図(左 : 継承を使ったもの, 右 : 委譲を使ったもの)

概要

wikipediaには以下のように書かれていました。

Adapter パターンを用いると、既存のクラスに対して修正を加えることなく、インタフェースを変更することができる。

Adapter パターン - Wikipedia

さすがWikipedia、簡潔で分かりやすいです。

仕組み自体は簡単で、意味も分かりやすいですが非常に有用なデザインパターンの一つだと思うので、是非これを機にマスターしてみてはどうでしょうか。

登場人物

名前 意味
Adaptee 既に用意されているメソッドを持っている役
Adapter Target(目的のインターフェイス)の要件を満たすようにAdapteeを利用する役
Target 必要とするインターフェイスを定義している役
f:id:hanaaaaaachiru:20210310225147p:plainf:id:hanaaaaaachiru:20210310232716p:plain
クラス図(左 : 継承を使ったもの, 右 : 委譲を使ったもの)

継承を使ったパターン

Adapterパターンには2つの手法があり、そのうちの一つが継承を使ったパターンになります。

ただ最初に言ってしまうと、こちらのやり方を用いる機会は非常に少なく、次に紹介する委譲を使ったパターンを利用することがほとんどのようです。

なぜかというと継承はクラス間に固い結びつきがあるのに対し,委譲はゆるやかな結びつきにすることができるからだと私は考えています。

class Client
{
    private static void Main(string[] args)
    {
        Target target = new Adapter();

        target.TargetMethod1();
        target.TargetMethod2();
    }
}

/// <summary>
/// 既に実装されていて,テストも十分なされているようなクラス
/// </summary>
public class Adaptee
{
    public void MethodA()
    {
        Console.WriteLine("Method A");
    }

    public void MethodB()
    {
        Console.WriteLine("Method B");
    }
}

/// <summary>
/// Adapteeのインターフェイスを目的のインターフェイスへ変換するための変換装置
/// </summary>
public class Adapter : Adaptee, Target
{
    public void TargetMethod1()
    {
        // Adapteeに定義されているメソッド等を活用してTargetの要件満たすように実装
        MethodA();
    }

    public void TargetMethod2()
    {
        MethodB();
    }
}

/// <summary>
/// 目的とするインターフェイス
/// </summary>
public interface Target
{
    void TargetMethod1();
    void TargetMethod2();
}
f:id:hanaaaaaachiru:20210310225147p:plain
クラス図

委譲を使ったパターン

もう一つの手法は委譲を使ったパターンです。

class Client
{
    private static void Main(string[] args)
    {
        Target target = new Adapter();

        target.TargetMethod1();
        target.TargetMethod2();
    }
}

/// <summary>
/// 既に実装されていて,テストも十分なされているようなクラス
/// </summary>
public class Adaptee
{
    public void MethodA()
    {
        Console.WriteLine("Method A");
    }

    public void MethodB()
    {
        Console.WriteLine("Method B");
    }
}

/// <summary>
/// Adapteeのインターフェイスを目的のインターフェイスへ変換するための変換装置
/// </summary>
public class Adapter : Target
{
    private Adaptee _adaptee;

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

    public void TargetMethod1()
    {
        // Adapteeに定義されているメソッド等を活用してTargetの要件満たすように実装
        _adaptee.MethodA();
    }

    public void TargetMethod2()
    {
        _adaptee.MethodB();
    }
}

/// <summary>
/// 目的とするインターフェイス
/// </summary>
public interface Target
{
    void TargetMethod1();
    void TargetMethod2();
}
f:id:hanaaaaaachiru:20210310232716p:plain
クラス図

クラス図ではTargetが抽象クラスになっていますが,別にインターフェイスでもどちらでも大丈夫です。

さいごに

余談ですがGoFのデザインパターン本で用いられている「委譲」は実は誤用で,本当は転送というそうです。
qiita.com

私の記事でも委譲と書いておきながら、実は転送を指していることも多々あるので注意していただけると幸いです。(まあC#で委譲といったら、ほぼ転送を指すような気がしますが)

ではまた。