2.Adapter パターン 1
- 2012/04/26 一部修正しました
2.1 Adapterパターンとは
第2章では Adapter パターンを学びます。adaptという単語は日本語で「適合させる」という意味で、adapterとは「適合させるもの」という意味になります。Adapterパターンは、インタフェースに互換性の無いクラス同士を組み合わせることを目的としたパターンです。
例えば、これまで利用していたメソッドと同じ機能を、よりすぐれた形で提供するメソッドを持つクラスの存在を知ったとします。しかし、このすぐれたメソッドは、これまで利用していたメソッドとは異なるインタフェースを持つため、乗り換えるとなると多大な変更を余儀なくされる場合があります。こんなとき、この2つのメソッドのインタフェースの違いを吸収してやる Adapter を準備することで、少ない変更で新しいメソッドに乗り換えることができるのです。
このような目的を果たすため Adapter パターンでは、2つの方法を与えています。一方は継承を利用した方法で、もう一方は委譲を利用したものです。
それでは、サンプルケースを見てみましょう。
2.2 サンプルケース1(継承を利用した Adapter パターン)
あなたは、相変わらず学校の先生をしています。少しずつ学校にも慣れてきたのですが、あなたのクラスはみんなばらばらで、まったくまとまりがありません。あなたは、学級代表を決める必要があると感じ、太郎君に学級代表を任せようと思いました。
あなた | : | 太郎君なら、みんなが一緒になって楽しめる学級を作り上げてくれるだろう。 みんなが一緒になって楽しむことができれば、おのずとまとまりも出てくるはずだ! |
放課後、あなたは太郎君を職員室に呼び出しました。
あなた | : | おい太郎、お前学級代表をやってみないか? |
太郎君 | : | えっ!無理ですよ・・・ |
あなた | : | お前ならクラスをまとめることができるよ。 |
太郎君 | : | 僕にはみんなをまとめるための能力はありません。 |
話は一向にまとまりそうに無く、陽も落ちてきたので、あなたは太郎君を帰らせました。さてどうしたものでしょう・・・。困り果てたあなたはベテラン先生に相談してみることにしました。
あなた | : | ベテラン先生、実はかくかくしかじかで・・・。 |
ベテラン | : | なるほどね、君は太郎君が持ってる「みんなで一緒になって楽しむことができる」という能力に期待している。でも、彼はみんなをまとめる能力は無いと言って拒んでるというわけだね。ちょっとソースコードと図にしてみようか。 |
public class Taro{ public void enjoyWithAllClassmate(){ System.out.println("みんなで楽しむ"); } }
public interface Chairperson{ public void organizeClass(); }
public class Teacher{ public static void main(String args[]){ Chairperson chairperson = new Taro(); chairperson.organizeClass(); } }
ベテラン | : | 大体こんな感じだな。 |
あなた | : | これでは、コンパイルエラーになってしまいます。 話がまとまらないわけですね。 |
ベテラン | : | そうだね、そもそも Chairperson インタフェースを実装していない Taro クラスを Chairperson オブジェクトとして利用しようというわけだ。 |
あなた | : | そうか、太郎が少し成長して、Chairperson インタフェースで定義されている organizeClass メソッドを実装すればよいわけですね。 |
ベテラン | : | そう、太郎君には、新たに実装する organizeClass メソッドで自身の enjoyWithAllClassmate メソッドを呼び出すようにしてもらう。これは、ちょうど継承を利用した Adapter パターンに相当する形だね。 |
あなた | : | あだぷたーぱたーん? |
ベテラン | : | デザインパターンでは、今回のようなインタフェースの違いを吸収してうまく利用するパターンとして Adapter パターンが紹介されているんだよ。Adapter パターンには継承を利用するものと、委譲を利用するものの2つの実現方法があって、今回のサンプルケース1では継承を使った Adapter パターンの形になるよ。クラス図を見てみてみようか。 |
次の日あなたは、太郎君をもう一度呼び出しました。
あなた | : | いいか太郎、難しく考える必要はない、自分を少し成長させて Chairperson インタフェースを実装しなさい。 |
太郎君 | : | わかりました、やってみます。でも、Chairperson インタフェースで定義されている organizeClass メソッドはどのように実装すればいいのでしょうか? |
あなた | : | とりあえず今のところは、Taro クラスで定義されている enjoyWithAllClassmate メソッドを呼び出すだけで問題ないよ。君の「みんなで楽しもうとする能力」(enjoyWithAllClassmate メソッド)は、インタフェースが違うだけで、「クラスをまとめるための能力」(Chairperson インタフェースで定義されている organizeClass メソッド)として十分に利用できるものだよ。 |
太郎君 | : | わかりました。 |
話はすんなりまとまりました。
(実習課題1)
Taro クラスと Chairperson インタフェースのインタフェースの違いを埋めるように NewTaro クラスを実装しなさい。 Teacher クラスのmain メソッドの中身を変更することは問題ありません。
実習課題1の回答
少し成長した太郎君のソースコードは、以下のようになると考えられます。
public class NewTaro extends Taro implements Chairperson{ public void organizeClass(){ enjoyWithAllClassmate(); } }
この際、Teacher クラスは以下のように変更する必要があるでしょう。
public class Teacher{ public static void main(String args[]){ Chairperson chairperson = new NewTaro(); chairperson.organizeClass(); } }
太郎君は、少し成長し、学級代表に求められる organizeClass というメソッドが呼ばれたときに、自らの enjoyWithAllClassmate メソッドを呼ぶようにしています。こうすることで、先生は「太郎君クラスをまとめて」と言えば、先生が期待する太郎君の能力を利用できるようになるました。
このように、これまでの太郎君を継承し、なおかつ Chairperson インタフェースを実装することで、太郎君が以前から持っていた能力を、「クラスをまとめる能力」として利用することができるようになりました。
ここで、Adapter パターンを利用していなければ、どのようになっていたでしょう?あなたは、クラスをまとめてほしいときに「太郎君、ちょっとみんなで楽しめるクラスにしてちょうだい」などと、わけのわからないことを言う必要があったかもしれません。さらには、太郎君が別の誰かに、「ちょっとクラスをまとめて」と頼まれたときに、「僕にはそんな能力がありません」と断ることになっていたかも知れません。それに、太郎君のすばらしい能力を「クラスをまとめる」ことに使えることを知っているのが、あなただけになってしまっていた可能性もあります。Adapter パターンを使って本当に良かったですね。
継承を利用した Adapter パターンの一般的なクラス図を描いて見ましょう。
サンプルケースでは、 Adapter クラスの役割を NewTaro クラスが担っていたわけです。このように、利用したいメソッドを持つクラスを拡張し、利用したいメソッドを定義するインタフェースを実装する Adapter を間に挟むことで、両者のインタフェースの違いを吸収しているのです。