GoFのデザインパターンのアダプタをRubyによるサンプルコードで紹介します。
アダプタとは現実世界の変換コネクタのようなものです。
直接つながらないコネクタと差込口は、それらの間を変換コネクタが結び付けます。
コネクタと差込口にカスタマイズが不要な点がアダプタの利点です。
🤔 アダプタが利用される場面
Adapterパターンは次のような場面でよく使います。
- 関連性・互換性のないオブジェクトどうしを結び付ける必要があります
- 他のコンポーネントへの変更ができるようにします
🎂 アダプタの構成要素
アダプタの構成要素は次の4つです。
- 利用者(Client):ターゲットのメソッドを呼び出す
- ターゲット(Target):インターフェースを規定します
- アダプタ(Adapter):アダプティのインターフェースを変換してターゲット向けのインターフェースを提供
- アダプティ(Adaptee):実際に動作する既存のクラス
🐰 サンプルソース
次のようなモデルを例にアダプタパターンを説明していきます。
Client: Printクラスを使う側 Printerクラス(Target):Clientが使っているメソッドをもつ print_weak: 括弧囲って文字列を表示する printer_strong: アスタリスクで囲って文字列を表示する OldPrinterクラス(Adaptee):すでに存在していたオブジェクト show_with_paren: 記述を弱めて書く show_with_aster: 記述を強めて書く
ClientはPrinter
クラスのメソッドを使うことはできますが、Oldprinter
クラスはメソッドの名前/定義が異なるため、改造な造に使うことができません。そこでAdapterデザインパターンを適用してClientがOldPrinter
クラスを使えるようにします。
まずは、Printer
クラスを確認します。
# 利用者(Client)へのインターフェイス (Target) |
次にOldPrinter
クラスです。
# Targetにはないインターフェイスを持つ (Adaptee) |
このOldPrinter
クラスのメソッドをClientが使えるPrinter
クラスのインタフェースにするAdapter
クラスを作成します。
このクラスには、次の2つのメソッドを定義します。
#print_weak
:OldPrinter#show_with_paren
を呼び出します#print_strong
:OldPrinter#show_with_aster
を呼び出します
# Targetが利用できるインターフェイスに変換 (Adapter) |
以上がソースコードです。では結果を確認するために、ClientにTargetを動かしてもらいます。
# 利用者(Client) |
ClientはTargetのメソッドを呼び出しているだけですが、Adapterを介して接続したOldPrinterのメソッドにアクセスできています。
このようにAdapterクラスによってPrinter/Oldprinterに変更が不要な点がAdapterデザインパターンの特徴です。
🗻 特異メソッドを使ったAdapter
ここでRubyの特異メソッドを使ってAdapterを表現した場合のコードを示します。
# Targetにはないインターフェイスを持つ (Adaptee) |
このように先ほどよりもシンプルなコードでAdapterを表現できていることがわかります。
🐝 クラス変更とアダプタ適用のどちらを選択すべきか?
必要なインタフェースを変更するのは、クラスや特定のインスタンスだけを直接変更したほうがずっとシンプルなコードになります。ではどのような場面でアダプタを適用すべきでしょうか?
次の判断材料を元に、「クラス変更」か「アダプタ適用」を選択してください。
- 問題のクラスの理解がある、変更が比較的少ない => クラス変更
- オブジェクトが複雑/完全な理解がない => アダプタ適用