デザインパターン入門 | Adapter(アダプタ)パターン

「Adapter(アダプタ)パターン」は、あるクラスの処理を利用する際に、そのクラスとそのクラスを利用する対象の間に「アダプタ」と呼ばれるクラスを作り処理の「中継ぎ」をする役割を担わせるプログラムパターンです。

しかし「言葉だけでは、なかなかイメージが涌かない」という方も多い人のではないでしょうか。

今回は「日常の事例」をテーマに「Adapter(アダプタ)パターン」について見ていきたいと思います。

身近な事例で考える「Adapter(アダプタ)パターン」

車に乗っている方なら馴染みのある「シガーソケット」ですが、家庭用の電化製品を使おうとすると、「インバーター」と呼ばれる電圧変換器を間に設置する必要があります。

例えば、「家庭用の電気ハンドクリーナ」を車の清掃に使おうとすると、

アダプターパターン

という接続が必要になります。

「Adapter(アダプタ)パターン」では、それぞれの役割ごとに

Target
必要なメソッドを持っている「インターフェース・クラス」。メソッドの実装方法は定めず「抽象メソッド」を定義している。
Adapter
変換するための機能を持つ「クラス」
Adaptee
利用したいメソッドを持っているクラス

Targetとなる「シガーソケット」のインターフェースをJava言語で作成すると下記のようになります。

package SampleAdapter;

public interface CigarSocketTarget {
    public abstract void cleanUp();
}

「cleanUpメソッド」は「掃除をする」という内容の抽象メソッドですが、「どのように掃除をする」かは何も定義されていません。

次にAdapteeとなる「ハンドクリーナ」となるクラスを作っていきます。

package SampleAdapter;

public class HandCleanerAdaptee {
    public void handCleanUp() {
        System.out.println("ハンドクリーナーで車内を掃除しました。");
    }
}

次にAdapterとなる「インバーター」となるクラスを作っていきます。

package SampleAdapter;

public class InverterAdapter extends HandCleanerAdaptee implements CigarSocketTarget{
    public void cleanUp() {
        handCleanUp();
    }
}

このクラスでは、「ハンドクリーナ(Adaptee)」を表すクラスを継承し、「シガーソケット(Target)」を表すインターフェースを実装しています。

「CigarSocketTarget」クラスの抽象メソッド「cleanUp」の内容を実装していますが、その中身は、「HandCleanerAdaptee」クラスの「handCleanUp」メソッドを呼び出しています。

アダプターパターン3

このように「Adapter(アダプター)」は「処理の中継ぎ」を行い、「Target」と「Adaptee」を繋ぐ「架け橋」となります。

これらのクラス・インターフェースを利用する「mainメソッド」を持つクラスは下記のようになります。

package SampleAdapter;

public class Main {
    public static void main(String[] args) {
        InverterAdapter ia = new InverterAdapter();
        ia.cleanUp();
    }
}

実行結果は、

ハンドクリーナーで車内を掃除しました。

のようになります。

「Adapter(アダプタ)パターン」にはもう一つ「委譲」を利用したパターンもよく知られています。

「委譲」を利用したAdapter(アダプタ)パターン

「InverterAdapter」クラスと「HandCleanerAdaptee」クラスの間に継承関係を作らずに、「InverterAdapter」クラスが「HandCleanerAdaptee」クラスのインスタンスをフィールドに持ちます。

アダプターパターン4

保持しているインスタンス経由で、「HandCleanerAdaptee」クラスの「handCleanUpメソッド」を呼び出します。

このように実装することで、「InverterAdapter」クラスはほかのクラスを継承することができるようになります。

Java言語の場合は、複数のクラスを継承できない(単純継承)という決まりがあるため、このように実装することで、「InverterAdapter」クラスがほかのクラスを継承することができるようになります。

「InverterAdapter」クラスのみが変更されるため、下記のように修正を行います。

package SampleAdapter;

public class InverterAdapter extends CigarSocketTarget {
    private HandCleanerAdaptee adpt;

    public InverterAdapter() {
        this.adpt = new HandCleanerAdaptee();
    }

    public void cleanUp() {
        adpt.handCleanUp();
    }
}

コンストラクタで、「adpt」フィールドに「HandCleanerAdaptee」クラスのインスタンスを格納しています。

「cleanUp」メソッドが実行されると、「adpt」フィールド経由で「handCleanUp」メソッドが呼び出されます。

今回は、

  • シガーソケット
  • インバーター
  • ハンドクリーナ

をテーマにしましたが、「直接は使えない」クラスを「間接的に使う」必要がある場合は、「Adapter(アダプタ)パターン」の利用を検討してみはいかがでしょうか。

HOMEへ