Command (コマンド) パターン
あるオブジェクトに対しての要求をメソッドではなく、クラスで表現しておくデザインパターンです。
あれ?ちょっと下のサンプルってどこかで見た気が...。
そうです。Decolator パターンで実装していたものをちょっとだけ変えて実装しています。
# ネタ切れw
サンプルでは、『こんな外観のこのコントロールを作れ』という要求オブジェクトを
命令のクラスに渡して、その要求をかなえています。
サンプルの中で使用している、ColorDialog や FontDialog についての詳細は、じゃんぬさんのページを参照して下さい。
■クラス図
ここで用いるサンプルのクラス図です。
準備中...
■サンプルの説明

BackColor・Font・ForeColor を任意で選択し、コンボボックスのコントロール名を選択します。
作成ボタンをクリックすると、コンボボックスで選択したコントロールの BackColor・Font・ForeColor が
選択した色やフォントであるコントロールが画面に生成されます。
サンプルのプロジェクトダウンロード
■コード
アプリケーションのエントリポイント StartUpForm です。
Public Class StartUpForm Inherits System.Windows.Forms.Form #Region " Windows フォーム デザイナで生成されたコード " Public Sub New() MyBase.New() ' この呼び出しは Windows フォーム デザイナで必要です。 InitializeComponent() ' InitializeComponent() 呼び出しの後に初期化を追加します。 AddHandler Application.ThreadException, AddressOf CommonExceptionAndHandler.ExceptionHandler.MyHandler End Sub ' Form は、コンポーネント一覧に後処理を実行するために dispose をオーバーライドします。 Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If Not (components Is Nothing) Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub ' Windows フォーム デザイナで必要です。 Private components As System.ComponentModel.IContainer ' メモ : 以下のプロシージャは、Windows フォーム デザイナで必要です。 'Windows フォーム デザイナを使って変更してください。 ' コード エディタを使って変更しないでください。 Friend WithEvents Button3 As System.Windows.Forms.Button Friend WithEvents Label3 As System.Windows.Forms.Label Friend WithEvents Button4 As System.Windows.Forms.Button Friend WithEvents ComboBox1 As System.Windows.Forms.ComboBox Friend WithEvents Button2 As System.Windows.Forms.Button Friend WithEvents Label2 As System.Windows.Forms.Label Friend WithEvents Button1 As System.Windows.Forms.Button Friend WithEvents Label1 As System.Windows.Forms.Label Friend WithEvents GroupBox1 As System.Windows.Forms.GroupBox <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() Me.Button3 = New System.Windows.Forms.Button Me.Label3 = New System.Windows.Forms.Label Me.Button4 = New System.Windows.Forms.Button Me.ComboBox1 = New System.Windows.Forms.ComboBox Me.Button2 = New System.Windows.Forms.Button Me.Label2 = New System.Windows.Forms.Label Me.Button1 = New System.Windows.Forms.Button Me.Label1 = New System.Windows.Forms.Label Me.GroupBox1 = New System.Windows.Forms.GroupBox Me.SuspendLayout() ' 'Button3 ' Me.Button3.Location = New System.Drawing.Point(160, 56) Me.Button3.Name = "Button3" Me.Button3.TabIndex = 20 Me.Button3.Text = "Button3" ' 'Label3 ' Me.Label3.Location = New System.Drawing.Point(56, 56) Me.Label3.Name = "Label3" Me.Label3.TabIndex = 19 Me.Label3.Text = "Label3" ' 'Button4 ' Me.Button4.Location = New System.Drawing.Point(136, 96) Me.Button4.Name = "Button4" Me.Button4.TabIndex = 18 Me.Button4.Text = "Button4" ' 'ComboBox1 ' Me.ComboBox1.Location = New System.Drawing.Point(8, 96) Me.ComboBox1.Name = "ComboBox1" Me.ComboBox1.Size = New System.Drawing.Size(121, 20) Me.ComboBox1.TabIndex = 17 Me.ComboBox1.Text = "ComboBox1" ' 'Button2 ' Me.Button2.Location = New System.Drawing.Point(160, 32) Me.Button2.Name = "Button2" Me.Button2.TabIndex = 16 Me.Button2.Text = "Button2" ' 'Label2 ' Me.Label2.Location = New System.Drawing.Point(56, 32) Me.Label2.Name = "Label2" Me.Label2.TabIndex = 15 Me.Label2.Text = "Label2" ' 'Button1 ' Me.Button1.Location = New System.Drawing.Point(160, 8) Me.Button1.Name = "Button1" Me.Button1.TabIndex = 14 Me.Button1.Text = "Button1" ' 'Label1 ' Me.Label1.Location = New System.Drawing.Point(56, 8) Me.Label1.Name = "Label1" Me.Label1.TabIndex = 13 Me.Label1.Text = "Label1" ' 'GroupBox1 ' Me.GroupBox1.Location = New System.Drawing.Point(8, 128) Me.GroupBox1.Name = "GroupBox1" Me.GroupBox1.Size = New System.Drawing.Size(280, 128) Me.GroupBox1.TabIndex = 21 Me.GroupBox1.TabStop = False Me.GroupBox1.Text = "GroupBox1" ' 'StartUpForm ' Me.AutoScaleBaseSize = New System.Drawing.Size(5, 12) Me.ClientSize = New System.Drawing.Size(292, 266) Me.Controls.Add(Me.GroupBox1) Me.Controls.Add(Me.Button3) Me.Controls.Add(Me.Label3) Me.Controls.Add(Me.Button4) Me.Controls.Add(Me.ComboBox1) Me.Controls.Add(Me.Button2) Me.Controls.Add(Me.Label2) Me.Controls.Add(Me.Button1) Me.Controls.Add(Me.Label1) Me.Name = "StartUpForm" Me.Text = "Form1" Me.ResumeLayout(False) End Sub #End Region '生成するコントロール Private Const CTRL_BUTTON As String = "ボタン" Private Const CTRL_TEXT As String = "テキストボックス" Private Const CTRL_LABEL As String = "ラベル" '画面がロードされた時のイベント Private Sub StartUpForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load Me.Text = "Command" Me.Label1.Text = "BackColor" Me.Label1.BorderStyle = BorderStyle.FixedSingle Me.Button1.Text = "参照" Me.Label2.Text = "Font" Me.Label2.BorderStyle = BorderStyle.FixedSingle Me.Button2.Text = "参照" Me.Label3.Text = "ForeColor" Me.Label3.BorderStyle = BorderStyle.FixedSingle Me.Button3.Text = "参照" Me.ComboBox1.Items.Clear() Me.ComboBox1.DropDownStyle = ComboBoxStyle.DropDownList Me.ComboBox1.Items.Add(Me.CTRL_BUTTON) Me.ComboBox1.Items.Add(Me.CTRL_TEXT) Me.ComboBox1.Items.Add(Me.CTRL_LABEL) Me.ComboBox1.SelectedIndex = 0 Me.Button4.Text = "生成" Me.GroupBox1.Text = "" End Sub '色選択ダイアログの表示 Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles Button1.Click, Button3.Click Dim targetlabel As Label If sender Is Me.Button1 Then targetlabel = Me.Label1 Else targetlabel = Me.Label3 End If Dim colorDlg As ColorDialog = New ColorDialog Try colorDlg.Color = targetlabel.BackColor colorDlg.FullOpen = True colorDlg.AnyColor = True colorDlg.SolidColorOnly = False colorDlg.ShowHelp = True If colorDlg.ShowDialog(Me) = DialogResult.OK Then targetlabel.BackColor = colorDlg.Color End If Finally If Not colorDlg Is Nothing Then colorDlg.Dispose() End Try End Sub 'フォント選択ダイアログの表示 Private Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Click Dim fontDlg As FontDialog = New FontDialog Try fontDlg.FontMustExist = True fontDlg.ShowHelp = True ' ダイアログを表示し、戻り値が [OK] の場合は選択したフォントを Label2 に適用する If fontDlg.ShowDialog() = DialogResult.OK Then Me.Label2.Font = fontDlg.Font End If Finally If Not fontDlg Is Nothing Then fontDlg.Dispose() End Try End Sub '生成ボタンをクリックした時のイベント Private Sub Button4_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button4.Click Me.GroupBox1.Controls.Clear() Dim createdtControl As Control Dim cmd As AbstractCommand Dim dmd As Demand = New Demand(Me.Label1.BackColor, _ Me.Label2.Font, _ Me.Label3.BackColor) If Me.ComboBox1.Text = Me.CTRL_BUTTON Then 'Button cmd = New ButtonCreateCmmand ElseIf Me.ComboBox1.Text = Me.CTRL_TEXT Then 'TextBox cmd = New TextBoxCreateCommand Else 'Label cmd = New LabelCreateCommand End If '要求をセット cmd.SetDemand(dmd) 'コントロール作成 createdtControl = cmd.Create() createdtControl.Text = cmd.GetType().Name '現在のインスタンスの名前を表示 createdtControl.Location = New Point(10, 10) createdtControl.Size = New Size(200, 20) 'コントロールをGroupBoxにAdd Me.GroupBox1.Controls.Add(createdtControl) End Sub End Class
こんな外観で作って欲しい、という要求オブジェクトです。
'見た目の要求 Public Class Demand '背景色 Private m_backColor As Color 'フォント Private m_font As Font '前面色 Private m_foreColor As Color 'コンストラクタ Public Sub New(ByVal bkColor As Color, _ ByVal fnt As Font, _ ByVal frColor As Color) Me.m_backColor = bkColor Me.m_font = fnt Me.m_foreColor = frColor End Sub '背景色アクセサ Public ReadOnly Property BackColor() As Color Get Return Me.m_backColor End Get End Property 'フォントアクセサ Public ReadOnly Property GetFont() As Font Get Return Me.m_font End Get End Property '前面色アクセサ Public ReadOnly Property ForeColor() As Color Get Return Me.m_foreColor End Get End Property End Class
要求された外観のコントロールを作成する命令の抽象親クラスです。
'要求された見た目のコントロールを作成する命令 Public MustInherit Class AbstractCommand '要求 Protected m_Demand As Demand '要求を設定する Public Sub SetDemand(ByVal dmd As Demand) Me.m_Demand = dmd End Sub 'コントロールを作成するメソッド定義 Public MustOverride Function Create() As Control End Class
要求具体的な命令であり、指示された外観の Button コントロールを作成するクラスです。
AbstractCommand を継承しています。
Public Class ButtonCreateCmmand Inherits AbstractCommand 'コントロールを作成するメソッド実装 Public Overrides Function Create() As System.Windows.Forms.Control Dim bt As Control = New Button bt.BackColor = MyBase.m_Demand.BackColor bt.Font = MyBase.m_Demand.GetFont bt.ForeColor = MyBase.m_Demand.ForeColor Return bt End Function End Class
要求具体的な命令であり、指示された外観の Label コントロールを作成するクラスです。
AbstractCommand を継承しています。
Public Class LabelCreateCommand Inherits AbstractCommand 'コントロールを作成するメソッド実装 Public Overrides Function Create() As System.Windows.Forms.Control Dim lb As Control = New Label lb.BackColor = MyBase.m_Demand.BackColor lb.Font = MyBase.m_Demand.GetFont lb.ForeColor = MyBase.m_Demand.ForeColor Return lb End Function End Class
要求具体的な命令であり、指示された外観の TextBox コントロールを作成するクラスです。
AbstractCommand を継承しています。
Public Class TextBoxCreateCommand Inherits AbstractCommand 'コントロールを作成するメソッド実装 Public Overrides Function Create() As System.Windows.Forms.Control Dim tx As Control = New TextBox tx.BackColor = MyBase.m_Demand.BackColor tx.Font = MyBase.m_Demand.GetFont tx.ForeColor = MyBase.m_Demand.ForeColor Return tx End Function End Class
■ひとこと
Decolator と似たような事をやっているのに、
中身の実装が殆ど異なる事を感じていただけたでしょうか。
同じ様な事をやりたい場合でも、色々と実装方法があるのを知るのはおもしろいですね。
Command パターンを適用した場合、命令が増えても柔軟に対応する事ができます。
サンプルの場合、RichTextBox に適用したい時は、RitchTextBox 用のコマンドクラスを作成してやればよいのです。
ただし、Command パターンが最も威力を発揮するのは、要求を処理するに当たって、
他のクラスと依存関係が殆ど無い場合であり、他クラスとの依存関係が強い場合は依存するクラスの全てに
変更の影響が波紋する恐れがあります。
その為、このデザインパターンを適用するには、要求の内容をよく吟味してから行った方がよさそうです。