システムが大規模になるほど、あるいは優れた設計が施されたシステムほど、システムは高度にコンポーネント化される傾向にあります。例えばトランザクション管理、ロギング、コネクション・プーリングなど、機能単位でコンポーネント化されます。
さて、開発中のシステムの機能の一部を、あるコンポーネントを用いて実現していた際、性能上の理由などから、他のベンダより提供されている同じ機能をもったコンポーネントに切り替える必要が生じたとしましょう。しかし、提供元が異なるコンポーネントは、利用するためのAPI(一連のメソッド呼び出しなど)に互換性がないことがほとんどです。システムの構築は進行してしまっているので、コンポーネントを切り替えるにはコードを大幅に書き換える必要が生じます。このような問題に直面した場合の良い解決方法はないものでしょうか?
デザインパターンは、こうした問題に直面したときに威力を発揮します。ここでは、「Adapterパターン」が問題を解決してくれます。
開発中のシステムSampleSystemでは、“ICompInterface”インターフェイスを持つコンポーネントを利用していたとします。このインターフェイスを実装したコンポーネントに性能上の問題があり、同等の機能をもった新しいコンポーネント“NewComp”を導入しなければならなくなったとします。しかし、以下に示すコードで分かるように、ICompInterfaceとNewCompには、利用方法に互換性がありません。
package net.mogra.wings.javatips; |
package net.mogra.wings.javatips; |
package net.mogra.wings.javatips; |
このICompInterfaceとNewCompとの間に「アダプタ」クラスを差し込むことによって、インターフェイスを適応させる方法が、Adapterパターンです。Adapterパターンは、このアダプタクラスの作り方によって、2通りの方法に分かれます。
コンポーネントをサブクラス化し、そこで適応させたいインターフェイスを実装することでアダプタを作成する方法です(リスト4)。サブクラス化の際にメソッドをオーバーライドすることで、コンポーネントの挙動をカスタマイズできる、というのが特徴です。しかし、こちらは、コンポーネントのクラスがfinal宣言されていたり、適合させたい対象がインターフェイスでなく抽象クラスだった場合には、採用できません。
package net.mogra.wings.javatips; |
適応させたいインターフェイスを実装し、クラス内にあるコンポーネントのインスタンスへ処理を委譲させるアダプタを作成する方法です(リスト5)。こちらの方が、より柔軟性をもったアプローチです。メソッドをオーバーライドしてコンポーネントの挙動をカスタマイズしたいなどの理由がなければ、こちらを採用するのが良いでしょう。
package net.mogra.wings.javatips; |
最後に、作成したアダプタクラスをリスト6のようにしてSampleSystemシステムへ組み込みます(赤字が修正部分)。下の例は(2)のアダプタを用いた場合ですが、(1)の場合でも修正方法は同様です。
public static void main(String[] args) { |
これで、元のシステムへの修正は、コンポーネントの実装を切り替えるたった1行の修正で済みました。このサンプル程度の小さなプログラムでは、「そんなものか」と思うかもしれませんが、何十ものクラスでコンポーネントが使われているような大規模なプログラムを考えてみて下さい。Adapterパターンの効果がはっきりするはずです。
また、ここからはAdapterパターンが扱う領域ではありませんが、リスト6の赤字部分のコンストラクタ呼び出しを、以下のように特定のメソッドで置き換えるのも良い方法です。
ICompInterface comp = ComponentFactory.getComponent(); |
コンストラクタが複数箇所で直接呼ばれていると、そのすべてを修正しない限りコンポーネントを完全に切り替えられません。しかし、こうしておけば、ComponentFactoryクラスを修正するだけでコンポーネントの切り替えができます。こうした方法は、非常によく採られる方法です。
Adapterパターンを用いたコードのクラス図は、一般に以下のようになります。ここで、上のサンプルコードにおけるSampleSystem、ICompInterface、NewComp、NewCompClassAdapter(NewCompObjectAdapter)は、それぞれClient、ITarget、Adaptee、Adapterに相当します。
Adapterパターンは、身近な所でも採用されています。Apache JakartaのCommons Loggingがその典型的な例です。Commons Loggingは、Javaのさまざまなロギング実装を共通のインターフェイスで利用できるようにするライブラリです。コアAPIのロギングや、Log4Jなど、利用方法がばらばらのロギング実装を、Commons LoggingのLogインターフェイスを元に一元的に操作できるのです。
上のクラス図で示した各クラスと、Commons Loggingにおける各クラスとの対応関係は以下の通りです。
クラス図 | Commons Loggingのクラス | |
---|---|---|
Client | Commons Loggingを利用するクラス(ユーザが作成する部分 | |
ITarget | Logインターフェイス | |
Adaptet | Jdk14Logger、Log4JLoggerなど | |
Adaptee | 個々のロギング実装。コアAPIのロギング(java.util.logging.Logger)やLog4J(org.apache.log4j.Logger)など | |
Copyright © ITmedia, Inc. All Rights Reserved.