実際の業務で新しいコードを1から書き始めるなんてことは滅多にありません。
基本は既にあるコードを追加・修正しバージョンアップを重ねるという業務がほとんどですね。
また1から作る場合でも別のプロジェクトからコードを引っ張ってきて、再利用することもありますね。
こういった作業はプログラミングを行ったことがない人には怠けてるように見えるでしょうか?
実はこの作業には「既に問題なく動くことが実績としてある」という大きなポイント・メリットがあります。
ですが、全くそのまま再利用できれば一番ですが機能の追加や別のコードとの連携をとるために修正をしなければなりません。
そういった場合に役に立つデザインパターンが、今回紹介する「Adapterパターン」です。
Contents
Adapterパターンとは?
Adapterパターンを利用する際に重要なことは、
- 「使う側(Client:依頼者)」が求める動作
- 「使われる側(Adaptee:適合される)」が提供している動作
をしっかりと見極めることです。
例えば、
- あなたはどうしても釘を打たなければなりません。← 求める動作
- 手元にはバナナしかありません。← 使われる側
(そしてなぜか振るという動作が提供されています(swingメソッド)) ← 提供している動作
こういった場合、貴方は「打つ」動作が必要です。(hitメソッド)
この都合のいいバナナでも釘を打てば、バナナに穴が開いてしまいますね。
そこでAdapterパターン、凍ったバナナ(FrozenBananaクラス)を作り、Hit関数を実装すれば解決できそうです。
実装してみよう。
まずはAdapterパターンを考えずに実装してみましょう。
1 2 3 4 5 6 7 8 |
package com.xoxopigs.adapter; public class You { public static void main(String[] args) { Banana banana = new Banana(); // Error - このままでは釘でバナナが破壊されてしまう。 banana.hit(); } } |
1 2 3 4 5 6 |
package com.xoxopigs.adapter; public class Banana { public void swing() { System.out.println("swing"); } } |
コメントにもあるようにバナナのまま釘を打てば、バナナが破壊されてしまうのは目に見えています。
では、求める動作「釘を打つ」ことができるようにAdapterパターンを利用して、Bananaを再利用します。
Adapterパターンを使ってみよう。
1 2 3 4 5 6 7 8 |
package com.xoxopigs.adapter; public class You { public static void main(String[] args) { Target banana = new FrozenBanana(); // Good!! banana.hit(); } } |
オブジェクトBananaを実際に求める動作を定義したTargetインターフェイスに変更し、実装クラスである「凍ったバナナ」を生成しています。
そして、実際に「打つ」動作を行っています。
1 2 3 4 5 6 |
package com.xoxopigs.adapter; public class Banana { void swing() { System.out.println("swing"); } } |
ここがポイントです。 既に提供されているBananaには一切手を加える必要がありません。
逆に何か修正や追加を行ってしまうと、バグが混入する可能性が発生、つまりはテスト対象になってしまいます。
1 2 3 4 |
package com.xoxopigs.adapter; public interface Target { public abstract void hit(); } |
Targetインターフェイスは求める動作(今回の場合は「打つ」)を定義しています。
1 2 3 4 5 6 |
package com.xoxopigs.adapter; public class FrozenBanana extends Banana implements Target { public void hit() { super.swing(); } } |
凍ったバナナクラスではバナナクラスで定義していた振るという動作を呼び出すだけになっています。
実際にはコンストラクタで水をかけたり、冷凍庫なりに入れる必要があるでしょうか。
クラス図
さて、ここで一般的なクラス図を見てみましょう。
Client:Youクラスが担当しました。
Target:Targetインターフェイスが担当しました。
Adapter:FrozenBananaクラスが担当しました。
Adaptee:Bananaクラスが担当しました。
まとめ
…ちょっと例が苦しかったでしょうか…。
なんとなくでもニュアンスを掴んでいただければいいのですが…。
Adapterパターンを利用する最大の利点は何といってもAdapteeクラスのテストを行う必要がない点です。
実際に動いてきた実績があるのですから。
AdapterパターンはIteratorパターンと同じくデザインパターンの触り・基本としてよく扱われますが、
基本だからこそ登場の機会も多くしっかりと使用できるときを見極める必要がありそうですね。