■ srMVUnit.pas -- Delphi用モデル/ビュークラス

■説明

Java の Observable と Observer を Delphiに移植した TObservable と TObserver クラスです。

■ダウンロード

mv100.zip (サンプルプログラムが含まれています)

■使い方と詳細

説明は Sun の JavaTM 2 Platform, Standard Edition, 1.4.0 API 仕様 から拝借しています。

TObservable クラスのメソッド一覧です。Java のオリジナル:Observable

メソッド スコープ 説明
AddObserver(AObserver: TObserver) public オブジェクトのオブザーバセットにオブザーバを追加します。
DeleteObserver(AObserver: TObserver) public オブジェクトのオブザーバセットからオブザーバを削除します。
NotifyObservers public オブジェクトが、HasChanged メソッドに示されるように変更されていた場合、そのすべてのオブザーバにそのことを通知し、次に ClearChanged メソッドを呼び出して、このオブジェクトがもはや変更された状態でないことを示します。
NotifyObservers(AArg: TObject) public オブジェクトが、HasChanged メソッドに示されるように変更されていた場合、そのすべてのオブザーバにそのことを通知し、次に ClearChanged メソッドを呼び出して、このオブジェクトがもはや変更された状態でないことを示します。
DeleteObservers public オブザーバリストを消去します。
SetChanged protected Observable オブジェクトを変更されたものとしてマーキングします。
ClearChanged protected オブジェクトがもはや変更された状態ではないこと、すなわち、最新の変更がすべてオブザーバに通知されたことを示します。
HasChanged public オブジェクトが変更されたかどうかを判定します。
CountObservers public Observable オブジェクトのオブザーバの数を返します。

TObserver のメソッド一覧です。Java のオリジナル:Observer

メソッド スコープ 説明
Update(AObservable:TObservable; AArg:TObject) public 被監視オブジェクトに変更があると、このメソッドが呼び出されます。

Java の Observable は interface として定義されています。

Delphi にも interface はあるわけですが、これは COM 向けの言語仕様であり、最低でも IUnknown を継承しないといけません。従って、Delphi で TObserver を interface として定義してしまうと、これを実装するクラスでは必要も無いのに IUnknown で宣言されているメソッド(QueryInterface,_AddRef,_Releaseの3つ)を実装しなければならなくなります。以前はこの方法(空のメソッドを定義する)で interface を使っていたこともあったのですが、あまりに見た目が悪いので止めました。

そのための副作用もあります。TObserver はビューですから、通常は TForm 等のビジュアルコンポーネントが対象になります。しかしDelphi は多重継承を許していませんので、このままでは TObserver を組み込むことが出来ません。そこで、フォームがビュークラスを「所有」し、ビュークラスがフォームに対して更新通知を伝える事で解決します。

まず、Form に更新用のメソッドを定義します。

  TMyForm = class(TForm)
    private
      FMyFormListener: TObserverListener;
      procedure UpdateView(AObservable: TObservable);
      …
  end;

TMyForm と同じ unit に、TObservable からの更新を TMyForm に伝えるクラスを定義します。

  TObserverListener = class(TObserver)
    public
     procedure Update(AObservable:TObservable; AArg:TObject);override;
  end;

TObserverListenerの実装は単純です。

  procedure TObserverListener.Update(AObservable: TObservable);
  begin
    MyForm.UpdateView(AObservable as TModel);
  end;

私はこういう形で使用しています。

また、AddObserver するタイミングも問題です。単純なアプリケーションの場合、私はプロジェクトソース内で AddObserver しています。もっと複雑な場合、例えば Observer と Observable の生存期間が一致しないようなアプリケーションの場合には、TMyForm が自分自身で AddObserver するか、Observer と Observable の接続関係を管理するためのクラスを別途作ります。

■今後の予定

Listenerクラスがする事はフォームの更新メソッドを呼ぶだけなわけで、そういう意味ではListenerクラスのインスタンスはひとつだけにして、そのクラスにフォームそのものか更新メソッドのポインタを登録するような形でも良いかもしれません。

ビューは自分がモデルから関連付けを削除されたタイミングを知る事が出来ません。関連付けされていない時に空画面を表示したいような場合にはこのタイミングを知りたくなります。これは TObserver のイベントとして実装する事が出来ます。

とにかく豪華にしようと思えば色々と出来ますが、こういうプリミティブなクラスは単純な方が使い易いので、するとすれば別ものとして作ることになると思います。