ゴミ捨て場

命を大事にしな

デザインパターンとともに学ぶ オブジェクト指向のこころ 7

第7章 Adapterパターン

鍵となる特徴

  • 目的
    • 修正することの出来ない既存オブジェクトを特定のインタフェースに適合させる
  • 問題
    • 流用したいデータや振る舞いが既存システムや外部システムに存在しているものの、そのインタフェースが正しくなく、クラスとして用いることが出来ないこと。通常は、抽象クラスから何らかの派生クラスを作成しなければならないときに使用する。
  • 解決策
    • 必要なインタフェースを保持したラッパークラスをAdapterによって提供する。
  • 構成要素と協調要素
    • Adapterは、Targetクラス(派生元クラス、Clientが使用するクラス)のインタフェースに合うようにAdaptee(インタフェースが正しくなく使えないクラス)のインタフェースを適合させる。これによってClientはAdapteeをTargetであるかのように使用できるようになる。
  • 因果関係
    • Adapterパターンにより、既存オブジェクトをインタフェースに制限されることなく、新たなクラス構造に取り込むことが可能になる。
  • 実装
    • 他のクラスを用意し、既存クラスを保持させる。そして保持しているクラス側で必要なインタフェースを用意し、既存クラスのメソッドを呼び出すようにする。

Adapterパターンの勘所

  • 点や線や四角形といった形状を、それらを見分けること無く、「何らかの表示可能な形状(Shapeクラスとする)」としてすべて扱いたい場合、ポリモーフィズムを用いて派生クラスを多態化する。
  • このような場合は、Shapeクラス内にインタフェースを定義した後、派生クラス(点、線、四角形など)ごとに適切な振る舞いを実装することになる。
  • そして新たなShepeとしてが円(Circleクラス)が仕様として追加されたとする。この場合、ShapeクラスからCircleクラスを派生させ、またShapeクラスのインタフェースに従って振る舞いを実装することになるだろう。これは結構手間のかかる作業である。
  • しかし幸運なことに、円を取り扱うXXCircleというクラスが実は別のプロジェクトで実装されていることがわかった。しかし以下のような問題が存在した。
    • そのクラスは自分のプロジェクトの規約とは異なったもので、メソッド名はShapeクラスのインタフェースには従っていなかった。
    • 外部で定義されたクラスなので、当然Shapeクラスを継承したクラスにはなっていない。
    • したがってShapeを用いたポリモーフィズムを前提としたこのシステムには直接XXCircleは利用できない。
  • このような問題をクリアするため、ソースを部分流用し、一部改修することや、XXCircleをCircleクラスとして期待される作りに修正することなどが考えられるが、どちらも難しい問題である。
  • このような状況に対応するには、既存コードを修正するのではなく、既存コードを適合(Adapt)させればよいのである。つまり、Shapeから派生させて新規クラスを作成し、XXCircle中にある円の実装をそこから使用することでShapeのインタフェースを実装すればよい。
    • ShapeクラスからCircleクラスを派生させる。
    • CircleにXXCircleを保持させる。
    • Circleオブジェクトに対するリクエストをXXCircleオブジェクトに転送する。
  • 適合対象となるクラスの中に、期待する機能が全て用意されていないケースも当然ある。そのような場合は以下のようにする。
    • 適合対象クラス内に存在している機能はそれを利用する。
    • 適合対象クラス内に存在していない機能はラッパークラス(今の例で言えばCircleクラス)内に実装する。

Class Adapterパターン

  • 上記で説明してしたのはObject Adapterパターンで、あるオブジェクト(ラッパー)が他のオブジェクト(適合対象)を保持しているので、そのように呼ばれる。
  • Class Adapterパターンは、多重継承を使用してAdapterパターンを実装するもの。
  • Class Adapterパターンは以下のような新規クラスを実装する。
    • 抽象クラスのインタフェースを定義するため、その抽象クラスからpublicで派生する。適合対象クラスの実装にアクセスするため、その既存クラスからprivateで派生する。
  • 新規クラス内でのインタフェースの実装は、そのメソッドに対応付けられた適合対象クラスのprivateメソッドを呼び出すようにする。
  • Object AdapterとClass Adapterのうちどちらが適切なのかは概念レベルでは違いを無視しても構わないが、実装レベルではどのような制約があるのかを考察して判断する必要があるだろう。

FacadeパターンとAdapterパターン似過ぎ問題

  • どちらのパターンも既存クラスが存在し、そのインタフェースが望み通りになっていないというのが問題として共通である。そして新規クラスを用意してやって望み通りのインタフェースを実現している点も同じである。
  • 両者の間ではラッパの種類が違うと言える。違いを以下列挙する。
    • Facadeパターンではインタフェースを設計し直す必要がなく、Adapterパターンは設計し直す必要がある。
    • Facadeパターンはポリモーフィズムを前提としないが、Adapterパターンではそれを利用することが多い(単に望むインタフェースがほしいだけのためにラッパを用意することもあるので「多い」と言っている)。
    • Facadeパターンの目的はクライアントから見た場合のサブシステムのインタフェースを簡素化することにある。Adapterパターンでも簡素化という目的は含まれるものの、本来の目的は適合対象クラスの再設計である。
  • Facadeパターンは複数のクラス群(複雑になってしまっているシステム全体)を隠蔽するのに対して、Adapterパターンは流用したい外部システムの1機能を隠蔽するとも言えるが、本質的には間違っている。Facadeが複雑な単一のオブジェクトを隠蔽することもあるし、Adapterが必要な機能を持つ数多くのクラスを隠蔽することもあるからである。
  • まとめると、Facadeはインタフェースを簡素化するもの、Adapterパターンは既存のインタフェースを他の期待されるインタフェースへ変換するもの、という違いがあるということになる。