ProtoType (プロトタイプ) パターン

あらかじめ、材料となる原型をコピーしてインスタンスを生成するデザインパターンです。
似たようなクラスを沢山作る場合がある時や、複雑な実装が内部に組み込まれていたりするクラスであり、
それを沢山作りたい場合に向いています。


サンプルでは、ある Form をプロトタイプとし、それを材料としてカスタマイズし、表示することにしました。
業務で応用するとしたら、マスタが沢山あって、各マスタ毎に簡単なメンテナンス画面を作成したい、
という場合でしょうか。

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

■クラス図

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

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

■サンプルの説明

サンプルの見た目1  サンプルの見た目2
StartUpForm にて、設定した条件のフォームを、上記設定のフォームを表示するボタンをクリックすると表示します。
右の図は、フォームのタイトル「 FavoriteGame 」にし、背景色を青にして、データソースを「プログラムより取得したData」にした場合です。
サンプルのプロジェクトダウンロード

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

■コード

アプリケーションのエントリポイント StartUpForm です。背景色については
ColorDialog コントロールを使用しています。これについての詳細はじゃんぬさんのページを参照下さい。

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 Label1 As System.Windows.Forms.Label
  Friend WithEvents TextBox1 As System.Windows.Forms.TextBox
  Friend WithEvents Label2 As System.Windows.Forms.Label
  Friend WithEvents NumericUpDown1 As System.Windows.Forms.NumericUpDown
  Friend WithEvents NumericUpDown2 As System.Windows.Forms.NumericUpDown
  Friend WithEvents Label3 As System.Windows.Forms.Label
  Friend WithEvents Label4 As System.Windows.Forms.Label
  Friend WithEvents Label5 As System.Windows.Forms.Label
  Friend WithEvents Button1 As System.Windows.Forms.Button
  Friend WithEvents Label6 As System.Windows.Forms.Label
  Friend WithEvents ComboBox1 As System.Windows.Forms.ComboBox
  Friend WithEvents Button2 As System.Windows.Forms.Button
  <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
    Me.Label1 = New System.Windows.Forms.Label
    Me.TextBox1 = New System.Windows.Forms.TextBox
    Me.Label2 = New System.Windows.Forms.Label
    Me.NumericUpDown1 = New System.Windows.Forms.NumericUpDown
    Me.NumericUpDown2 = New System.Windows.Forms.NumericUpDown
    Me.Label3 = New System.Windows.Forms.Label
    Me.Label4 = New System.Windows.Forms.Label
    Me.Label5 = New System.Windows.Forms.Label
    Me.Button1 = New System.Windows.Forms.Button
    Me.Label6 = New System.Windows.Forms.Label
    Me.ComboBox1 = New System.Windows.Forms.ComboBox
    Me.Button2 = New System.Windows.Forms.Button
    CType(Me.NumericUpDown1, System.ComponentModel.ISupportInitialize).BeginInit()
    CType(Me.NumericUpDown2, System.ComponentModel.ISupportInitialize).BeginInit()
    Me.SuspendLayout()
    '
    'Label1
    '
    Me.Label1.Location = New System.Drawing.Point(8, 16)
    Me.Label1.Name = "Label1"
    Me.Label1.TabIndex = 0
    Me.Label1.Text = "Label1"
    '
    'TextBox1
    '
    Me.TextBox1.Location = New System.Drawing.Point(120, 16)
    Me.TextBox1.Name = "TextBox1"
    Me.TextBox1.TabIndex = 1
    Me.TextBox1.Text = "TextBox1"
    '
    'Label2
    '
    Me.Label2.Location = New System.Drawing.Point(8, 48)
    Me.Label2.Name = "Label2"
    Me.Label2.TabIndex = 2
    Me.Label2.Text = "Label2"
    '
    'NumericUpDown1
    '
    Me.NumericUpDown1.Location = New System.Drawing.Point(120, 48)
    Me.NumericUpDown1.Name = "NumericUpDown1"
    Me.NumericUpDown1.TabIndex = 3
    '
    'NumericUpDown2
    '
    Me.NumericUpDown2.Location = New System.Drawing.Point(120, 72)
    Me.NumericUpDown2.Name = "NumericUpDown2"
    Me.NumericUpDown2.TabIndex = 5
    '
    'Label3
    '
    Me.Label3.Location = New System.Drawing.Point(8, 72)
    Me.Label3.Name = "Label3"
    Me.Label3.TabIndex = 4
    Me.Label3.Text = "Label3"
    '
    'Label4
    '
    Me.Label4.Location = New System.Drawing.Point(8, 104)
    Me.Label4.Name = "Label4"
    Me.Label4.TabIndex = 6
    Me.Label4.Text = "Label4"
    '
    'Label5
    '
    Me.Label5.Location = New System.Drawing.Point(120, 104)
    Me.Label5.Name = "Label5"
    Me.Label5.Size = New System.Drawing.Size(32, 23)
    Me.Label5.TabIndex = 7
    Me.Label5.Text = "Label5"
    '
    'Button1
    '
    Me.Button1.Location = New System.Drawing.Point(152, 104)
    Me.Button1.Name = "Button1"
    Me.Button1.TabIndex = 8
    Me.Button1.Text = "Button1"
    '
    'Label6
    '
    Me.Label6.Location = New System.Drawing.Point(8, 136)
    Me.Label6.Name = "Label6"
    Me.Label6.TabIndex = 9
    Me.Label6.Text = "Label6"
    '
    'ComboBox1
    '
    Me.ComboBox1.Location = New System.Drawing.Point(120, 136)
    Me.ComboBox1.Name = "ComboBox1"
    Me.ComboBox1.Size = New System.Drawing.Size(121, 20)
    Me.ComboBox1.TabIndex = 10
    Me.ComboBox1.Text = "ComboBox1"
    '
    'Button2
    '
    Me.Button2.Location = New System.Drawing.Point(8, 208)
    Me.Button2.Name = "Button2"
    Me.Button2.TabIndex = 11
    Me.Button2.Text = "Button2"
    '
    'StartUpForm
    '
    Me.AutoScaleBaseSize = New System.Drawing.Size(5, 12)
    Me.ClientSize = New System.Drawing.Size(292, 266)
    Me.Controls.Add(Me.Button2)
    Me.Controls.Add(Me.ComboBox1)
    Me.Controls.Add(Me.Label6)
    Me.Controls.Add(Me.Button1)
    Me.Controls.Add(Me.Label5)
    Me.Controls.Add(Me.Label4)
    Me.Controls.Add(Me.NumericUpDown2)
    Me.Controls.Add(Me.Label3)
    Me.Controls.Add(Me.NumericUpDown1)
    Me.Controls.Add(Me.Label2)
    Me.Controls.Add(Me.TextBox1)
    Me.Controls.Add(Me.Label1)
    Me.Name = "StartUpForm"
    Me.Text = "StartUpForm"
    CType(Me.NumericUpDown1, System.ComponentModel.ISupportInitialize).EndInit()
    CType(Me.NumericUpDown2, System.ComponentModel.ISupportInitialize).EndInit()
    Me.ResumeLayout(False)

  End Sub

#End Region

  'ドロップダウンリストの値
  Private Const DATA_FROM_DB As String = "ACCESSより取得したData"
  Private Const DATA_FROM_PG As String = "プログラムより取得したData"
  'プロトタイプのキー名
  Private Const PROTOTYPE_NAME As String = "prototype"
  'プロトタイプ管理クラス
  Private m_manager As PrototypeManager

  '画面ロード時のイベント
  Private Sub StartUpForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Me.Text = "ProtoType"

    Me.Label1.Text = "フォームのタイトル"
    Me.TextBox1.Text = ""

    Me.Label2.Text = "フォームのサイズ(幅)"
    Me.NumericUpDown1.Minimum = Me.Width
    Me.NumericUpDown1.Maximum = 1000
    Me.NumericUpDown1.Value = Me.Width

    Me.Label3.Text = "フォームのサイズ(高さ)"
    Me.NumericUpDown2.Minimum = Me.Height
    Me.NumericUpDown2.Maximum = 1000
    Me.NumericUpDown2.Value = Me.Height

    Me.Label4.Text = "背景色"
    Me.Label5.Text = ""
    Me.Label5.BackColor = Me.BackColor

    Me.Label5.BorderStyle = BorderStyle.FixedSingle
    Me.Label6.Text = "表示するデータソース"

    Me.Button1.Text = "参照"
    Me.Button2.Text = "上記設定のフォームを表示する"
    Me.Button2.Width = 180

    'ComboBoxのリスト生成
    Me.ComboBox1.DropDownStyle = ComboBoxStyle.DropDownList
    Me.ComboBox1.Items.Clear()
    Me.ComboBox1.Items.Add(Me.DATA_FROM_DB)
    Me.ComboBox1.Items.Add(Me.DATA_FROM_PG)
    Me.ComboBox1.SelectedIndex = 0
    Me.ComboBox1.Width = 160

    'プロトタイプの作成と登録
    Me.m_manager = New PrototypeManager
    Dim prototypeForm As ClonableForm
    prototypeForm = New ClonableForm   'プロトタイプの作成
    Me.m_manager.Register(Me.PROTOTYPE_NAME, prototypeForm) '登録
  End Sub

  '参照ボタンクリック時のイベント
  Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click

    Dim cd As ColorDialog = New ColorDialog
    Try
      cd.Color = Me.Label5.BackColor
      cd.FullOpen = True
      cd.AnyColor = True
      cd.SolidColorOnly = False
      cd.ShowHelp = True

      If cd.ShowDialog(Me) = DialogResult.OK Then
        Me.Label5.BackColor = cd.Color
      End If
      cd.Dispose()
    Finally
      If Not cd Is Nothing Then cd.Dispose()
    End Try
  End Sub

  '上記設定のフォームを表示するボタンクリック時のイベント
  Private Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Click
    '表示するDataTableを設定
    Dim displayDataTable As DataTable
    If Me.ComboBox1.Text = Me.DATA_FROM_DB Then
      displayDataTable = DisplayDataTableCreator.GetDataTableFromDatabase()
    ElseIf Me.ComboBox1.Text = Me.DATA_FROM_PG Then
      displayDataTable = DisplayDataTableCreator.GetDataTableFromProgram()
    End If

    'プロトタイプの取得
    Dim createdForm As IComponent = Me.m_manager.Create(Me.PROTOTYPE_NAME)
    'プロトタイプをカスタマイズして表示する
    createdForm.Display(Me.TextBox1.Text, _
                 New System.Drawing.Size(CType(Me.NumericUpDown1.Text, Integer), _
                                  CType(Me.NumericUpDown2.Text, Integer)), _
                 Me.Label5.BackColor, _
                 displayDataTable, _
                 Me)
  End Sub
End Class
このカテゴリーの先頭へ このページの先頭へ

複製を可能にするインターフェースです。
ICloneable インターフェースを継承しています。

IComponent.vb
'複製を可能にするインターフェース
Public Interface IComponent
  Inherits ICloneable

  '表示するメソッド定義
  Sub Display(ByVal titleString As String, _
          ByVal formSize As Size, _
          ByVal formBackColor As Color, _
          ByVal displaydatatable As DataTable, _
          ByVal owner As Form)
  '複製を作成するメソッド定義
  Function CreateClone() As IComponent
  '自身を破棄するメソッド定義
  Sub Dispose(ByVal disposing As Boolean)

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

複製が可能な Form です。
IComponent インターフェースを実装しています。

ClonableForm.vb
'複製が可能なFormクラス
Public Class ClonableForm
  Inherits System.Windows.Forms.Form
  Implements IComponent

#Region " Windows フォーム デザイナで生成されたコード "

  Public Sub New()
    MyBase.New()

    ' この呼び出しは Windows フォーム デザイナで必要です。
    InitializeComponent()

    ' InitializeComponent() 呼び出しの後に初期化を追加します。

  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 フォーム デザイナを使って変更してください。  
  ' コード エディタを使って変更しないでください。
  <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
    components = New System.ComponentModel.Container
    Me.Text = "ClonableForm"
  End Sub

#End Region

  '自身の複製を作成する
  Private Function Clone() As Object Implements IComponent.Clone
    Return Me 'Shallow Copyを返却する
  End Function

  '表示
  Public Sub Display(ByVal titleString As String, _
              ByVal formSize As Size, _
              ByVal formBackColor As Color, _
              ByVal displaydatatable As DataTable, _
              ByVal owner As Form) Implements IComponent.Display
    Me.Text = titleString
    Me.Size = formSize
    Me.BackColor = formBackColor

    'DataGridを描画する
    Dim grid As DataGrid = New DataGrid
    grid.Location = New System.Drawing.Point(10, 25)
    grid.Size = New System.Drawing.Size(200, 200)
    grid.DataSource = displaydatatable
    Me.Controls.Add(grid)

    Me.ShowDialog(owner)
  End Sub

  '複製を作成する(IComponent型で返却する)
  Public Function CreateClone() As IComponent Implements IComponent.CreateClone
    Dim iformObjecet As IComponent
    iformObjecet = DirectCast(Me.Clone(), IComponent)
    Return iformObjecet
  End Function
End Class
このカテゴリーの先頭へ このページの先頭へ

StartUpForm のヘルパークラス PrototypeManager です。
欲しいオブジェクトのコピーを登録・作成してくれます。

PrototypeManager.vb
'IComponentインターフェースを利用し、インスタンスの複製を行う
Public Class PrototypeManager

  'IComponent型が格納されているHashTable
  Private m_components As Hashtable = New Hashtable

  'HashTableに、IComponent型のオブジェクトを登録する
  Public Sub Register(ByVal name As String, ByVal createdComponent As IComponent)
    Me.m_components.Add(name, createdComponent)
  End Sub

  'HashTableから、IComponent型のオブジェクトを取得する
  Public Function Create(ByVal name As String) As IComponent
    Dim createdForm As IComponent = DirectCast(Me.m_components(name), IComponent)
    Return createdForm.CreateClone()
  End Function

  '破棄
  Public Sub Dispose()
    For Each dic As DictionaryEntry In Me.m_components
      DirectCast(dic.Value, IComponent).Dispose(True)
    Next
  End Sub
End Class
このカテゴリーの先頭へ このページの先頭へ

StartUpForm のヘルパークラス DisplayDataTableCreator です。
画面に表示したいデータを作成してくれます。

DisplayDataTableCreator.vb
'表示用データ作成
Public NotInheritable Class DisplayDataTableCreator

  'Accessよりデータの取得
  Public Shared Function GetDataTableFromDatabase() As DataTable
    Dim mdbpath As String = System.IO.Path.Combine(Application.StartupPath, "MyPrototype.mdb")
    Dim connection_string As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" _
                         + mdbpath + ";"
    'SELECT文
    Const SELECT_SCRIPT As String = "SELECT ID, Name FROM wankuma"

    'Accessよりデータの取得
    Dim myDa As CommonDataAccess.AbstructDataAccess
    myDa = New CommonDataAccess.OleDataAccess
    Try
      myDa.Open(connection_string)
      Return myDa.DataSelect(SELECT_SCRIPT)
    Finally
      myDa.Close()
    End Try
  End Function

  'プログラム内よりデータの取得
  Public Shared Function GetDataTableFromProgram() As DataTable
    Dim dt As DataTable = New DataTable("ゲーム")
    dt.Columns.Add("ID", GetType(String))
    dt.Columns.Add("Name", GetType(String))

    dt.Rows.Add(New String() {"1", "アトランチスの謎"})
    dt.Rows.Add(New String() {"2", "けっきょく南極大冒険"})
    dt.Rows.Add(New String() {"3", "マッピー"})
    dt.Rows.Add(New String() {"4", "エキサイトバイク"})
    dt.Rows.Add(New String() {"5", "ロードファイター"})
    Return dt
  End Function
End Class
このカテゴリーの先頭へ このページの先頭へ

■ひとこと

サンプルでは、StartUpForm の Load 時等に使用するプロトタイプを登録しています。
そして、StartUpForm 自身の Dispose メソッドが呼ばれた時、登録しているプロトタイプのインスタンスを破棄しています。


サンプルでは 1 つしかプロトタイプがありませんが、
業務で画面を作るとき、全ての画面は大体何パターンかに分けられるでしょう。
そのパターン毎にプロトタイプを用意し、使いまわして、独自の部分はデリゲート等で実装したりすると、
ちょっと実装が楽になりそうですね。

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