以前に書いた、デザインパターンの話のつづき、今回は、構造に関するものです。
GoF本では、デザインパターンを主に3つに分けている。
1つめは、生成に関するもの
2つめは、構造に関するもの
3つめは、振る舞いに関するもの
1つめの生成はわかりやすいです。3つめの振る舞いというのも、これはメソッドに関するもんだと思いをめぐらすことができます。。。が、構造っていわれても。。。クラス割り?
いまいちはっきりしませんが、そうやって割っているので、そういうことにしましょう。
で、今回は、構造に関するものです。ここに属するものは
・Adapter いわゆるラッパー
・Bride 機能と実装をわける
・Composite 違うものを1つの上位クラスにいれて、同じように扱う
・Decorator 付け足す
・Facade 入り口を1つにする
・Flyweight オブジェクトの共有
・Proxy 代わりに処理する
って感じです。
今回も、どーいうことがいいたいのかを中心に書いていきます。
(順番は変えます。簡単なもんから説明していってます)
■Facade
入り口を1つにする。これは、どっかのサーバーを呼び出すなんていうケースで、サーバー側のプログラム(呼び出し)は、今後増えていくなんていうようなケースのとき、とりあえず、
int execute(HashMap argMap);
というすべて、同じインターフェースにして、argMapの
キー"command" の値に呼び出すメソッドを設定、
それ以外のキーと、値に、その呼び出すメソッドで必要な引数を記述してもらって、
全部入り口はこのメソッドで共通にする
などというときに使います。
■Adapter
いわゆるラッパーです。
2つのメソッドが違うとき、たとえば、上記の例だと
int execute(HashMap argMap);
というメソッドが入り口で、呼び出すメソッドは int doJob(String arg1,String arg2);
などと、違ったものかもしれません。このとき
2つの違ったメソッドを呼び出し可能にするラッパーがAdapterです。
■Proxy
代わりに処理する。
たとえば、上記のサーバーを呼び出す場合、1回呼び出してしまったら、検索の場合、帰ってくる値は、2度目も3度目も同じでいいというケースがあります。(データベースのコードテーブルなどである)。
こういうとき、
クライアント側にも
execute(HashMap argMap);
というメソッドを用意し、このメソッドをみんな使ってもらい、
このメソッド内では、
・新規のアクセスの場合は、サーバーアクセスして
execute(HashMap argMap);
を呼び出し、その結果を返すと同時に、どこかにプールしておく
・2度目以降のアクセスの場合、プールした内容の中にあれば、
サーバーにアクセスしないで、プールした値を返す。
などというとき、クライアントのexecuteメソッドは、サーバーのexecuteメソッドのproxyであるといいます(HTTPのプロキシも、キャッシュを使って似たようなことしてる)
■Flyweight
オブジェクトの共有。singletonのとき、1個のオブジェクトしか作らないで共有しましたが、あれとおなじです。ただし、1個だけじゃなくって、複数個作って共有することもあります。
■Bride
機能のクラスと実装のクラスをわけます。
なにがいいの、とか、どうやるの。。っていうのは、
Java言語で学ぶデザインパターン入門
第9章(121ページから)を見てください。かならずしもメリットだけとは限りません(と思います。クラスが深くなりやすい)。
■Composite
違うものを1つの上位クラスにいれて、同じように扱う。
いろんな種類のあるものを(再帰とかするため)、まとめて扱いたいとき、上位クラスをつくって、そいつから、それぞれの種類のクラスをextendしてやるものです。
具体的に、違うものとしてファイルとディレクトリで、それを同じものとみなして再帰させるというプログラムが
Java言語で学ぶデザインパターン入門
第11章(153ページから)にあります。
■Decorator
付け足すとき、なんですけど、機能を追加する場合、包むことによって機能を追加する方法です。
ファイルの読み込み(FileReaderクラス)のとき、バッファを使って行うようにしようとした場合、BufferedFileReaderというクラスを、FileReaderから継承してつくって、
BufferedFileReader in = new BufferedFileReader(foo.in);
ってしても、いいんだけど、もしそうすると、今度はソケットを使いたい場合、BufferedSocketReaderっていうクラスを作って、
BufferedSocketReader in = new BufferedSocketReader("www.yahoo.co.jp",80);
ってやんないといけなくなってしまいます。
さらに、BufferdFileReaderを機能追加した、ExtraBufferdFileReaderっていうのを考えたら、それのソケット対応のために、ExtraBufferedSocketReaderっていうのもつくって。。。
入出力増えたらたいへんそう(>_<!)
つまり、機能を継承で追加すると。。。。大変なことになる場合があります。
java.ioでは、そういうことはしてなくて、FileReaderを、BufferedReaderがつつむことで、バッファリングした読み込み機能を追加する、つまり
BufferedReader in
= new BufferedReader(new FileReader("foo.in"));
とします。ソケットの場合
Socket socket = new Socket("www.yahoo.co.jp",80);
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
ってします。このとき、BufferedReaderは、ファイル用、ソケット用とは、別れません。
これは、継承でなく、委譲という方法を使って実現しているのですが、これが、Decoratorのパターンです。
具体的に、これをどうやって実現するのかとか、継承と委譲の説明その他もろもろは
Java言語で学ぶデザインパターン入門
第12章(169ページから)にあります。
だんだん面倒になってきて、後のほうはみんな
。。。を見てください
になってしまった(^^;)