Singleton (シングルトン) パターン

インスタンスが必ず 1 つしか生成されない事を保障するデザインパターンです。
コーディングする人が、意識して必ず 1 つしかインスタンスを生成せず、使いまわす事は可能ですが
そのクラスのインスタンスが必ず 1 つである事を保障はできませんよね。


今回は、VisualBasic6.0 の時にあった、Forms コレクションを Singleton パターンを用いて擬似的に再現しました。
参考にしたのは、Visual Basic .NET で Forms コレクションを作成する方法です。
※マルチスレッド環境における Singleton の振る舞いはちょっと複雑みたいなので、ここではシングルスレッド環境である事を前提としています。

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

■クラス図

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

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

■サンプルの説明

サンプルの見た目  サンプルの見た目
Form1 を表示ボタンをクリックすると、右の何もない Form1 をモードレスで表示します。(何回かクリックして沢山表示してみて下さい)
テキストボックスに何か入力し、タイトルバー変更(StartUp) またはタイトルバー変更(GetInstance) ボタンをクリックすると
表示されている Form1 のタイトルバーの文字をテキストボックスの値に変更します。
終了ボタンをクリックすると、開いている全ての Form1 を破棄して、アプリケーションを終了します。
サンプルのプロジェクトダウンロード

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

■コード

アプリケーションのエントリポイント StartUp クラス です。
Formsコレクションを内部で生成し、Shared なメンバ変数に値をセットします。
次に、StartUpForm クラスのインスタンスを生成し、アプリケーションを開始しています。

StartUp.vb
Public Class StartUp

  'フォームコレクション
  Public Shared Forms As FormsCollection

  'アプリケーションのエントリポイント
  <STAThread()> _
  Shared Sub Main()
    AddHandler Application.ThreadException, AddressOf CommonExceptionAndHandler.ExceptionHandler.MyHandler
    'GetInstanceメソッドにより、インスタンスを取得する
    Forms = FormsCollection.GetInstance()
    Application.Run(New StartUpForm)
  End Sub
End Class
このカテゴリーの先頭へ このページの先頭へ

アプリケーション開始時に表示される Form です。

StartUpForm.vb
Public Class StartUpForm
  Inherits System.Windows.Forms.Form

#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 フォーム デザイナを使って変更してください。  
  ' コード エディタを使って変更しないでください。
  Friend WithEvents Button1 As System.Windows.Forms.Button
  Friend WithEvents Button2 As System.Windows.Forms.Button
  Friend WithEvents Button3 As System.Windows.Forms.Button
  Friend WithEvents Button4 As System.Windows.Forms.Button
  Friend WithEvents TextBox1 As System.Windows.Forms.TextBox
  <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
    Me.Button1 = New System.Windows.Forms.Button
    Me.Button2 = New System.Windows.Forms.Button
    Me.Button3 = New System.Windows.Forms.Button
    Me.Button4 = New System.Windows.Forms.Button
    Me.TextBox1 = New System.Windows.Forms.TextBox
    Me.SuspendLayout()
    '
    'Button1
    '
    Me.Button1.Location = New System.Drawing.Point(8, 24)
    Me.Button1.Name = "Button1"
    Me.Button1.TabIndex = 0
    Me.Button1.Text = "Button1"
    '
    'Button2
    '
    Me.Button2.Location = New System.Drawing.Point(112, 80)
    Me.Button2.Name = "Button2"
    Me.Button2.TabIndex = 1
    Me.Button2.Text = "Button2"
    '
    'Button3
    '
    Me.Button3.Location = New System.Drawing.Point(112, 112)
    Me.Button3.Name = "Button3"
    Me.Button3.TabIndex = 2
    Me.Button3.Text = "Button3"
    '
    'Button4
    '
    Me.Button4.Location = New System.Drawing.Point(8, 152)
    Me.Button4.Name = "Button4"
    Me.Button4.TabIndex = 3
    Me.Button4.Text = "Button4"
    '
    'TextBox1
    '
    Me.TextBox1.Location = New System.Drawing.Point(8, 96)
    Me.TextBox1.Name = "TextBox1"
    Me.TextBox1.TabIndex = 4
    Me.TextBox1.Text = "TextBox1"
    '
    'StartUpForm
    '
    Me.AutoScaleBaseSize = New System.Drawing.Size(5, 12)
    Me.ClientSize = New System.Drawing.Size(292, 266)
    Me.Controls.Add(Me.TextBox1)
    Me.Controls.Add(Me.Button4)
    Me.Controls.Add(Me.Button3)
    Me.Controls.Add(Me.Button2)
    Me.Controls.Add(Me.Button1)
    Me.Name = "StartUpForm"
    Me.Text = "StartUpForm"
    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 = "Singleton"
    Me.TextBox1.Text = ""

    'ボタンに長い文字列を表示する為、サイズ変更
    Dim sz As Size = New System.Drawing.Size(170, 23)
    Me.Button1.Size = sz
    Me.Button2.Size = sz
    Me.Button3.Size = sz
    Me.Button4.Size = sz

    Me.Button1.Text = "Form1を表示"
    Me.Button2.Text = "タイトルバー変更(StartUp)"
    Me.Button3.Text = "タイトルバー変更(GetInstance)"
    Me.Button4.Text = "終了"
  End Sub

  'Form1を表示ボタン押下時のイベント
  Private Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim myFormtest1 As Form1 = New Form1
    myFormtest1.Show()

    For Each formItem As Form In StartUp.Forms
      Console.WriteLine("コレクションに" + formItem.Name + "が入っています。")
    Next
  End Sub

  'タイトルバー変更(StartUp)
  Private Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Click
    For Each formItem As Form In StartUp.Forms
      formItem.Text = Me.TextBox1.Text
    Next
  End Sub

  'タイトルバー変更(GetInstance)
  Private Sub Button3_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button3.Click
    Dim testSingleton As FormsCollection
    testSingleton = testSingleton.GetInstance()
    For Each formItem As Form In testSingleton
      formItem.Text = Me.TextBox1.Text
    Next
  End Sub

  '終了ボタン押下時のイベント
  Private Sub Button4_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button4.Click
    'Formsコレクションで保持しているForm群の破棄
    Dim forms(StartUp.Forms.Count - 1) As Form
    Dim counter As Integer = 0
    For Each formItem As Form In StartUp.Forms
      forms(counter) = formItem
      counter += 1
    Next
    For Each currentForm As Form In forms
      currentForm.Dispose()
    Next
    Application.Exit()
  End Sub
End Class
このカテゴリーの先頭へ このページの先頭へ

当アプリケーションにおいて、包含している Form オブジェクトを管理するコレクションです。
このクラスは、GetInstanse メソッドを介してインスタンスを取得する事により、
常に単一インスタンスである事を保障しています。

FormsCollection.vb
'フォームコレクション
Public NotInheritable Class FormsCollection
  Inherits CollectionBase

  '自身のインスタンス
  Private Shared m_formsCollection As FormsCollection = New FormsCollection

  '外部非公開のコンストラクタ
  Private Sub New()
    Console.WriteLine("FormsCollectionインスタンスを生成しました")
  End Sub

  '自身のインスタンスを返却する
  Public Shared Function GetInstance() As FormsCollection
    Return m_formsCollection
  End Function

  'フォームコレクションにフォームを追加
  Public Shadows Function Add(ByVal FormObject As Form) As Form
    Console.WriteLine("FormsCollectionのAddが呼ばれました:" + FormObject.Name)
    MyBase.List.Add(FormObject)
    Return FormObject
  End Function

  'フォームコレクションからフォームを削除
  Public Shadows Sub Remove(ByVal FormObject As Form)
    Console.WriteLine("FormsCollectionのRemoveが呼ばれました:" + FormObject.Name)
    MyBase.List.Remove(FormObject)
  End Sub
End Class
このカテゴリーの先頭へ このページの先頭へ

StartUpForm クラスからモードレスで表示される Form1 です。
コンストラクタと Dispose メソッドの中で一工夫する事により、コレクションに追加・削除しています。

Form1.vb
'表示用フォーム(Newでコレクションに登録、Disposeでコレクションから削除)
Public Class Form1
  Inherits System.Windows.Forms.Form

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

  Public Sub New()
    MyBase.New()

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

    ' InitializeComponent() 呼び出しの後に初期化を追加します。
    'FormsCollectionに自身のインスタンスを追加
    StartUp.Forms.Add(Me)

  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)
    'FormsCollectionから自身のインスタンスを削除
    StartUp.Forms.Remove(Me)
  End Sub

  ' Windows フォーム デザイナで必要です。
  Private components As System.ComponentModel.IContainer

  ' メモ : 以下のプロシージャは、Windows フォーム デザイナで必要です。
  'Windows フォーム デザイナを使って変更してください。  
  ' コード エディタを使って変更しないでください。
  <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
    '
    'Form1
    '
    Me.AutoScaleBaseSize = New System.Drawing.Size(5, 12)
    Me.ClientSize = New System.Drawing.Size(292, 266)
    Me.Name = "Form1"
    Me.Text = "Form1"

  End Sub

#End Region

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

■ひとこと

StartUpForm クラスのタイトルバー変更(StartUp) またはタイトルバー変更(GetInstance) ボタンをクリックを実行
してみるとお解りいただけるかと思いますが、どちらも同じ FormsCollection のインスタンスを参照しています。
同一アプリケーション内で Form の集合体は、1 つで十分です。逆に複数あった場合は、整合性が取れなくなります。
FormCollection が唯一のインスタンスである事が保障されているので、終了ボタンをクリックした時のコード内で
確実に開いている全ての Form オブジェクトを破棄することができます。

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