Strategy (ストラテジー) パターン

Strategy とは『戦略』という意味です。相手の出方によって用いる戦略をクラスで表現するデザインパターンです。
入力された値等に対し、If 文等で分岐して何を返却するかの戦略を立てる方法もありますが、
複雑な分岐の場合、可読性も落ち、当然メンテナンスも大変になってきます。


サンプルでは、クライアントから描画を要求されたコントロールの種類によって、描画(戦略)のクラスを作成する事にしました。

このカテゴリーの先頭へ このページの先頭へ

■クラス図

ここで用いるサンプルのクラス図です。
準備中...

このカテゴリーの先頭へ このページの先頭へ

■サンプルの説明

サンプルの見た目1  サンプルの見た目2
1 枚目のタブページを選択すると、DataGrid コントロールに SQLServer のデータベース pubs の authors テーブルの値を表示します。
2 枚目のタブページを選択すると、ListView コントロールに SQLServer のデータベース pubs の authors テーブルの値を表示します。
サンプルのプロジェクトダウンロード

このカテゴリーの先頭へ このページの先頭へ

■コード

アプリケーションのエントリポイント StartUpForm です。

StartUpForm.vb
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
このカテゴリーの先頭へ このページの先頭へ

何かのコントロールデータをバインドする戦略のインターフェースです。

IDataBinder.vb
'コントロールにデータバインドするインターフェース
Public Interface IDataBinder

  'コントロールにデータをセットするメソッド定義
  Sub DataBind(ByVal ctrl As Control, ByVal dt As DataTable)

End Interface
このカテゴリーの先頭へ このページの先頭へ

DataGrid コントロールにデータをバインドするクラスです。
IDataBinder を実装しています。

StartUpForm.vb
'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 を実装しています。

ListViewBinder.vb
'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 コントロールバインドする、何かのデータです。

AbstructData.vb
'抽象 データ
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 を継承しています。

AbstructData.vb
'具象 データ(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 へインターフェースを提供するクラスです。
作戦を練るのはこのクラスが行います。

DisplayDataSupporter.vb
'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 テストの容易性を高める効果もあります。

このカテゴリーの先頭へ このページの先頭へ