Menu
Coddy logo textTech

アダプターパターン

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"}
}

XmlToJsonAdapterXmlService をラップし、JsonData インターフェースを実装します。クライアントコードは、データが元々 XML ソースから来たものであることを知らずに、使い慣れた JsonData インターフェースを操作します。アダプターは、内部ですべての変換を処理します。

Adapterパターンは、既存のクラスを使用したいがそのインターフェースが要件に合わない場合、あるいは互換性のないインターフェースを持つクラス同士を連携させる再利用可能なクラスを作成したい場合に使用します。

challenge icon

チャレンジ

簡単

メディアプレーヤーのアダプターシステムを構築しましょう!すべてのメディアソースが標準インターフェースを通じてオーディオを提供することを期待する最新のオーディオシステムがあると想像してください。しかし、全く異なる方法で音を再生する古いレコードプレーヤーを統合する必要があります。

アダプターパターンの実装を整理するために、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() を呼び出し、結果を出力する
  
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン