2006 年 5 月 17 日 20 時 33 分

PuzzleAnswer: 解答の保持


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


設計が続く。今日は PuzzleAnswer だ。
解答を保持するクラスである。
PuzzleSolver や PuzzleLine から使われ、
解答状況は全てここに格納する。

PuzzleAnswer は解答欄であるため、
2 次元配列を模倣するのが好ましい。

メソッドやプロパティ。
・内部コンストラクタ
・セル値の取得・設定:(インデクサ)
・幅の取得: Width
・高さの取得: Height

基本はそれだけだ。これらを格納するフィールドを考える。
・セル値: m_eStates() ※ 2 次元配列
・幅: m_lWidth 
・高さ: m_lHeight

いままで、幾つかの配列系クラスを作ったので基本は同じ。

内部コンストラクタは、手抜きをして、
IPuzzleLoader を引数として受け取ることにする。
そうすれば多少楽になる。

あと、PuzzleAnswer には特別な機能をつけてみよう。

PuzzleLine は、行・列クラスなので、
例えば、どこかの行クラスがセルを変更した場合、
必ずどこかの列クラスが影響を受けるはずだ。

PuzzleLine が内部で保持している状態は、
外部からのセルの変更によって更新されなければならない。
つまり、PuzzleLine に通知する仕組みが必要だ。

PuzzleLine に通知用メソッドを用意する手もあるが、
ここではスマートに、PuzzleAnswer にイベントを作ろう。
つまり、セルの変更が発生した際に、通知するイベントだ。

PuzzleAnswer への参照を保持するクラスは、
このイベントを受け取ってアクションを起こすことができる。
PuzzleLine が、別の PuzzleLine に変更を通知するより、
間接的に PuzzleAnswer から通知したほうがスマートなのだ。

では、実装してみよう。

========== クラスモジュール: PuzzleAnswer ==========

'##################################################
'# パズルの解答を格納するクラス
'# 状態の管理を全てここで行なうと共に、
'# 解答状態の変更を通知する
'##################################################

' セルの変更が発生した場合、通知する
Public Event Changed(ByVal x As Long, ByVal y As Long, ByVal NewState As CellStateConstants)

Private m_eStates() As CellStateConstants
Private m_lWidth    As Long
Private m_lHeight  As Long

' コンストラクタ代わり
Public Sub Construct(ByVal Loader As IPuzzleLoader)
    Dim x      As Long
    Dim y      As Long
    Dim oAnswer As ArrayList
    Dim oRow    As ArrayList

    m_lWidth = Loader.Width
    m_lHeight = Loader.Height
    Set oAnswer = Loader.Answer
    ReDim m_eStates(0 To m_lWidth - 1, 0 To m_lHeight - 1) As CellStateConstants

    For y = 0 To Height - 1
        Set oRow = oAnswer(y)
        For x = 0 To Width - 1
            m_eStates(x, y) = oRow(x)
        Next
    Next
End Sub

' 指定位置のセルの状態を取得する
Public Property Get Item(ByVal x As Long, ByVal y As Long) As CellStateConstants
    Item = m_eStates(x, y)
End Property

' 指定位置のセルの状態を設定する
Public Property Let Item(ByVal x As Long, ByVal y As Long, ByVal Value As CellStateConstants)
    If m_eStates(x, y) = Value Then Exit Property
    RaiseEvent Changed(x, y, Value)
    m_eStates(x, y) = Value
End Property

' パズルの幅を取得する
Public Property Get Width() As Long
    Width = m_lWidth
End Property

' パズルの高さを取得する
Public Property Get Height() As Long
    Height = m_lHeight
End Property

========== end of PuzzleAnswer ==========

2 次元配列なので、インデクサでは 2 つの引数がいる。
ByVal x As Long, ByVal y As Long だ。
つまり、letter は、3 つの引数となる。

Item プロパティもおなじみだ。前と同様、
エクスポートして既定のプロシージャ属性をつける。

イベントは、Public Event イベント名(引数)で定義する。
これにより、オブジェクトが自発的に
状態の変更を外部のリスナーに通知することができるのだ。

イベントを発生させるには、
RaiseEvent ステートメントを呼び出す。
ここでは、Property Let で読んでいる。
先に値を調べて、「変更があった場合」に通知するのだ。

この実装方法には大きな利点がある。

PuzzleAnswer のリスナーとして登録しておけば、
どのクラスにもイベントが通知される。

つまり、PuzzleSolver インスタンスを作って利用する側で、
「解答のステップをリアルタイムに表示」などの機能を
簡単に実現することができるようになるのだ。



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