オブジェクト指向における再利用のためのデザインパターン改訂版 読書ノート 3/3¶
- 著者:
Eric Gamma, Richard Helm, Ralph Johnson, John Vlissides
- 監訳者:
本位田真一、吉田和樹
- 出版社:
ソフトバンク クリエイティブ株式会社
- 発行年:
1999 年
- ISBN:
978-4-7973-1112-9
第 5 章 振る舞いに関するパターン¶
Chain of Responsibility¶
複数オブジェクトに要求を処理する機会を与える。要求の送信・受信オブジェクト間の結合を避け、オブジェクトをチェーンでつなぐ。
動機として、GUI アプリで状況依存ヘルプ機能について考察している。
ヘルプ情報は <もっとも特殊なものからもっとも一般的なものへと組織化していく> (p. 237)
印刷ボタン→印刷ダイアログ→アプリケーション、のような順にヘルプ優先度がある感じがわかる。
<ヘルプ要求を出すオブジェクトをヘルプ情報を与えるオブジェクトから引き離す方法> (p. 237) が必要。
<このパターンのポイントは、複数のオブジェクトに要求を処理する機会を与えることにより、要求を送信するオブジェクトと受信するオブジェクトを引き離すことにある> (p. 237)
<チェーンの中にある各オブジェクトは、要求を処理したり後続のオブジェクト (
successor
) にアクセスするための共通のインタフェースを持っている> (p. 238)ヘルプの要求を処理したいクラスは、
HelpHandler
を親クラスにする (p. 238)
結果
結合度を低くする。
責任分散を柔軟にする。
要求が受信されるかどうかは保証されない。
実装
チェーンの実装
オブジェクト間に既存の参照関係があれば、それを流用して
successor
チェーンとすることもあり。
チェーンの接続
Handler
クラスでは、通常はsuccessor
も保持する。要求をsuccessor
に転送することを実装のデフォルトとする。
要求の表現
要求を
Request
オブジェクトとしてカプセル化してHandleRequest
に渡すのが普通。<サブクラスは自らが関与すべき要求のみを扱う。他の要求は転送する> (p. 242)
サンプルコードではさっきのヘルプの実装を検討している。
<既存のウィジェット階層での親オブジェクトへの参照を利用する> (p. 243) ことで、ヘルプ要求を伝えていく方法をとる。
「チェーンの最初」といったら、一番最初に要求を処理するチャンスのあるオブジェクトを指すようだ。
<ただし、
successor
はWidget
オブジェクトではなく、任意のHelpHandler
オブジェクトである> (p. 245)
使用例
<ユーザのイベントを処理するために Chain of Responsibility パターンが使われている> (p. 246)
<ユーザがマウスをクリックしたりキーを押したりすると、イベントが生成され、チェーンに沿って伝えられていくことになる> (p. 246)
関連するパターン
<しばしば Composite パターンとともに適用される。その場合、
component
の親オブジェクトをsuccessor
にすることができる> (p. 247)子(持たれている方)が先に要求を処理するチャンスがあって、無視するならば親(所有者)に転送、か。
Command¶
要求をオブジェクトとしてカプセル化
取り消し可能なオペレーションをサポート
<ツールキットはボタンやメニューの中で、直接、要求を実装することはできない> (p. 249)
<要求自身をオブジェクトにすることにより、ツールキットのオブジェクトが、仕様化されていないアプリケーションオブジェクトの要求を作成できるようにする> (p. 249)
<もっとも簡単な形としては、このインタフェースに、抽象化した
Execute
オペレーションを入れておく> (p. 249)<
Command
の具象クラスが受信オブジェクトとアクションの組を明らかにする。受信オブジェクトには、要求を実行するために必要な知識がある> (p. 249)PasteCommand
の場合、受信オブジェクトはDocument
である。<
command
をつなぎ合わせるのは一般的なので、MenuItem
オブジェクトが複数のcommand
に対してExecute
オペレーションの呼び出しを行うことができるように、MacroCommand
クラスを定義する> (p. 251)図を見ればこれがすぐに Composite パターンだとわかる。
動的に
command
を入れ替えることができるということが、状況依存のヘルプを実装するのに便利。command
を複数合成することで、コマンドスクリプトをサポートすることができる。
適用可能性
<Command パターンでは、そのようなコールバック関数の代わりにオブジェクトを使う> (p. 251)
なるほど。Command パターンはコールバック関数の進化系なんだろう。
<
command
での処理の結果を再び元の状態に戻すことができるように、状態を保存するようにしておくことができる> (p. 252)<呼び出し結果を元に戻す
Unexecute
オペレーション> (p. 252)実行
command
を履歴リストに蓄えておき、<取り出しや再実行は、このリスト内を前後に移動しながら> (p. 252)Execute
/Unexecute
の連続呼び出しでUndo
/Redo
を実装できる、か。
<一般に、1 つのトランザクションは、データに対する更新手続きの集合をカプセル化している> (p. 252)
結果のリストを見ると、このパターンにはデメリットがないようだ。
実装
<極端な場合、単に
Receiver
オブジェクトと要求を実行するアクションを結び付けるだけのものから、Receiver
オブジェクトにはまったく委譲することなくそれ自身ですべてを実装してしまうものまで考えることができる> (p. 254)さっきの例で言うと
PasteCommand::Execute
がDocument::Paste
メソッドを呼び出すだけか、貼り付けロジックをPasteCommand
が独自に実装するかの違いが考えられるということ。Undo
/Redo
をサポートする場合、<Receiver
オブジェクトは、自身を元の状態に戻すことができるようなオペレーションをcommand
に対して提供しなければならない> (p. 254)履歴リストも必要。リスト内を時系列順に移動するような。
<たとえば、選択された複数のオブジェクトを削除する
DeleteCommand
オブジェクトは、それが実行される際には、削除されるオブジェクトの集合を保持しておかなければならない> (p. 255)ヒステリシス。
Undo
/Redo
を反復実行すると、何かがまずくて元の状態とは異なってくる様。<
command
が他のオブジェクトの内部に踏み入ることなくこの情報にアクセスできるように、Memento パターンを適用することができる> (p. 255)
サンプルコード
<取り消しできない、または引数を必要としない簡単な
command
については、Receiver
オブジェクトをパラメータ化するためにクラステンプレートを使うことができる> (p. 257)// p. 257 template <class Receiver> class SimpleCommand : public Command{ public: typedef void (Receiver::*Action)(); SimpleCommand(Receiver*, Action); // ... };
ただし、このクラスのコンストラクタ呼び出しはコードを書くのが面倒。
MacroCommand
クラスにUnexecute
オペレーションを実装する場合、逆順にcommand
をたどってUnexecute
を実行しなければならない (p. 258)<
command
を削除するのはMacroCommand
クラスの責任である> (p. 258)
Interpreter¶
真面目に読む気なし。
運用適用性
<文法が単純な場合> (p. 263)
<効率が重要な関心事ではない場合> (p. 263)
結果
<文法が複雑なときには、パーザやコンパイラジェネレータのような他の技術を使うほうが適当だろう> (p. 265)
実装
<Interpreter パターンと Composite パターンは、実装上の問題において多くの共通点を持っている> (p. 265)
サンプルコード
<C++ で実装された Bool 表現を操作・評価するシステム> (p. 269) の例。これは先に利用例を見てから、各メソッドを見ていくのが理解がいいと思う。
// p. 272 一部改変 VariableExp* x = new VariableExp("X"); VariableExp* y = new VariableExp("Y"); BooleanExp* expression = new OrExp( new AndExp(new Constant(true), x), new AndExp(y, new NotExp(x))); Context context; context.Assign(x, false); context.Assign(y, true); bool result = expression->Evaluate(context);
<Interpreter パターンには、Composite パターンを使ったクラス階層上に 1 つのオペレーションを分散させる、ということ以上の意味がある> (p. 273)
使用例
<オブジェクト指向言語により実装されたコンパイラでは広く使われている> (pp. 273-274)
<もっとも一般的な形式(つまり、1 つのオペレーションを Composite パターンに基づくクラス階層上に分散させるような場合)を考えると、Composite パターンはほとんどの場合に Interpreter パターンを含んだ形で使われる。しかし Interpreter パターンは、クラス階層を言語を定義するものとして考えた場合に限り使用すべきである> (p. 274)
Iterator¶
動機
<リストのような集約オブジェクトは、その内部構造を明かすことなく、要素にアクセスする方法をユーザに対して提供するべきである> (p. 275)
<アクセスや走査のための責任を抜き出して、これを iterator オブジェクトに与えるということである> (p. 275)
<走査のメカニズムを
List
オブジェクトから切り離すことで、List
インタフェースを使って要素を列挙していく以外にも、走査について異なる方針を持ったiterator
を定義することができるようになる> (p. 276)<
CreateIterator
オペレーションは、factory method の使用例になる> (p. 276)クラス図によると
AbstractList
のメソッドにCreateIterator
がある。List
はListIterator
を返し、SkipList
はSkipListIterator
を返す。
結果
<複雑な aggregate には、走査の方法がたくさんあるだろう> (p. 278)
<コード生成では、構文解析木を inorder に走査することもあれば、preorder に走査することもあるだろう> (p. 278)
<複数の走査を同時に実行することができる> (p. 278) とあるが、そういうケースを何か例示して欲しい。
実装
<外部 iterator は内部 iterator に比べてより柔軟である> (p. 278)
iteration を制御するのがクライアントか iterator 自身かの違いによって、外部 iterator と呼んだり内部 iterator と呼んだりするようだ。
<aggregate が走査のアルゴリズムを定義していて、iterator は単に iteration の状態を保持しておくためだけに使われるのかもしれない> (p. 279)
<走査の最中に aggregate に要素を追加したり、また削除したりすると、ある要素に 2 回アクセスしてしまったり、またはまったくアクセスしなかったりということが起こりかねない> (p. 279)
これはよくミスるんだ。
<iterator には特権的なアクセス権を持たせてもよい> (p. 280)
<外部 iterator を、Composite パターンで示されるような再帰的な集約構造上で実装するのは難しいだろう> (p. 280)
<構造内でのパスを保存しておかなければならない> (p. 280)
構造内のノードが兄弟、親、子ノードをたどれる場合は、cursor ベースの iterator のほうがよい。
<composite の構造は、しばしば 2 種類以上の方法で走査する必要がある。 preorder, postorder, inorder, breadth-first などの走査が一般的である> (p. 281)
関連パターン
<iterator は、iteration の状態を把握するために memento を使うことができる> (p. 290)
Mediator¶
相互作用をカプセル化する。<オブジェクト同士がお互いを明示的に参照し合うことがないように> (p. 291)
<オブジェクト間の関連を増やすことがせっかく高めた再利用性を再び低める傾向がある> (p. 291)
<しばしば、ダイアログ内のウィジェット間には依存関係がある> (p. 292)
<別のダイアログボックスでは、ウィジェット間に異なる依存関係が存在するだろう> (p. 292)
例えば
FontDialogDirector
クラスを定義し、それに <ウィジェット間の通信におけるハブ> (p. 292) として活躍させる。ウィジェットは他のウィジェットのことを知っている必要がなくなる。
<オブジェクトの集まりが通信する場合> (p. 294) に Mediator パターンを適用する可能性がある。
<
mediator
自体を保守が難しい一枚岩> (p. 296) になる。colleague
からmediator
への通信手段だが、<1 つのアプローチとしては、Observer パターンを使って Mediator クラスを Observer として実装することがあげられる> (p. 296)<Mediator パターンのもう 1 つの適用例として、複雑な更新を調整する場合があげられる。例としては、Observer パターンで説明する
ChangeManager
クラスがあげられる。(略)ChangeManager
オブジェクトは、変化が起こったオブジェクトに対して依存関係にあるオブジェクトにそれを知らせることにより更新を行う> (p. 300)オブジェクト間の依存関係が複雑な場合の更新調整という意味だろうか。
Memento¶
オブジェクトを後にある時点の状態に戻すことができるようにするというパターン。
<memento は、別のオブジェクトの内部状態のスナップショットを保存するオブジェクトである> (p. 304)
<別のオブジェクト> のことを memento に対して originator と呼ぶ。
originator は要求に応じて memento を返す。
<
Caretaker
クラスには、Memento
クラスの narrow インタフェースが見えるようになっている> (p. 305)とにかく
Caretaker
オブジェクトは、Memento
オブジェクトの中身を細かくいじるようなことはない。<それとは対称的に、
Originator
クラスには wide インタフェースが見えるようになっている> (p. 305)Memento
オブジェクトを生成する役割があるから、Originator
はMemento
のことをよく把握している必要がある。<理想的には、
Memento
オブジェクトを生成したOriginator
オブジェクトだけがMemento
オブジェクトの内部構造にアクセスすることを許されるようにする> (p. 305)Memento
のデータ量が多いケースでは、コストが高くつく。「差分」だけを保存しておけば済むようにできるなら、そうする。Caretaker
はMemento
を管理する。オブジェクトを削除する責任がある (p. 307) ということ。
Observer¶
<あるオブジェクトが状態を変えたときに、それに依存するすべてのオブジェクトに自動的にそのことが知らされ、また、それらが更新されるように、オブジェクト間に一対多の依存関係を定義する> (p. 313)
<関連するオブジェクト間で無矛盾性を保つ必要がある> (p. 313)
しかし、そのためにクラス間の結合度を高めるようなことはしたくない (p. 313)
スプレッドシートとバーチャートの例え (p. 313) は、<同じデータに対して異なるユーザインタフェースがいくつあっても構わない> (p. 314) ということを示したい。
<
subject
には、それに依存するobserver
を任意の数だけ持たせることができる> (p. 314) ということは、極端な話ゼロでも構わない(意義があるかどうかは置いて)。
適用可能性のところに色々書いてあるが、基本的には<1 つのオブジェクトを変化させるときに、それに伴いその他のオブジェクトも変化させる必要があり、しかも変化させる必要があるオブジェクトを固定的に決められないとき> (p. 314) 状況で決まりだろう。
subject
はobserver
を知っている。observer
は更新のインタフェースを定義する。ConcreteSubject
はConcreteObserver
に影響する状態を保存している。ConcreteObserver
はConcreteSubject
への参照を保持している。<通知を得るまでには自身の状態の更新を延ばしている> (p. 316)
<
subject
とobserver
の結合は抽象的であり極小である> (p. 316)<
observer
同士は互いに相手の存在を知らないため、subject
の変化に伴うコストの総計をobserver
が予測することはできない> (p. 316)
このパターンは記述量がけっこうある。
<
subject
が多くてobserver
が少ないときにはコストが高くつく> (p. 317)1 つの
observer
が複数のsubject
に依存しているような場合、<どのsubject
が通知を送ったのかをobserver
に知らせるようにUpdate
オペレーションインタフェースを拡張する必要がある> (p. 317)<どのオブジェクトが
Notify
オペレーションを呼び出すことになるのか> (p. 317) だが、subject
にやらせるにせよobserver
にやらせるにせよ、トレードオフがある。<
subject
が削除される際に、observer
に対してsubject
への参照をリセットするように通知を出すようにすること> (p. 317) を検討する。<
Subject
クラスのどのオペレーションが通知のきっかけを作るのかは、文書化しておくのがよい> (p. 318)subject
の変更情報をどのようにobserver
に引き渡すかで、push 型と pull 型に分類できる。これもトレードオフがある (pp. 318-319)subject
とobserver
の依存関係が複雑なときには、間にワンクッションChangeManager
オブジェクトのようなものをはさんで、依存関係や変更通知を管理させる場合がある (pp. 319-320)
サンプルコードは「時計」の実装例。タイマーが Subject
で、各種時計が
Observer
だ。
State¶
<クラス内では、振る舞いの変化を記述せず、状態を表すオブジェクトを導入することでこれを実現する> (p. 325)
TCPConnection
の例では、established, listen, closed の状態をそれぞれクラスとして表現する。<このパターンでキーとなる考え方は、
TCPState
クラスと呼ばれる抽象クラスを導入することである> (p. 325)パターン適用可能性としては、<オペレーションが、オブジェクトの状態に依存した多岐に渡る条件文を持っている場合> (p. 326) 等がある。
Context
クラスがState
オブジェクトを持つ。<状態に依存した要求を
ConcreteState
オブジェクトに委譲する> (p. 327)<
ConcreteState
オブジェクトに対して自身を引数として送る> (p. 327)
状態遷移は
Context
クラスかConcreteState
クラスが決定するらしい (p. 327)どちらでも OK ということか。
結果
<個々の状態に対する振る舞いを
State
のサブクラスに分配するため、クラスの数は増え、1 つのクラスを利用する場合よりもコンパクトではなくなるという問題である。しかし、多くの状態が存在する場合には、このように分配することにより、実際に良い効果が得られる。なぜならば、もしこの方法を用いていなければ、多数の条件文が必要になるからである> (p. 327)<実行状態という概念をオブジェクトの地位にまで引き上げる> (p. 327)
<
Context
クラスが矛盾した内部状態を持つのを防ぐことができる> (p. 328)もし
ConcreteState
が独自の変数を持たないならば、Flyweight パターンも検討。
実装
<どの構成要素が状態遷移の規準を定義するのかを特定していない> (p. 328)
<
State
のサブクラス自身が次の状態と遷移の時期を特定できれば、一般的により柔軟で適切なものになる> が、サブクラス間に依存関係が入り込むことになる。
テーブル検索型の状態遷移も紹介していて、利点と欠点を挙げている。決定的な欠点は遷移の規準が不明確になることだろうか。
Context
クラスがどの程度の頻度で状態を変えるかによって、ConcreteState
オブジェクトの生成・破棄の戦略を決めるのがよい。<委譲ベースの言語> (p. 329) とは何だろう。
サンプルコード
TCPConnection
TCPState
をfriend
宣言している。void ChangeState(TCPState*)
メソッドを提供する。この例の運用では、コンストラクタで
TCPClosed
に状態メンバーをセットする。
TCPState
多くの TCP 関連メソッドは空実装。当然仮想関数。
ここにも
ChangeState
という名のメソッドがいる。// p. 331 void TCPState::ChangeState(TCPConnection* t, TCPState* s){ t->ChangeState(s); }
ConcreteState
この例ではいずれも <ローカルな状態> を保持しないので、各サブクラスを Singleton とする。
例えば
TCPListen::Send
メソッドの実装は、まず SYN や ACK の送受信処理を行ってから、最後にChangeState(t, TCPEstablished::Instance());
のようにする。
使用例
<インタラクティブ描画プログラム> における <ツール> について説明。
<たとえば、線描画ツールは、新しい線を生成するためにユーザにクリックとドラッグを行わせる。選択ツールは、ユーザに図形を選択させる> (p. 333)
Tool
のサブクラスとして線描画ツールやら選択ツールやらが定義されていて、クリックやドラッグのアプリケーション内での振る舞いが実際にはサブクラスに委譲されているので、それぞれ異なる。
Strategy¶
Strategy はカプセル化された交換可能なアルゴリズム (p. 335)
別名が Policy になっている。
テキストストリームを取り扱う方法を例に話が進む。
<特に、改行について複数のアルゴリズムをサポートする場合> (p. 335)
<テキストをフォーマットするときには、
Compositor
のオブジェクトに対してこの責任を委譲する> (p. 336)
適用可能性も色々挙げているが、基本はこれだろう。
多くの振る舞いが <複数の条件文として現れている場合> (p. 336)
構造、構成要素、協調関係について。
Strategy
がアルゴリズムに共通のインタフェースを宣言する。ConcreteStrategy
がアルゴリズムを実装する。Context
がStrategy
を利用する。アルゴリズムに必要なデータを引き渡したりするのかもしれない。色々なアルゴリズムをサポートするのに
Context
を派生させない理由は、 <アルゴリズムの実装とContext
クラスの実装が混ざってしまい、Context
クラスを理解し、保守し、拡張することをより難しくしてしまう> (p. 338) から。わざわざアルゴリズムを独立させている。<振る舞いの種類がクライアントに関係がある場合にのみ、Strategy パターンを利用するべきである> (p. 339)
Context
はConcreteStrategy
が効果的にアクセスできるようにするべし (p. 339)C++ の場合、テンプレートを利用して
Strategy
をコンパイル時に選択させることができる (p. 340) もっとも、Strategy
を動的に変更できなくて構わない場合に限る手段だが。
サンプルコードの Compose
メソッドは引数リストがゴチャゴチャしてないか?
Template Method¶
<アルゴリズムのスケルトン> (p. 347)
<その中のいくつかのステップについては、サブクラスの定義に任せることにする> (p. 347)
またぞろ Application
と Document
クラスの例を挙げ、Document
を「開く」オペレーションについての議論。
// pp. 347-348; 一部省略
void Application::OpenDocument(const char* name){
if(!CanOpenDocument(name)){
return;
}
Document* doc = DoCreateDocument();
if(doc){
_docs->AddDocument(doc);
AboutToOpenDocument(doc);
doc->Open();
doc->DoRead();
}
}
<
OpenDocument
オペレーションは、文書を開くための各ステップを定義する> (p. 348)OpenDocument
はおそらく仮想関数になっていなくて、この中の各呼び出しメソッドがApplication
やDocument
の仮想関数になっている。 <抽象オペレーションを使ってアルゴリズムのいくつかのステップを定義することにより、template method はそれらの順番を固定する> (p. 348)
適用可能性にいいことが書いてある。
<まず、既存のコードにおける相違点を識別し、次にその相違点を新しいオペレーションに分離する。最後に、既存のコードを、その相違点については新しいオペレーションを呼び出すようにした template method で置き換える> (p. 348)
このセクションは短い。
<template method は、コード再利用のための基本的な方法である> (p. 349)
ハリウッドの原則
<hook operation は、デフォルトでは何もしないようにしておくことがしばしばある> (p. 350)
実装のコツ
C++ では
primitive operation を
private
宣言する (p. 351)<template method は非仮想関数として宣言しておく> (p. 351)
primitive operation の数を最小化すること (p. 351)
名前を見て template method, primitive operation とわかるようにすると便利 (p. 351)
<template method はたいへん基本的なもの> (p. 352)
Visitor¶
最初に読んだときにクラス構造が頭にストンと入らなかったパターン。
<オペレーションを加えるオブジェクトのクラスに変更を加えずに、新しいオペレーションを定義することができる> (p. 353)
動機
構文木の例
Node
のサブクラスにVariableRefNode
やAssignmentNode
等がある。<数多くのノードのクラスにわたってこれらのオペレーションを分散させることが、システムを理解しにくく、保守しにくく、変更しにくくしてしまう> (p. 353)
各ノードに
TypeCheck
やらGenerateCode
が分散している。三重苦状態なわけだ。<そこで、関連するオペレーションを各クラスから取り出して別のオブジェクトにまとめ、アブストラクト・シンタックスツリーを走査するときにその要素にこのオブジェクトを渡す、というアプローチをとることができる> (p. 354)
別のオブジェクトが visitor と呼ばれるもの。
TypeCheckingVisitor
やらCodeGeneratingVisitor
やらだ。
<Visitor パターンでは、2 つのクラス階層を定義する> (p. 355)
オペレーションを加えられる側 (
Node
)オペレーションを定義する側 (
NodeVisitor
)
適用可能性
適用条件の記述が割と細かい。
<オブジェクト構造を定義するクラスはほとんど変わらないが、その構造に新しいオペレーションを定義することがしばしば起こる場合> (p. 355)
構成要素
ConcreteVisitor
クラスは <構造を走査していく過程で、状態に結果が蓄積されていくことがしばしばある> (p. 356)Element
クラスは <引数としてvisitor
をとるAccept
オペレーションを定義する> (p. 356)ConcreteElement
クラスがAccept
の実装をする。
結果
<Visitor パターンでは、
Element
の新しいサブクラスを加えることを難しくする> (p. 358)<新しい
ConcreteElement
クラスがひんぱんに追加されるときには、Visitor
クラスの階層を保守することが難しくなる危険性がある> (p. 358)<
visitor
によるアプローチでは、ConcreteElement
クラスのインタフェースが、visitor
が仕事を行うのに十分、強力であることを仮定している> (p. 359)Element
側のカプセル化がもろくなる可能性を指摘している。
実装
初見ではコード例が頭に入らなかった。練習問題として、
Visitor
ElementA
,ElementB
ConpositeElement
クラスを定義してみよう。
CompositeElement::Accept
の実装方法に注意。ダブルディスパッチの話題が出てくる。
Accept
がそれなのだが、<ダブルディスパッチとは、単に、実行されるオペレーションが要求の種類と 2 つの受け手の型に依存することを意味している> (p. 361)
Visitor
の型とElement
の型がAccept
を決める。
サンプルコード
<visitor は通常 composite と関連がある> (p. 362) というわけで Composite パターンの説明で出てきた
Equipment
クラスを引っ張り出す。<
Equipment
クラスはとても簡単なので、実際には Visitor パターンを利用する必要はない> (p. 362)
Visitor の利用例コードを見落としがちだが、以下のようになる。
Equipment* component; // ... component をどこからか得る。 InventoryVisitor visitor; component->Accept(visitor); // visitor 内部に Inventory 情報が蓄積された。
単に
Accept
呼び出しだけだ。
使用例
<Inventor では、3 次元のシーンをノードの階層として表現する。それぞれのノードは、幾何学的な図形オブジェクト、あるいはその属性のどちらかを表現している。シーンを描写したり入力イベントをマッピングするオペレーションは、それぞれ別の方法でこの階層を走査する必要がある> (p. 366)
描画、イベント処理、検索、バウンディングボックス計算等々、それぞれの用途に専用の visitor が存在すると言っている。
まとめ¶
まだ全部読み切っていない。
Observer パターンでは <
observer
とsubject
が制約を維持するために協力し合わなければならない> (p. 370)<
mediator
を再利用可能なものにするよりも、observer
とsubject
を再利用可能なものにする方が容易なのは明らかである> (p. 370)<Observer パターンよりも Mediator パターンの方が通信の流れを理解するのは容易である> (p. 370)
<協力し合うオブジェクトが直接お互いを参照しているときには、(略)システムの階層化と再利用性に対してマイナスの効果を及ぼす> (p. 371)
<Observer パターンは、Command パターンよりも送信―受信オブジェクトの結合をさらにゆるく定義する> (p. 371)
<
mediator
は、さらに柔軟性を得るためには独自のディスパッチスキーマを実装しなければならないだろう> (p. 372)
第 6 章 終わりに¶
<本書は単に既存の設計法について述べたものである。本書はチュートリアルとしては妥当だが、熟練したオブジェクト指向設計者にはあまり役に立たないと思われるかもしれない> (p. 375)
<読者がいかにデザインパターンを見つけてカタログ化していくことができるかについて述べる> (p. 375)
6.1 デザインパターンに何を期待するか¶
<デザインパターンを用いることで、より高いレベルで設計し、設計について議論することが可能になるのだ> (p. 376)
<十分に長い間オブジェクト指向システムに従事すれば、自力でデザインパターンを習得することができるだろう。しかし、本書を読めばはるかに速く習得できるはずである > (p. 376)
<デザインパターンは分析モデルから実装モデルへの転換のときに特に効果がある> (p. 377)
<柔軟で再利用可能な設計には、分析モデルには存在しないオブジェクトが含まれる> (p. 377)
<進化を続けるためには、ソフトウェアは“リファクタリング”と呼ばれるプロセスによって作り直さなければならない> (p. 378)
<優秀は設計者はリファクタリングが必要になるような変更には気付くものである> (p. 378)
6.2 経緯¶
<本書のカタログは Erich の学位論文の一部として始まった> (p. 378) おお、学位論文なのか。
<しかし、パターンを理解できるのは、すでにパターンを使ったことのある人に限られていた> (p. 379)
<なぜ行っているのかを理解することは、何をしているかを理解するよりも難しい> (p. 379) これはいい言葉だ。
付録以降¶
ノートに取るほどの重大な記述はなさそうだ?