第3章〈GoF〉Command パターン 3/4, IronPython
IronPython で学ぶ WPF プログラミングの世界《IronPython2.6》
〈GoF〉Command パターン
■ 概要
WPF の特徴を活かして、デザインパターン
イベントに呼応するアクションを規定したいときには〈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》すべての問題解決を静的(コンパイル時)に図るのではなく、それを動的(実行時)に委ねられるので、早期の意思決定に捕われない、最適な解を求める試行錯誤を支援します。