2006 年 5 月 14 日 18 時 52 分

PuzzleSolver #2: 循環参照


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


VBA は COM の技術基盤を使用するので、
インスタンスの生存期間を、参照カウンタを使って管理する。

変数にインスタンスへの参照を格納した場合、
インスタンスが持つ参照カウンタを 1 つ増加させる。
スコープを外れる、または Nothing を代入すると、
インスタンスが持つ参照カウンタを 1 つ減少させる。
これらは内部的に行なわれている。

もし、クラスのフィールドとして、
別のクラスのインスタンスを格納した場合、
所有する側のフィールドが Nothing にならない限り
所有される側のインスタンスは解放されないことになる。

複雑なオブジェクトを設計する際に問題になるのは、
親子の関係を持つクラスを設計する場合だ。
親クラスは、利用側プログラムが自由に作成し、
子クラスは、親クラスが暗黙に作成するとしよう。

親は、通常子クラスのインスタンスを所有する。
親が子の参照を保有していないと、
親の知らない間に子が解放されてしまう可能性があるからだ。

しかし、子クラスにも親の参照を所有させてしまうと、
プログラムが親の参照を解放しても、
親子間でお互いの参照が残ってしまうため、
オブジェクトはいつまでたっても解放されずに残る。

これが、循環参照と呼ばれる状況であり、
VBA においては設計上避けなければならない事態である。

通常、親子間でやりとりが必要なことは多いが、
子クラスには親クラスの参照を持たせられないので、
親クラスのメソッドを呼ぶことはできない。

では、どうするか。

こういう場合は、親子間の通信を媒介するクラスを作る。
親や子は、その媒介クラスへの参照を保有し。
媒介クラスのメソッドやプロパティ、イベントを経由して
お互いにデータの交換や通知などを行なうのだ。

循環参照の問題があるので、
媒介クラスは、親や子の参照を保有することはできない。
なので、イベントを使って通知するか、
媒介クラス自体に重要なデータを集めておき、
親や子にはロジックのみを格納するなどの方法がある。

では、PuzzleSolver 周りのクラスの設計をしよう。

PuzzleSolver が親クラスとなる。
行や列のクラスである PuzzleLine クラスは、
PuzzleSolver の子クラスに相当することになり、
PuzzleSolver は、PuzzleLine のインスタンスを保有する。

媒介クラスになるのが、PuzzleAnswer クラスだ。
PuzzleAnswer クラスは解答データを保持し、
親子からデータを参照できるようにする。

PuzzleAnswer には、親子への参照を持たせず、
純粋に解答データを所有するだけの機能に限定する。
こうしておけば、PuzzleSolver が解放されれば、
PuzzleLine が解放され、PuzzleAnswer が解放される。

PuzzleLine が親を参照できないということで、
行や列が必要とするデータは全て PuzzleAnswer に持たせる。

PuzzleLine の役割は行や列の 1 並びを管理し、
それに対して解答処理を行なう機能に限定される。
これならば、PuzzleAnswer とのやり取りによって、
間接的に結果を親に返すことができる。

つまり、PuzzleAnswer の機能は、セルの状態を持つ
2 次元配列をラップしたようなクラスとなる。
IPuzzleLoader のように ArrayList にしても良いのだが、
PuzzleAnswer には更に機能を追加する予定があるので、
新たなクラスとして設計することにする。

PuzzleSolver の役割は、パズルの行列を管理することと、
適切な行や列の解答処理を連鎖的に呼び出して、
最終的な解答に導くロジックということになる。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.