デザインパターン入門 | 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」メソッドを呼び出しています。
このように「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」クラスのインスタンスをフィールドに持ちます。
保持しているインスタンス経由で、「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(アダプタ)パターン」の利用を検討してみはいかがでしょうか。