Smalltalk の MVC フレームワーク

Squeak の今の標準 GUI フレームワークは Morphic ですが、古き良き Smalltalk-80 由来の MVC フレームワークも普通に使うことが出来ます。ありがたや〜。

Model

せっかくなので、日経Linux 2006.12月号の 「プログラミングのオキテ」の ストップウォッチをそれなりに真似て作ってみます。まずは Model から。

Object subclass: #WatchModel
    instanceVariableNames: 'running time last'
    classVariableNames: ''
    poolDictionaries: ''
    category: 'NKTimeWatch'
initialize
    | now |
    running _ false.
    time _ 0.
    last _ 0.0.

    [[(Delay forMilliseconds: 100) wait.
    self isRunning ifTrue:
        [now _ (Time millisecondClockValue) // 100.
        self time: self time + now - self last.
        self last: now.
        self changed: #time]] repeat] fork.

startStop
    self last: Time millisecondClockValue // 100.
    running _ self isRunning not. 

WatchModel については Ruby 版ほぼそのまんまです。(手抜きして 時間が秒単位の浮動小数ではなく 100msec単位の整数になっていますが (^^; )。#time / #time:、#last / #last: や、#isRunning は適当に定義してください。

View

次ぎに View です。MVC は、デザインパターンというだけでなく Smalltalk では GUIフレームワークですので、ST80-Framework カテゴリに View というそのままズバリの抽象クラスが実装されています。

今回は、ウインドウとしての機能も欲しいところなので、View の派生クラスである StanderdSystemView を派生して WatchView を作ります。

StandardSystemView subclass: #WatchView
    instanceVariableNames: ''
    classVariableNames: ''
    poolDictionaries: ''
    category: 'NKTimeWatch'

まず、画面表示を行う displayView メソッドを定義します。これは Model の状態変化と関係ない、画面再描画時(ウインドウリサイズとか)に呼ばれる「そう言う名前の」メソッドです。

displayView
    | displayTime |
    super displayView.
    self clearInside.

    displayTime _ (Text string: self model time printString 
                    attribute: TextEmphasis bold) asDisplayText.
    displayTime foregroundColor: Color blue
                backgroundColor: Color white.
    displayTime displayAt: self insetDisplayBox center.

super displayView と self clearInside で、デフォルトの再表示処理とView の中身の描画クリアを行います。その後、WatchModel の時間値を元に 表示文字列を作って それを #displayAt: でWatchView の中に描画します。

次ぎに Model からの変更通知時処理として、 update: メソッドを上書きします。

update: anAspect 
    self displayView.

最後に、Controller が明示的に指定されなかったときのためのデフォルトコントローラとして、(これからつくる)WatchKeyController を返すメソッドを定義します。

defaultControllerClass
    ^ WatchKeyController

これで View のコーディングは終了です。Ruby 版や普通に手前でMVC するときによくやる、Initialize時の Subject への 自身の appendObserver() は、継承元のViewクラスでやってくれているので、今回は特に自分でそのようなコードを書く必要はありません。

この WatchView については梅沢さんの Happy Squeaking!! のこのページのまんまですので、詳しい説明はそちらへ。

Controller

Controller については、(GUIですが)プログラミングのオキテにならって
キーボードからの入力を処理する WatchKeyController を定義します。

StandardSystemController subclass: #WatchKeyController
    instanceVariableNames: ''
    classVariableNames: ''
    poolDictionaries: ''
    category: 'NKTimeWatch'

例によって、MVC フレームワークにおんぶにだっこですので、
以下のメソッドを定義するだけで OK です。

controlActivity
        | key |
        super controlActivity.
        key _ self sensor keyboard.
        key == $s ifTrue: [self model startStop].

オリジナルでは 「q」以外のキーは 全て Start/Stop する仕様でしたが、コンソールアプリケーションじゃないのと あたしの趣味とで、「s」キーを押したときに Start/Stop するように変更しました。

WatchController も Happy Squeaking!!のまんまです。

動かしてみる

あとは Workspece で以下のコードを do it すれば ストップウォッチが動き出します。

| watchView |
watchView _ WatchView new.
watchView model: WatchModel new.

watchView label: watchView name.
watchView maximumSize: 200 @ 100.
watchView minimumSize: 50 @ 30.
watchView borderWidth: 3.
watchView backgroundColor: Color orange.

watchView controller open.


こんな感じで、元祖 MVC はどうでしたでしょうか。知識として語られることは多くても生で触れられることはあんまりない MVC ですが、Squeak のお陰で 誰でも気軽に MVC と戯れることが出来ます。そういう猫も、今回が 初 MVC だった訳ですが、うーん、これが あたしの生まれる前からあったのかぁ、と唸らずには居られないです。

MVC については、梅沢さんの Happy Squeaking !! の第5回 が 非常にわかりやすく大変参考になりました。MVCアーキテクチャ に興味のあるかたは Squeak 片手に 是非一度遊んでみてください。