アダプターパターン
CoddyのDartジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 95/110。
Adapterパターンは、互換性のないインターフェース同士を連携させることを可能にします。これは、本来であれば連携できない2つのクラス間の架け橋として機能し、既存のクラスをクライアントが期待する新しいインターフェースでラップします。これは、サードパーティのライブラリやレガシーコードをアプリケーションに統合する際に特に役立ちます。
JSONデータを扱うシステムがあり、XMLのみを出力するコンポーネントを統合する必要があると想像してください。どちらのクラスも変更する代わりに、それらの間を変換するアダプターを作成します。
// システムが期待する既存のインターフェース
abstract class JsonData {
String getJson();
}
// 互換性のないインターフェースを持つレガシークラス
class XmlService {
String getXml() {
return '<data><name>John</name></data>';
}
}
// アダプターは XmlService を JsonData と互換性のあるものにします
class XmlToJsonAdapter implements JsonData {
final XmlService _xmlService;
XmlToJsonAdapter(this._xmlService);
@override
String getJson() {
String xml = _xmlService.getXml();
// XML を JSON に変換(簡略化)
return '{"name": "John"}';
}
}
void main() {
var xmlService = XmlService();
var adapter = XmlToJsonAdapter(xmlService);
print(adapter.getJson()); // {"name": "John"}
}XmlToJsonAdapter は XmlService をラップし、JsonData インターフェースを実装します。クライアントコードは、データが元々 XML ソースから来たものであることを知らずに、使い慣れた JsonData インターフェースを操作します。アダプターは、内部ですべての変換を処理します。
Adapterパターンは、既存のクラスを使用したいがそのインターフェースが要件に合わない場合、あるいは互換性のないインターフェースを持つクラス同士を連携させる再利用可能なクラスを作成したい場合に使用します。
チャレンジ
簡単メディアプレーヤーのアダプターシステムを構築しましょう!すべてのメディアソースが標準インターフェースを通じてオーディオを提供することを期待する最新のオーディオシステムがあると想像してください。しかし、全く異なる方法で音を再生する古いレコードプレーヤーを統合する必要があります。
アダプターパターンの実装を整理するために、2つのファイルを作成します:
media_adapter.dart: このファイルには、インターフェースとアダプタークラスが含まれます。最新システムが期待する抽象クラスAudioSourceを定義してください。これには、オーディオ出力を文字列として返すString playAudio()メソッドが必要です。次に、レガシー機器を表すVinylPlayerクラスを作成します。これにはtrackNameフィールドと、*crackle* Playing vinyl: [trackName] *crackle*を返すString spinRecord()メソッドがあります。最後に、AudioSourceを実装し、VinylPlayerをラップするVinylAdapterを構築します。アダプターのplayAudio()メソッドは、レコードプレーヤーのspinRecord()メソッドを呼び出してその結果を返し、互換性のないインターフェースを橋渡しする必要があります。main.dart: メディアアダプターファイルをインポートし、アダプターによって古いレコードプレーヤーが最新のAudioSourceインターフェースを期待するコードでどのように動作するかを示します。トラック名Bohemian Rhapsodyを持つVinylPlayerを作成します。次に、このレコードプレーヤーをラップするVinylAdapterを作成します。期待されるインターフェースに準拠していることを証明するために、アダプターをAudioSource型の変数に格納します。オーディオソースに対してplayAudio()を呼び出し、その結果をプリントしてください。
このパターンの素晴らしい点は、メインコードが標準の AudioSource インターフェースで動作することです。舞台裏で古いレコードプレーヤーが実際の作業を行っていることを知る必要はありません!
期待される出力:
*crackle* Playing vinyl: Bohemian Rhapsody *crackle*チートシート
Adapter パターンは、本来は連携できない2つのクラス間の架け橋として機能することで、互換性のないインターフェースを一緒に動作させることができます。
アダプターは、既存のクラスをクライアントが期待する新しいインターフェースでラップします。これにより、サードパーティのライブラリやレガシーコードを統合する際に役立ちます。
基本構造
// クライアントが期待するターゲットインターフェース
abstract class JsonData {
String getJson();
}
// 互換性のないインターフェースを持つ既存のクラス
class XmlService {
String getXml() {
return '<data><name>John</name></data>';
}
}
// アダプターは XmlService を JsonData と互換性のあるものにします
class XmlToJsonAdapter implements JsonData {
final XmlService _xmlService;
XmlToJsonAdapter(this._xmlService);
@override
String getJson() {
String xml = _xmlService.getXml();
// XMLをJSONに変換する
return '{"name": "John"}';
}
}使用法
void main() {
var xmlService = XmlService();
var adapter = XmlToJsonAdapter(xmlService);
print(adapter.getJson()); // {"name": "John"}
}アダプターは、互換性のないクラスをラップしながら、ターゲットインターフェースを実装します。クライアントコードは、基礎となる実装を知ることなく、使い慣れたインターフェースで動作します。
使用場面: 既存のクラスを使用する必要があるが、そのインターフェースが必要なものと一致しない場合、または互換性のないインターフェースを持つクラスと連携する再利用可能なクラスを作成する場合に使用します。
自分で試してみよう
import 'media_adapter.dart';
void main() {
// TODO: トラック名 "Bohemian Rhapsody" を持つ VinylPlayer を作成する
// TODO: vinyl player をラップする VinylAdapter を作成する
// TODO: アダプターを AudioSource 型の変数に格納する
// TODO: オーディオソースの playAudio() を呼び出し、結果を出力する
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
4Null Safety
Null Safety 入門Nullable と Non-Nullable? 演算子と ! 演算子late キーワードと Null SafetyNull-Aware 演算子クラスにおける Null Safetyまとめ:ユーザープロフィールシステム10コレクションとジェネリクス
List, Set, Map の概要型安全なコレクションジェネリッククラスジェネリックメソッドジェネリクスの制約Iterable と Iteratorまとめ:ジェネリックなストレージ