Javaにおけるアダプターパターン:互換性のないシステムのシームレスな統合
別名
- ラッパー
アダプターデザインパターンの目的
Javaにおけるアダプターデザインパターンは、クラスのインターフェースをクライアントが期待する別のインターフェースに変換し、互換性を実現します。
現実世界の例を用いたアダプターパターンの詳細な説明
現実世界の例
メモリカードに写真があり、それをコンピュータに転送する必要があるとします。転送するには、コンピュータのポートと互換性のあるアダプタが必要です。メモリカードをコンピュータに接続できるようにするためです。この場合、カードリーダーがアダプタです。別の例としては、一般的な電源アダプタが挙げられます。3ピンプラグは2ピンプラグのコンセントには接続できません。2ピンプラグのコンセントと互換性を持たせるために電源アダプタが必要です。さらに別の例としては、ある人が話す言葉を他の人に翻訳する通訳が挙げられます。
分かりやすく言うと
アダプターパターンを使用すると、互換性のないオブジェクトをアダプターでラップして、別のクラスと互換性を持たせることができます。
Wikipediaによると
ソフトウェアエンジニアリングでは、アダプターパターンとは、既存のクラスのインターフェースを別のインターフェースとして使用できるようにするソフトウェアデザインパターンです。ソースコードを変更せずに既存のクラスを他のクラスと連携させるためによく使用されます。
Javaにおけるアダプターパターンのプログラム例
Javaにおけるアダプターパターンの例は、互換性のないインターフェースを持つクラスを別のクラスと連携させる方法を示しています。
手漕ぎボートしか使えず、帆走が全くできない船長志望者を考えてみましょう。
まず、インターフェース `RowingBoat` と `FishingBoat` があります。
public interface RowingBoat {
void row();
}
@Slf4j
public class FishingBoat {
public void sail() {
LOGGER.info("The fishing boat is sailing");
}
}
船長は、`RowingBoat` インターフェースの実装で移動できると期待しています。
public class Captain {
private final RowingBoat rowingBoat;
// default constructor and setter for rowingBoat
public Captain(RowingBoat rowingBoat) {
this.rowingBoat = rowingBoat;
}
public void row() {
rowingBoat.row();
}
}
さて、海賊が来ていて、船長は逃げなければならないとしましょう。しかし、利用できるのは漁船だけです。船長が手漕ぎボートのスキルで漁船を操作できるようにするアダプタを作成する必要があります。
@Slf4j
public class FishingBoatAdapter implements RowingBoat {
private final FishingBoat boat;
public FishingBoatAdapter() {
boat = new FishingBoat();
}
@Override
public void row() {
boat.sail();
}
}
これで、`Captain` は `FishingBoat` を使って海賊から逃れることができます。
public static void main(final String[] args) {
// The captain can only operate rowing boats but with adapter he is able to
// use fishing boats as well
var captain = new Captain(new FishingBoatAdapter());
captain.row();
}
プログラムの出力
10:25:08.074 [main] INFO com.iluwatar.adapter.FishingBoat -- The fishing boat is sailing
Javaでアダプターパターンを使用する場合
Javaでアダプターパターンを使用するのは以下の場合です。
- 既存のクラスを使用したいが、そのインターフェースが必要とするものと一致しない場合
- 関連のない、または予期しないクラス、つまり必ずしも互換性のあるインターフェースを持つとは限らないクラスと連携する再利用可能なクラスを作成したい場合
- 既存のサブクラスをいくつか使用する必要があるが、すべてをサブクラス化してインターフェースを適合させるのは現実的でない場合。オブジェクトアダプターは、親クラスのインターフェースを適合させることができます。
- サードパーティライブラリを使用するアプリケーションのほとんどは、アプリケーションとサードパーティライブラリの間の中間層としてアダプターを使用して、アプリケーションとライブラリを切り離しています。別のライブラリを使用する必要がある場合は、アプリケーションコードを変更することなく、新しいライブラリのアダプターのみが必要です。
アダプターパターン Javaチュートリアル
- Javaにおけるアダプターデザインパターンの使用 (Dzone)
- Javaにおけるアダプター (Refactoring Guru)
- Javaにおけるアダプターパターン (Baeldung)
- アダプターデザインパターン (GeeksForGeeks)
アダプターパターンの利点と欠点
クラスアダプターとオブジェクトアダプターは、それぞれ異なる利点と欠点があります。クラスアダプターは、特定のAdapteeクラスにバインドすることによってAdapteeをTargetに適合させます。つまり、クラスとそのすべてのサブクラスを適合させることはできません。このタイプのアダプターでは、AdapterはAdapteeのサブクラスであるため、Adapteeの動作の一部をオーバーライドできます。さらに、Adapteeに到達するための追加のポインタ間接参照を必要とせずに、1つのオブジェクトのみを導入します。
一方、オブジェクトアダプターでは、単一のアダプターで、Adapteeとそのすべてのサブクラスを含む複数のAdapteeを操作できます。このタイプのアダプターは、すべてのAdapteeに同時に機能を追加できます。ただし、Adapteeの動作をオーバーライドするのがより困難になります。Adapteeをサブクラス化し、AdapterがAdaptee自体ではなくこのサブクラスを参照する必要があるためです。
Javaにおけるアダプターパターンの実際の適用例
- Java IOライブラリの `java.io.InputStreamReader` と `java.io.OutputStreamWriter`。
- 異なるGUIコンポーネントインターフェース間の変換を可能にするプラグインまたはアダプターを備えたGUIコンポーネントライブラリ。
- java.util.Arrays#asList()
- java.util.Collections#list()
- java.util.Collections#enumeration()
- javax.xml.bind.annotation.adapters.XMLAdapter