第3章〈GoF〉Command パターン 3/4, IronPython

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

IronPython で学ぶ WPF プログラミングの世界《IronPython2.6》

GoF〉Command パターン

《著》森こねこ・後藤いるか・小粒ちゃん《監修》小泉ひよ子とタマゴ倶楽部
第1版♪1995/07/02 ● 第2版♪2003/01/29 ● 第3版♪2008/04/28

■ 概要

WPF の特徴を活かして、デザインパターン Command を導入します。

イベントに呼応するアクションを規定したいときには〈GoF〉Command パターンを導入します。すると、密接に関係する「イベント/アクション」対を、再利用可能な「部品」として扱えます。

《Note》IronPython1.x 用に作成したセミナー課題を、IronPython2.6 で再構成しました。

■ 関連記事

Command パターンに加えて

モジュール routedCommandToolBar.py では、〈GoF〉Command パターンの問題点を解消するために、コマンドバインディングを導入します。前述した 事例 のように Command パターンを導入する代わりに、ICommand に象徴される、既存のフレームワークを利用します。

class PaintColor(RoutedCommand):         # Command::ConcreteCommand
    def __init__(self, panel):
        self.panel = panel
    def Execute(self, sender, e):
        e = sender.Content
        self.panel.Background = getattr(Brushes, e)
    def CanExecute(self, sender, e):
        e.CanExecute = True

RoutedCommand の子孫クラスでは、インターフェース ICommand の規定に従って、メソッド Execute/CanExecute を定義します。

  • メソッド Execute は、前述した事例の execute に相当します。
  • メソッド CanExecute は、イベントが発生したときに、対応するアクションを実行するかどうかを規定します。アクションを実行したいときには、プロパティー .CanExecute に True を設定します。

他のクラス FileOpen/FileClose についても同様なので、ここでは割愛します(付録を参照してください)。

>ipy.exe routedCommandToolBar.py CommandToolBar.xaml


アプリケーションを起動すると、前述 と同様のウィンドウが現れます。

CommandBinding を利用すると

コマンドバインディングを利用するには、CommandBinding を用意して、ソース/ターゲットの対応関係を規定します。

class ExWindow(Window):         # Command::Client
    def init(self):
        ...
        for content, command, toolBar in [
            ("Open...", FileOpen  (panel=self.panel), self.fileToolBar ),
            ("Close"  , FileClose (window=self)     , self.fileToolBar ),
            ("Red"    , PaintColor(panel=self.panel), self.colorToolBar),
            ("Green"  , PaintColor(panel=self.panel), self.colorToolBar),
            ("Blue"   , PaintColor(panel=self.panel), self.colorToolBar),
            ]:
            button = Button(
                Content=content,
                Command=command,
                )
            binding = CommandBinding(
                command,
                command.Execute,
                command.CanExecute)
            button.CommandBindings.Add(binding)
            toolBar.Items.Add(button)

プロパティー Command= に、コマンド command を設定すると、要素ツリーを往来しながら、適切なイベントハンドラーを起動して、対応するアクションを実行します。
CommandBinding は「イベント/コマンド」の対応関係を規定します。コマンドの対象において、イベント PreviewExecuted/Executed および PreviewCanExecute/CanExecute が発生すると、RoutedCommand のメソッド Execute および CanExecute が起動されます。このとき、コマンドの対象で CommandBinding が見つかると、適切なイベントハンドラーが起動されます。そこで見つからないと、CommandBinding を持つ要素が見つかるまで、要素ツリーを往来します。
プロパティー .CommandBindings は、複数のコマンドバインディングを管理します。ここでは、ソース/ターゲットをともに、ボタン Button に設定しています。そのため、ツールバーにあるボタン button(ソース)をクリックするとイベントが発生して、ボタン button(ターゲット)のコマンドバインディングを経由して、対応するアクション(コマンド)command を起動します。

《Note》どのアクションを実行すべきかは(コンパイル時ではなく)要素ツリーの構造に沿って実行時に決定させるのが得策です。さもないと、要求仕様の変更に際して、再コンパイルが必要になります。すべての問題解決を事前に行う必要があると、ウォーターフォール型のシステム開発と同様の病巣を抱える羽目に陥ります。「クラス」指向から「オブジェクト」指向への扉を開く鍵は、こんなところにも落ちています。

Command パターンを概観する:コマンドバインディング

コマンドバインディングを導入すると、部品としての独立性を促進します。


コントロールに象徴される「視覚部品(コンポーネント)」は、コマンドに象徴される「機能部品」と独立して管理できるので、部品としての独立性が向上するとともに、再利用の可能性が広がります。
機能部品として独立したコマンドは、既存のフレームワークが提供するコントロールの制約を受けません。たとえば、ボタンのプロパティー .Click に限らず、任意のイベントに対して共通のプロトコルを提供します。
また、特定のコントロールと独立して「イベント/アクション」の対を規定できるので、要求仕様の変更に伴う要素ツリーの再構築が容易になります。たとえば、最上位のウィンドウにコマンドバインディング設定しておくだけで、その傘下にある各要素を自由に再配置できます。

《Note》すべての問題解決を静的(コンパイル時)に図るのではなく、それを動的(実行時)に委ねられるので、早期の意思決定に捕われない、最適な解を求める試行錯誤を支援します。

Last updated♪09/06/23