bigsleepの日記

 | 

2012-03-17

読書メモ

10:32

増補改訂版Java言語で学ぶデザインパターン入門

を読みました。


色々

2004年の本らしい。元のGoFデザインパターンの本は1995年らしい。

GoFの方の本は昔読もうとして何だか抽象的でよく理解できなくて、途中で読むのを止めてしまった記憶がありますがこちらの本はとても読みやすく、わりとすらすら読めました。

実例がまずあってそれから詳しい話や抽象的な話に進むという感じで、理解しやすいような気がします。

言語

例はJavaで書かれてて、本にCDが付属してこれにサンプルコードが入ってました。

GoFの方はC++の例が書かれているみたいです。

デザインパターン自体は言語に依存しないものらしいんですが、

静的型付と動的型付の言語とか、総称型があるかないかとかで設計は変わってくると思うのでそれぞれのデザインパターンの出現頻度とか重要度とか実装とかも変わってくると思います。

静的型付の言語をベースにして考え出されたパターンだというのを意識したほうが理解しやすい場合もあるかもしれません。

Javaジェネリクスというのが入ったのは2004年頃らしいのでこの本では使われてません。

C++テンプレートが入ったのかいつか知らないんですが、GoFの本では使ってるんだろうか。

あと、マルチスレッド編の本もあるらしく、マルチスレッドで一冊の本になるってことはそれだけできることが増えるとか、設計がむずかしいってことなんだろうか。

以下は各パターンのまとまりのないメモ。

Iterator

Javaってポインターないんだ。

自分はC++を使うのでstlコンテナとアルゴリズムの話が思い浮かびました。

コンテナそれぞれにアルゴリズムを実装するとコンテナの数×アルゴリズムの数の実装が必要になるけれど、イテレーターを使うようにするとアルゴリズムが再利用できる。

Adapter

Javaって関数ポインターないんだ。

関数ポインターを受け取るAPIとか、関数オブジェクトを受け取るAPIだと色々と違ってくると思います。

C++関数オブジェクトをとる場合にstd::bindとかで引数の数とか並べ方とかを変えるようなこともこのパターンに入るような気がします。

Template method

なんらかの処理の流れをインターフェースで規定して、派生クラスで実装の詳細をオーバーライドさせる。

C++テンプレートは関係ない。

Template methodをインスタンス生成に適用したのがFactory method。

インターフェースと実装の詳細を分離するというのが他のパターンでもちょくちょくでてきます。

細部実装の変更があってもクライアント側に変更が必要が無いようにする。

pimplイディオムクライアントコードの再コンパイルが必要無いようにするとかの意味もある。

Factory method

Template methodをインスタンス生成に適用したもの。

それぞれの部品の作り方などを派生クラスで実装して、組み立て工程などを共通化する。

Sigleton

生成されるインスタンスを一つだけにする。

C++だとなんか色々な実装方法があってModern C++ Designか何かの本に色々書いてあったような気がしますがどういう実装がよいんだったかはあまり覚えてません。

コンストラクタをprivateにしてstaticなget_instanceみたいな関数でstatic変数インスタンスを渡すというような実装はたまに見るような気がします。

Prototype

オブジェクトプロトタイプを保持しておいてこれをコピーしてインスタンスを生成する。

型で表すと種類が多くなりすぎる場合や、状態が複雑で一から作りにくい場合等に使うようです。

C++だと動的に型名からインスタンスを得たりできないので、色々な派生クラスのインスタンスをmapとかに入れておいてこれをCloneして使うとかやることがあるような気がします。

Builder

これはなんかAbstruct factoryとの違いがよくわからないんですが、生成するものは抽象クラスではないようなのでクライアント側は生成するオブジェクトの型を知っているようです。

ある決まった型のオブジェクトの生成方法を色々選択できる。

組み立ての工程など実装の詳細は隠蔽される。

Abstruct factory

これはわりと有名というかよく聞くパターンのような気がします。があまりよくわかってません。

プロダクトとファクトリーに抽象クラスを使って、動的に生成するプロダクトを変えられるようにするというところが特徴なんだろうか。

インスタンス生成のインターフェースと実装の詳細を切り離すというところはFactory methodと同じような気がします。

動的型付の言語だと動的に変えられるって普通にできそう。

Bridge

機能を追加するための継承と、インターフェースを派生クラスでオーバーライドする実装のための継承があって継承の階層が深くならないようにするためのパターンのようです。

Exceptional C++には機能の再利用のための継承はしてはいけないというようなことが書いてあったので、このあたりは意見が分かれるところかもしれません。

機能階層のベースクラスで、実装階層のベースクラスをメンバとして保持して委譲で利用する。

機能の追加も委譲でいい場合もありそう。

Stategy

アルゴリズムや処理の内容を入れ替えられるようにするパターン。

このあたりも関数オブジェクトであれば関数をわたすようなインタフェースになるような気がします。

あとオブジェクトの型によって処理を変える場合にtraitsを使うのも似たパターンな気がします。

Composite

木構造など再帰的な構造を表現するパターン。

派生クラスが自身のベースクラスをメンバとして持ったりする。

Chain of Responsibilityのところを見るとJavaだとクラスのメンバにそのクラス自体を使えるんですね。

C++だと直接再帰的にはできないのでBoost.Variant使ったり、vectorでメンバに持ったりすることがあります。

Decorator

これも再帰的構造のようです。

飾り付ける対象となるオブジェクトと飾り付けを加えたオブジェクトとに共通のベースクラスを継承させて同一視して再帰的に飾り付けを重ねることができるようにする。

Visitor

こないだ読んだというか読んでる途中のふつうのコンパイラをつくろうという本にちょうどVisitorパターンがでてました。

それぞれのオブジェクトメソッドに処理を書いても同じことはできるけど、ファイルがバラバラになってしまって見づらいとかメソッドにするべきでないような処理を書く場合に使うようです。

コンテナとかでは普通ベースクラスにキャストされて保持されているのでそのままVisitorにオブジェクトを渡すと元の型がわかりません。なのでVisitorをacceptして自身を渡すという関数をオーバーライドさせます。

Visitorにはそれぞれの派生クラスのための関数オーバーロードで実装します。

動的型付だとオーバーロードなかったりするのでacceptでvisitor.visit_hogetype(self)とか別々の名前の関数を呼ぶ感じになるのかな。

それかMix-inとかにしたほうがいいのかもしれません。Mix-inってよく知らないんで違うかも。

Chain of Responsibility

これも再帰的構造のようです。

クラスが自身のクラスをメンバに持っていて連鎖して処理をたらい回す。連結リストっぽいです。

これはvectorとかに入れてもいいのではと思ったんですが、昔は標準ライブラリとかあまりなかったからなのかな。

Facade

FacadeMediatorオブジェクト間の関係を整理するようなパターンのようです。

クライアント側から複数のオブジェクトを複雑に操作する必要がある場合に窓口となるクラスを作ってそのクラスに処理をやらせる。

これもインターフェースを規定して実装の詳細は隠蔽するという意味もあるようです。

Mediator

Facadeは一つとたくさんの間の関係で、Mediatorはたくさんのオブジェクト同士の関係を整理するためのパターン。

オブジェクト間で直接やりとりせずにMediatorを介してやりとりするようにする。

Observer

割とよく聞くパターンのような気がします。が実際自分でちゃんと書いたことはないような気がしますが。

GUIプログラムなどでよく使われます。

MVCのModelが変更された時にViewに通知して表示を変更させる。

C++だとBoost.Signals2が使えます。

Memento

オブジェクトの状態を保存するようなクラスをつくって、undoやredoをできるようにする。

これもあまり使ったこと無いんですが、情報が多い場合は差分を保持したりするんだろうか。

例えば与えられた処理を覚えておいてその逆処理というか逆関数のような処理ができるようにしておいてundoするとかもできるかも。

State

これもまだ使ったことないような気がします。

こんどゲームなど作るときはBoost.Msmなどを使って見たいと思います。

Flyweight

あまり使ったことないかと思ったんですが、ゲームなどでキャラクターの画像を使いまわしたりするのも同じようなことかもしれません。

インスタンスを作るコストとか、インスタンスの種類、インスタンスの数とかで使った方がいいのかどうか分かれそうです。

Boost.Flyweightも使ったことありません。

Proxy

これは遅延評価のためのパターンかもしれません。

インスタンス生成を遅らせて必要になったときだけ作る。

Boost.Ublasの実装にmatrix_proxyとかいうクラスが出てきたような記憶があります。

Command

イベントと呼ぶことの方が多いような気がします。

イベントハンドラーとして関数や、関数オブジェクトをセットするようなインターフェースが多いと思います。

GUIのツールセットなどには大抵なにかしらのイベントの仕組みがあります。


Interpreter

LuaとかSquirrelとか使うのがこれっぽいです。

自分で実装するのは大変そうなので既成のものを使うことが多そうです。

動的言語でも使うことがあるんだろうか。

 |