ねらい
既存クラスのインタフェースを変える
別名
Wrapper
モチベーション
- 既製品の汎用クラスのインタフェースが合わず、フレームワークで使えなかったり、アプリケーションのクラスと互換性がなかったりする
-
汎用クラス自体を書き換えるのは現実的でない
- ソースコードを持っている必要がある
- 汎用なのに、特定のアプリケーションのインタフェースに合わせるべきではない
- 汎用クラスのインタフェースだけ変えればよい
-
やり方が2通りある
- 継承方式
- 集約方式
つかいどころ
- あるクラスのインタフェースを変えて、他のクラスとの互換性をもたせたい
-
【集約方式】インタフェースを変えたい側(Adaptee)が独立して継承ツリーをもっている
-
継承方式ではサブクラスまでAdaptできない
- できなくはないけどAdapter派生まみれになるからやめたほうがいい
-
構造
登場人物
-
Target
- 満たすべきインタフェース
-
Client
- Targetインタフェースに依存するクラス
-
Adaptee
- Targetインタフェースとインタフェースの合わないクラス
- これにTarget互換をもたせたい
-
Adapter
- AdapteeをTarget互換にするためのクラス
- Target派生orTargetを実装するクラス
クライアントコードからの利用
- ClientはAdapterをTargetとして利用する
功罪
継承方式と集約方式とのトレードオフ
継承 | 集約 | |
---|---|---|
Adapteeとの関係 | Adapteeを継承 | Adapteeオブジェクトを集約 |
Adaptee派生クラス対応 | 不可 | 可 |
Adapteeのオーバライド | 可 | 不可 |
間接層 | 増えない | 増える(Adapteeメンバ) |
ほか、考えるべきこと
-
AdapteeをTarget互換にするためのAdapterの仕事量はさまざま
-
もともと似てれば大したことはしない
- メソッド名の読み替えとか
- 引数の順番を変えるだとか
- 欠けている機能を補うだとかは重い
-
-
Pluggable adapters
- 汎用モジュールの提供を想定
- ユーザが
Adapter
を実装しさえすれば、任意のAdaptee
を使えるつくりにする
-
2-way adapters
-
Adaptee
として透過性を残すTarget
互換にしつつ、Adaptee
のインタフェースも残すということ
-
Adaptee
のインタフェースとTarget
のインタフェースとが大きく異なっていれば多重継承で可能- 両方publicで、ということかな
-
実装
-
継承方式 in C++
-
Target
はpublic、Adaptee
はprivateで継承- 外から見たら
Target
にしか見えなくなる
- 外から見たら
-
-
Pluggable Adapters
- 汎用モジュールは
Client
にあたる -
「汎用」にするやり方は3つ
-
継承・オーバライド方式
Client
自体がTarget
- 汎用にしたい処理を仮想関数にしておく
- 利用者には
Client
のサブクラスを作らせる(Adapter
)
-
集約・委譲方式
- 処理を委譲するクラス
Adapter
のインスタンスを受け取る口を用意する -
汎用にしたい処理は、
Adapter
に処理を委譲する- ので、
Adapter
に要求するインタフェースは公開されている必要がある - 静的型付け言語では、これを
Target
インタフェースとして提供する - 動的型付けな言語では、ダックタイピングでもいい
- ので、
- 処理を委譲するクラス
-
パラメータ
- Smalltalk固有の話?よくわかんなかった
-
- 汎用モジュールは
他のStructural Patternとの対比
-
Bridge
-
構造が似ている
-
集約のパターンはだいたい似ると思うんですけど
- StateとStrategyとかね
-
-
狙いは異なる
-
Adapter
- 既存のクラスのインタフェースを変える
-
Bridge
- インタフェースと実装とを分離する
-
-
-
Decorator
-
インタフェースを変えずに(=透過的)機能を追加する
- 2-way adapterとは似ている
-
Adapterは一般にインタフェースを変えるためのもの
- 変えた先のインタフェースに合わせるために機能追加を伴うことはある
-
-
Proxy
- これもインタフェースを変えない
英語
-
off-the-shelf
- 既製品の、ありものの
-
viable
- 実行可能な