Strategy (ストラテジー) パターン
Strategy とは『戦略』という意味です。相手の出方によって用いる戦略をクラスで表現するデザインパターンです。
入力された値等に対し、If 文等で分岐して何を返却するかの戦略を立てる方法もありますが、
複雑な分岐の場合、可読性も落ち、当然メンテナンスも大変になってきます。
サンプルでは、クライアントから描画を要求されたコントロールの種類によって、描画(戦略)のクラスを作成する事にしました。
■クラス図
ここで用いるサンプルのクラス図です。
準備中...
■サンプルの説明
1 枚目のタブページを選択すると、DataGrid コントロールに SQLServer のデータベース pubs の authors テーブルの値を表示します。
2 枚目のタブページを選択すると、ListView コントロールに SQLServer のデータベース pubs の authors テーブルの値を表示します。
サンプルのプロジェクトダウンロード
■コード
アプリケーションのエントリポイント 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) 'プロトタイプの破棄 If Not Me.m_manager Is Nothing Then Me.m_manager.Dispose() End If End Sub ' Windows フォーム デザイナで必要です。 Private components As System.ComponentModel.IContainer ' メモ : 以下のプロシージャは、Windows フォーム デザイナで必要です。 'Windows フォーム デザイナを使って変更してください。 ' コード エディタを使って変更しないでください。 Friend WithEvents TabControl1 As System.Windows.Forms.TabControl Friend WithEvents TabPage1 As System.Windows.Forms.TabPage Friend WithEvents TabPage2 As System.Windows.Forms.TabPage Friend WithEvents DataGrid1 As System.Windows.Forms.DataGrid Friend WithEvents ListView1 As System.Windows.Forms.ListView <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() Me.TabControl1 = New System.Windows.Forms.TabControl Me.TabPage1 = New System.Windows.Forms.TabPage Me.DataGrid1 = New System.Windows.Forms.DataGrid Me.TabPage2 = New System.Windows.Forms.TabPage Me.ListView1 = New System.Windows.Forms.ListView Me.TabControl1.SuspendLayout() Me.TabPage1.SuspendLayout() CType(Me.DataGrid1, System.ComponentModel.ISupportInitialize).BeginInit() Me.TabPage2.SuspendLayout() Me.SuspendLayout() ' 'TabControl1 ' Me.TabControl1.Controls.Add(Me.TabPage1) Me.TabControl1.Controls.Add(Me.TabPage2) Me.TabControl1.Location = New System.Drawing.Point(8, 8) Me.TabControl1.Name = "TabControl1" Me.TabControl1.SelectedIndex = 0 Me.TabControl1.Size = New System.Drawing.Size(280, 248) Me.TabControl1.TabIndex = 2 ' 'TabPage1 ' Me.TabPage1.Controls.Add(Me.DataGrid1) Me.TabPage1.Location = New System.Drawing.Point(4, 21) Me.TabPage1.Name = "TabPage1" Me.TabPage1.Size = New System.Drawing.Size(272, 223) Me.TabPage1.TabIndex = 0 Me.TabPage1.Text = "TabPage1" ' 'DataGrid1 ' Me.DataGrid1.DataMember = "" Me.DataGrid1.HeaderForeColor = System.Drawing.SystemColors.ControlText Me.DataGrid1.Location = New System.Drawing.Point(8, 8) Me.DataGrid1.Name = "DataGrid1" Me.DataGrid1.Size = New System.Drawing.Size(256, 208) Me.DataGrid1.TabIndex = 0 ' 'TabPage2 ' Me.TabPage2.Controls.Add(Me.ListView1) Me.TabPage2.Location = New System.Drawing.Point(4, 21) Me.TabPage2.Name = "TabPage2" Me.TabPage2.Size = New System.Drawing.Size(272, 223) Me.TabPage2.TabIndex = 1 Me.TabPage2.Text = "TabPage2" ' 'ListView1 ' Me.ListView1.Location = New System.Drawing.Point(8, 8) Me.ListView1.Name = "ListView1" Me.ListView1.Size = New System.Drawing.Size(256, 208) Me.ListView1.TabIndex = 0 ' 'StartUpForm ' Me.AutoScaleBaseSize = New System.Drawing.Size(5, 12) Me.ClientSize = New System.Drawing.Size(292, 266) Me.Controls.Add(Me.TabControl1) Me.Name = "StartUpForm" Me.Text = "Form1" Me.TabControl1.ResumeLayout(False) Me.TabPage1.ResumeLayout(False) CType(Me.DataGrid1, System.ComponentModel.ISupportInitialize).EndInit() Me.TabPage2.ResumeLayout(False) Me.ResumeLayout(False) End Sub #End Region '画面がロードされた時のイベント Private Sub StartUpForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load Me.Text = "Strategy" Me.TabControl1.SelectedIndex = 1 Me.TabPage1.Text = "DataGrid" Me.TabPage2.Text = "ListView" Me.TabControl1.SelectedIndex = 0 End Sub '選択タブページ変更時のイベント Private Sub TabControl1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) _ Handles TabControl1.SelectedIndexChanged If Me.TabControl1.SelectedTab.Controls.Count < 1 Then Exit Sub Dim supporter As DisplayDataSupporter 'TabPage の ControlCollection には DataGrid か ListView のいずれか ' 1 つしか存在しないのでこの様に記述しています Dim ctrl As Control = Me.TabControl1.SelectedTab.Controls(0) Dim entity As AbstructData = New Authors supporter = DisplayDataSupporter.GetFormatter(ctrl) supporter.DataBind(ctrl, entity) End Sub End Class
何かのコントロールデータをバインドする戦略のインターフェースです。
'コントロールにデータバインドするインターフェース Public Interface IDataBinder 'コントロールにデータをセットするメソッド定義 Sub DataBind(ByVal ctrl As Control, ByVal dt As DataTable) End Interface
DataGrid コントロールにデータをバインドするクラスです。
IDataBinder を実装しています。
'DataGridコントロール用 Public Class DataGridBinder Implements IDataBinder 'コントロールにデータをセット Public Sub DataBind(ByVal ctrl As System.Windows.Forms.Control, _ ByVal dt As System.Data.DataTable) Implements IDataBinder.DataBind Dim myDataGrid As DataGrid = DirectCast(ctrl, DataGrid) myDataGrid.DataSource = dt End Sub End Class
ListView コントロールにデータをバインドするクラスです。
IDataBinder を実装しています。
'ListViewコントロール用 Public Class ListViewBinder Implements IDataBinder 'コントロールにデータをセット Public Sub DataBind(ByVal ctrl As System.Windows.Forms.Control, _ ByVal dt As System.Data.DataTable) Implements IDataBinder.DataBind Dim myListView As ListView = DirectCast(ctrl, ListView) myListView.View = View.Details myListView.Items.Clear() myListView.Columns.Clear() '列の追加 For Each col As DataColumn In dt.Columns myListView.Columns.Add(col.ColumnName, 100, HorizontalAlignment.Left) Next '行の追加 For Each row As DataRow In dt.Rows Dim rowStr(dt.Columns.Count - 1) As String For i As Integer = 0 To dt.Columns.Count - 1 rowStr(i) = DirectCast(row(i), String) Next myListView.Items.Add(New ListViewItem(rowStr)) Next End Sub End Class
DataGrid または ListView コントロールバインドする、何かのデータです。
'抽象 データ Public MustInherit Class AbstructData 'DataTable取得メソッド定義 Public MustOverride Function GetDataTable() As DataTable 'SqlServerよりデータの取得 Protected Function SelectData(ByVal SqlScript As String) As DataTable Const CONNECTION_STRING As String = "Data Source=(local);Initial Catalog=pubs;Integrated Security=SSPI;" Dim myDa As CommonDataAccess.AbstructDataAccess = New CommonDataAccess.SqlServerDataAccess myDa.Open(CONNECTION_STRING) Try Return myDa.DataSelect(SqlScript) Finally If Not myDa Is Nothing Then myDa.Close() End Try End Function End Class
SQLServer のデータベース pubs の authors テーブルの値を保持するクラスです。
AbstructData を継承しています。
'具象 データ(Authors) Public Class Authors Inherits AbstructData 'DataTable取得メソッド実装 Public Overrides Function GetDataTable() As System.Data.DataTable Return MyBase.SelectData("SELECT * FROM authors") End Function End Class
IDataBinder を管理し、StartUpForm へインターフェースを提供するクラスです。
作戦を練るのはこのクラスが行います。
'IDataBinderを管理するクラス Public Class DisplayDataSupporter 'データバインド インターフェース Private m_binder As IDataBinder Private Shared m_dataGridSupporter As DisplayDataSupporter = New DisplayDataSupporter(New DataGridBinder) Private Shared m_listViewSupporter As DisplayDataSupporter = New DisplayDataSupporter(New ListViewBinder) 'コンストラクタ Private Sub New(ByVal binder As IDataBinder) Me.m_binder = binder End Sub 'データバインド オブジェクト を取得 Public Shared Function GetFormatter(ByVal ctrl As Control) As DisplayDataSupporter If TypeOf ctrl Is DataGrid Then Return m_dataGridSupporter ElseIf TypeOf ctrl Is ListView Then Return m_listViewSupporter End If End Function 'データバインドする Public Sub DataBind(ByVal ctrl As Control, ByVal entity As AbstructData) Dim dt As DataTable = entity.GetDataTable() Me.m_binder.DataBind(ctrl, dt) ctrl.Refresh() End Sub End Class
■ひとこと
StartUpForm の中で、TabContorl1 の SelectedIndex 等を見て、If 文や Case 文等で
分岐し、それぞれのコントロールを描画する事も可能ですが、こうしてクラスに分けておく事により、
他の画面にも、簡単に流用できるし、描画したいコントロールが増えた場合にもメインロジックは弄らないですみます。
Strategy パターンは、条件文が複雑になる場合や、If 文等で判断すると、実装が複雑になってしまう場合に向いています。
戦略をクラスとして表現する事のメリットとして、もう一つ、
Unit テストの容易性を高める効果もあります。