例題で学ぶデザインパターン #1.4: デザインパターン〈GoF〉Observer, #1

前の記事記事一覧次の記事

例題で学ぶ Jython/Swing デザインパターン《Jython2.5》
デザインパターンGoF〉Observer, #1

《著》越智ことり+小粒ちゃん《監修》小泉ひよ子とタマゴ倶楽部
第1版♪2003/05/23 ● 第2版♪2009/04/03

■ 概要

アプリケーションを作成する過程を通して、Jython/Swing によるデザインパターンを習得します。

この課題では、Swing/GUI を使ってモデルの変化に呼応する複数のビューを提示します。〈GoF〉Observer パターンを導入すると、依存性を扱うのが容易になるので、要求仕様の変更にも柔軟に対処でき、簡潔で見通しの良いコードを記述できるようになります。

《Note》JPython1.1.x/Jython2.1.x 用に作成したセミナー課題を、Jython2.5 で再構成しました。

事例:モジュールを起動する

モジュールを起動すると、次のようなウィンドウが現れます。

$ jython2.5.0 -i step04/ListEx.py 


(左)リスト JList を構成する各項目を選択すると、(右)キャンバス Canvas の背景色が変化します。たとえば、ノード antiquewhite を選択すると、キャンバスの背景がその色に変化します。

事例:モジュールを分割する

リファクタリングの第一歩は、モジュールを分割することです。リファクタリングの前後で、機能は変化しませんが、その後の作業を楽にします。


ここでは、既存のモジュール ListEx.py を、複数のモジュール ListModel.py/ListView.py に分割します。

事例:コードの解説

■ モジュール ListEx.py
class View(JPanel):                         # -------------------- before
    def __init__(self, *args, **keys):
        self.model = Model()
        list = ListView(
            self.model.listData,
            valueChanged = self.valueChanged,
            )
        self.canvas = CanvasView()
class View(JPanel):                          # -------------------- after
    def __init__(self, *args, **keys):
        list = ListView(Model())
        canvas = CanvasView(list)

コンストラクター ListView の引数には、リスト項目を管理するモデル Model を指定します。コンストラクター CanvasView の引数には、依存する対象としてリスト list/ListView を指定します。すると、ListView の選択された項目が変化したのに呼応して、CanvasView の背景色が変化します。

《Note》モジュールの分解容易性〔modular decomposability〕:モジュールは、相互に独立した小さな問題に分解するのを支援します。

リファクタリング後の View は、ListView/CanvasView を仲介するだけで、その後の処理には関わりません。前項の図 と比較すると、View から ListView/CanvasView への矢線が消えているのが分かります。これによって、モジュールの独立性が維持されます。

《Note》モジュールの結合容易性〔modular composability〕:モジュールは、必要なら自由に組み合わせて利用できます。ここでは、相互に独立クラス ListView/CanvasView が、必要なら依存関係を結ぶことができ、その場を提供するのが、仲介役 View の役割です。

■ モジュール ListView.py
class ListView(JList):
    def __init__(self, model, **keys):
        self._model = model
        self.observers = []
        super(self.__class__,self).__init__(
            self._model.listData,
            valueChanged = self._notify,
            )

    def _notify(self, e):
        self._value = e.source.selectedValue
        for e in self.observers:
            e._update(self)

    def attach(self, observer):
        self.observers.append(observer)
        
    def value(self):
        return self._model.color(self._value)

class CanvasView(Canvas):
    def __init__(self, subject):
        subject.attach(self)

    def _update(self, subject):
        self.background = subject.value()

Tips

》作業中です《

Last updated♪09/06/24