1. パターンの意図
アダプタ(Adapter)パターン は、
既存のクラスやインターフェースをクライアントが期待する形式に変換する デザインパターンです。
解決する問題
- ライブラリや既存コードのインターフェースが自分のプロジェクトと合わない
- 外部 API とアプリ内のモデルを変換する必要がある
- 「インターフェース不一致」を吸収したい
ポイント
- 電源変換プラグのイメージ:合わない形を変換して使えるようにする
- クライアント側は「変換されていること」を意識せず利用できる
2. UML 図
- Target:クライアントが期待するインターフェース
- Adaptee:既存のクラス(インターフェースが合わない)
- Adapter:Adaptee を Target に適合させる役割
- Client:Target に依存し、Adapter を通じて Adaptee を使う
3. Flutter / Dart 実装例
3.1 期待するインターフェース(Target)
abstract class JsonParser {
Map<String, dynamic> parse(String text);
}
3.2 既存クラス(Adaptee)
class LegacyXmlParser {
Map<String, dynamic> parseXml(String xml) {
// XML を JSON っぽく変換(簡略化例)
return {"data": xml};
}
}
3.3 アダプタ
class XmlToJsonAdapter implements JsonParser {
final LegacyXmlParser _xmlParser;
XmlToJsonAdapter(this._xmlParser);
@override
Map<String, dynamic> parse(String text) {
return _xmlParser.parseXml(text);
}
}
3.4 利用例
void main() {
JsonParser parser = XmlToJsonAdapter(LegacyXmlParser());
var result = parser.parse("<data>hello</data>");
print(result); // {data: <data>hello</data>}
}
4. Android / Kotlin 実装例
4.1 Target
interface JsonParser {
fun parse(text: String): Map<String, Any>
}
4.2 Adaptee
class LegacyXmlParser {
fun parseXml(xml: String): Map<String, Any> {
return mapOf("data" to xml)
}
}
4.3 Adapter
class XmlToJsonAdapter(private val xmlParser: LegacyXmlParser) : JsonParser {
override fun parse(text: String): Map<String, Any> {
return xmlParser.parseXml(text)
}
}
4.4 利用例
fun main() {
val parser: JsonParser = XmlToJsonAdapter(LegacyXmlParser())
val result = parser.parse("<data>hello</data>")
println(result) // {data=<data>hello</data>}
}
5. メリット / デメリット
メリット
- 既存クラスを修正せずに再利用できる
- 新旧インターフェースの橋渡しが可能
- クライアントは一貫したインターフェースで利用可能
デメリット
- クラス数が増える
- アダプタを乱用すると「変換だらけ」で複雑になる
6. 実務ユースケース
Flutter
- Platform Channel:iOS/Android ネイティブ API を Dart の形式に変換
- 外部 API レスポンス変換:REST/GraphQL レスポンスをアプリ内モデルに適合
- レガシーコードラップ:旧サービスのクラスを新しいインターフェースに統一
Android (Kotlin)
- RecyclerView.Adapter(UIコンポーネントのデータ変換)
- API モデル変換(DTO → Domain モデル)
- Java ライブラリのラップ(Java API を Kotlin フレンドリーに変換)
7. 実装上の注意点
Flutter / Dart
-
fromJson
やtoEntity
を Adapter 的責務として明確化すると整理しやすい - Repository 層で DTO ⇔ Domain 変換に多用される
Android / Kotlin
- Adapter の乱用で 「変換クラスだらけ」 にならないように注意
- Mapper / Converter との責務分離を意識する
8. どんなときに使う?
- 外部ライブラリを修正せずに利用したい
- API / データモデルをプロジェクト仕様に合わせたい
- クライアントは「新しい統一 API」に依存させたい
まとめ
- アダプタパターンは「インターフェース不一致を吸収するための変換器」
- Flutter では Platform Channel、Android では RecyclerView.Adapter が代表例
- 既存コードを変更できない/したくないときに特に有効
- 過剰に増やさず、責務を明確化したアダプタ設計がベストプラクティス