GUIコンポーネントとインターフェース

[id:soutaro:20050901:1125614439]

GUIにデータを表示するために、オブジェクトにGUIのためのインタフェースを実装しておくことは、不自然な気がします。いや違うな。不自然とかじゃなくって、経験的にそれはやらないな、と思うわけですね。特に、DelphiとかVBとかC#とかの場合に限ると。

(あれこれ議論しながら考えがまとまってきたところもあるので、前に書いたこととちょっと違うことを言うことになるかもしれませんが、)
[id:lethevert:20050829:p5]に挙げた

interface Node
{
  Node getChild();
  Node next();
  Node getParent();
  String getLabel();
}

というのは、GUIのためのインターフェースというよりも、木構造オブジェクトに対する一般的なアクセス方法を定義したインターフェースではないかと思うのです。だから、木構造データの表示GUIコンポーネントは、木構造アクセスインターフェースを実装したオブジェクトなら、どういうオブジェクトでも表示できます、というほうが設計としてよいのではないか、ということです。木構造アクセスインターフェースを、木構造データにあらかじめ実装しておくことは、それほど不自然ではないように思いますし。

表示したいオブジェクトのクラスに特定のインタフェースを実装しておかなくてはいけないという状況は、おかしいと思うんですよ。AとBとCで表示するかもしれないから、それぞれに対応するIAとIBとICを実装しておこう、なんてばかげてると思うわけです。あるいはIAを実装しないオブジェクトを表示しようと思ったら、そこで行き詰るわけですし。

データオブジェクトがあらかじめ作られていて、かなり時間がたってから、データオブジェクトに手を加えずにGUIコンポーネントで表示する、というような局面では、まあ、そうなのかもしれません。
しかし、GUIの表示コンポーネントの方がライブラリとしてあらかじめ作られていて、個々のケースに合わせてデータオブジェクトを作っていくことになるのが、普通だと思います。そのときに、最初にデータオブジェクトを設計・実装する段階では、GUIからアクセスすることは考えずに、対処したい問題に適した形でデータオブジェクトを作成する(おそらく、DBなどにデータを永続化するような部分が意識の中心にある)はずで、データオブジェクトができてから、そのデータをどうやって表示しようということになると思います。(実際のコーディングの順序は、人によると思いますが、少なくとも設計レベルでは、データが決まってから表示を考えるのは間違いないはず)
そうなったときに、TTreeViewで表示する項目は、TTreeNodeもしくはその派生クラスでないとダメですよ、ということになっていると、解決策としては、

  1. さっきのデータオブジェクトと対応するTTreeNodeオブジェクト(またはそのサブクラスのオブジェクト)を両方作り、2重管理する
  2. さっきのデータオブジェクトを、TTreeNodeオブジェクトのサブクラスとして作り直す

ということを考えなければいけないのです。2重管理の方は、データオブジェクトを変更すると、対応するTTreeNodeオブジェクトやその仲介クラスも変更するという2重メンテが発生し、TTreeNodeオブジェクトのサブクラスとして作り直すという方は、すでにデータオブジェクトが何かのサブクラスであった場合には、多重継承でなければ解決できない問題が発生してしまいます。また、サブクラスにすることで、本来、データとは関係のないメソッドやフィールドがデータオブジェクトに追加されるという気持ちの悪いことも起こります。
さらに、こういうコードは、GUIコンポーネントとデータオブジェクトが癒着した構造になりやすいので、再設計して、表示コンポーネントを変更しようとしたときには、必然的に大手術になってしまい、作り直したほうが速いというようなことにもなります。サブクラス化することで、本来、お互い見えてはいけないようなprivate, protectedなメソッドやフィールドへアクセスできてしまうので、それらへ直接アクセスしたコードになってしまいやすいからです。
結果的に、メンテナンスのしにくいコードが生まれてしまうのです。