デザインパターン_構造パターン-《アダプターパターン》
ダークホースプログラマによるJavaデザインパターンの詳細解説、23のJavaデザインパターン(図表+フレームワークソースコード解析+実戦)をノートとしてまとめています。
概要
ヨーロッパ諸国に旅行する場合、そのソケットは下の写真の一番左にあり、これがヨーロッパ標準です。使用するプラグは下図の一番右のプラグです。したがって、ラップトップや携帯電話をローカルで直接充電することはできません。したがって、ソケットコンバータが必要です。コンバータの最初の側はローカルソケットに差し込まれ、もう一方の側は充電用であり、プラグをローカルで使用できるようになります。このような例は世の中にたくさんあり、携帯電話の充電器 (220v を 5v に変換)、カードリーダーなどが実際にアダプター モードを使用しています。
意味
- クラスのインターフェイスをクライアントが必要とする別のインターフェイスに変換し、互換性のないインターフェイスのために連携できなかったクラスが連携できるようにします。
- アダプタ モードは
类适配器模式
と对象适配器模式
に分かれており、前者のクラス間の結合は後者よりも高く、プログラマは既存のコンポーネント ライブラリ内の関連コンポーネントの内部構造を理解する必要があるため、アプリケーションは比較的少なくなります。
構造
アダプター パターンには次の主な役割が含まれます。
- ターゲット (Target) インターフェイス: 現在のシステム ビジネスによって予期されるインターフェイス。抽象クラスまたはインターフェイスにすることができます。
- Adapte クラス: アクセスして適応させる既存のコンポーネント ライブラリ内のコンポーネント インターフェイスです。
- アダプタ(Adapter)クラス:アダプタオブジェクトを継承または参照することでアダプタインタフェースをターゲットインタフェースに変換し、顧客がターゲットインタフェースの形式でアダプタにアクセスできるようにするコンバータです。
クラスアダプターパターン
実装方法:現行システムのビジネスインタフェースを実現するアダプタクラスを定義すると同時に、既存コンポーネントライブラリ内の既存コンポーネントを継承します。
【例】カードリーダー
既存のコンピューターはSDカードのみを読み取ることができますが、TFカードのコンテンツを読み取りたい場合は、アダプターモードを使用する必要があります。TF カードの内容を読み取るためのカード リーダーを作成します。
クラス図は次のとおりです。
TFCard と TFCardImpl はアダプター クラス、SDCard と SDCardImpl はターゲット インターフェイス、SDAdapterTF はアダプター クラスです。
アダプター クラス SDAdapterTF に SDCard インターフェイスを実装させ、アダプター クラス TFCardImpl を継承して、TF 操作と互換性を持たせるようにします。
コードは以下のように表示されます:
-
ターゲットインターフェイス - SDカード用インターフェイス
public interface SDCard { // 读取SD卡方法 String readSD(); // 写入SD卡功能 void writeSD(String msg); }
-
特定の SD カード実装クラス
public class SDCardImpl implements SDCard { public String readSD() { String msg = "sd card read a msg :hello world SD"; return msg; } public void writeSD(String msg) { System.out.println("sd card write msg : " + msg); } }
-
パソコン教室
public class Computer { // 从SD卡中读取数据 public String readSD(SDCard sdCard) { if (sdCard == null) { throw new NullPointerException("sd card null"); } return sdCard.readSD(); } }
-
アダプタークラスインターフェース - TFカードインターフェース
public interface TFCard { // 读取TF卡方法 String readTF(); // 写入TF卡功能 void writeTF(String msg); }
-
アダプタークラス - TFカード実装クラス
public class TFCardImpl implements TFCard { public String readTF() { String msg ="tf card read msg : hello world tf card"; return msg; } public void writeTF(String msg) { System.out.println("tf card write a msg : " + msg); } }
-
アダプタークラスの定義(SD互換TF)
public class SDAdapterTF extends TFCardImpl implements SDCard { public String readSD() { System.out.println("adapter read tf card "); return readTF(); } public void writeSD(String msg) { System.out.println("adapter write tf card"); writeTF(msg); } }
-
テストクラス
public class Client { public static void main(String[] args) { // 创建计算机对象 Computer computer = new Computer(); // 读取SD卡中的数据 String msg = computer.readSD(new SDCardImpl()); System.out.println(msg); System.out.println("==============="); // 使用该电脑读取TF卡中的数据 // 创建适配器类对象 SDAdapterTF sdAdapterTF = new SDAdapterTF(new TFCardImpl()); String msg1 = computer.readSD(sdAdapterTF); System.out.println(msg1); } }
出力
SDCard read msg : hello world SD =============== adapter read tf card TFCard read msg : hello world TFcard
クラス アダプター パターンは、構成再利用の原則に違反します。クラス アダプタは、クライアント クラスにインターフェイス仕様がある場合に使用できますが、それ以外の場合は使用できません。
オブジェクトアダプターパターン
実装方法:オブジェクトアダプタモードを利用することで、既存のコンポーネントライブラリに実装されているコンポーネントをアダプタクラスに導入することができ、このクラスは現行システムのビジネスインターフェースも同時に実現します。
FutureTaskの構築方法はオブジェクトアダプタパターンを利用してRunnableからCallableに変換しますが、
詳細はFutureTaskの構築方法 ソースコード解析を参照してください。
【例】カードリーダー
オブジェクト アダプター パターンを使用して、カード リーダーのケースを書き換えます。
クラス図は次のとおりです。
TFCard と TFCardImpl はアダプター クラス、SDCard と SDCardImpl はターゲット インターフェイス、SDAdapterTF はアダプター クラスです。
アダプター クラス SDAdapterTF に SDCard インターフェイスを実装させましたが、今回はアダプター クラス TFCard を直接継承するのではなく集約します。これは複合再利用の原則を満たします。インターフェイス仕様の SDCard がない場合は、特定のターゲット実装クラス SDCardImpl を直接継承することができ、これにより、上記のクラス アダプター モードの 2 つの欠点が解決されます。
クラスアダプターパターンのコードは、アダプタークラス(SDAdapterTF)とテストクラスを変更するだけです。
コードは以下のように表示されます:
-
アダプター オブジェクトの作成 (SD 互換 TF)
public class SDAdapterTF implements SDCard { // 聚合-声明适配者类 private TFCard tfCard; public SDAdapterTF(TFCard tfCard) { this.tfCard = tfCard; } public String readSD() { System.out.println("adapter read tf card "); return tfCard.readTF(); } public void writeSD(String msg) { System.out.println("adapter write tf card"); tfCard.writeTF(msg); } }
-
テストクラス
public class Client { public static void main(String[] args) { Computer computer = new Computer(); SDCard sdCard = new SDCardImpl(); System.out.println(computer.readSD(sdCard)); System.out.println("------------"); // 创建适配者类对象 TFCard tfCard = new TFCardImpl(); // 创建适配器类对象 SDAdapterTF adapter = new SDAdapterTF(tfCard); System.out.println(computer.readSD(adapter)); } }
出力
SDCard read msg : hello world SD =============== adapter read tf card TFCard read msg : hello world TFcard
注: もう 1 つのアダプタ パターンは、インターフェイス アダプタ パターンです。インターフェイスにすべてのメソッドを実装したくない場合は、抽象クラス アダプターを作成してすべてのメソッドを実装できます。現時点では、抽象クラスを継承するだけで済みます。
アプリケーションシナリオ
- 以前に開発されたシステムには、新しいシステムの機能要件を満たすクラスがありますが、そのインターフェイスは新しいシステムのインターフェイスと一致しません。
- サードパーティが提供するコンポーネントを使用しますが、コンポーネントのインターフェース定義は自分で必要とするインターフェース定義とは異なります。
JDKソースコード解析 - InputStreamReader
Reader(文字ストリーム)とInputStream(バイトストリーム)の適応には、InputStreamReader(バイトデータを文字データに変換)を使用します。
InputStreamReader は java.io パッケージの Reader を継承し、その中に実装されていない抽象メソッドを実装します。好き:
public int read() throws IOException {
return sd.read();
}
public int read(char cbuf[], int offset, int length) throws IOException {
return sd.read(cbuf, offset, length);
}
上記コードの sd (StreamDecoder クラス オブジェクト) ですが、Sun の JDK 実装では、実際のメソッド実装は sun.nio.cs.StreamDecoder クラス内の同名のメソッドの呼び出しカプセル化です。
クラス構造図は次のとおりです。
Reader、InputStream、StreamDecoder の 3 つのクラスは、標準のオブジェクト アダプター パターンを使用します。StreamDecoder は、InputStream を集約し、2 つの読み取りメソッドも提供します。実際、これら 2 つのメソッドは、親クラス Reader を書き換えるためのメソッドであるため、StreamDecoder はアダプター クラスです。その親抽象クラス Reader がターゲット インターフェイスであり、InputStream がアダプター クラスです。
- InputStreamReader は、Reader も実装する StreamDecoder のカプセル化です。
- StreamDecoder は Java SE API の一部ではありませんが、Sun JDK によって提供される独自の実装です。しかし、構築メソッドでバイト ストリーム クラス (InputStream) をカプセル化し、このクラスをバイト ストリームと文字ストリーム間の変換に使用することはわかっています
解码
。
結論は:
表面的な観点から見ると、InputStreamReader は、InputStream バイト ストリーム クラスと Reader 文字ストリームの間の変換を行います。上記の Sun JDK の実装クラス関係構造からわかるように、StreamDecoder の設計と実装では実際にアダプター モードが採用されています。