例題で学ぶデザインパターン #1.4: デザインパターン〈GoF〉Observer, #1
例題で学ぶ Jython/Swing デザインパターン《Jython2.5》
デザインパターン〈GoF〉Observer, #1
■ 概要
アプリケーションを作成する過程を通して、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
》作業中です《