Winformアプリで シンプルなコードからSingleton Patternへ移行しよう

2024年8月21日

このチュートリアルでは、簡単なゲームプログラムを作成し、それをより安全で一貫した設計に改善する方法を学びます。まず、シンプルなコードを書き、その後、シングルトンパターンというデザインパターンを導入して、コードの設計を改善していきます。ここでは、ゲーム全体の管理を担う「ゲームマネージャ」をシングルトンパターンで実装し、ゲームの状態や進行を一貫して管理する方法を学びます。また、シングルトンパターンを使って、インスタンスが複数作成されないことを確認する例も追加します。


ステップ1: シンプルなゲームコードを作成する

まずは、以下のシンプルなゲームコードを見てください。このコードは、ゲームのスコアを管理し、ボタンをクリックするたびにスコアが増加します。

public partial class Form1 : Form
{
    private GameManager gameManager;

    public Form1()
    {
        InitializeComponent();
        gameManager = new GameManager();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        gameManager.Score++;
        MessageBox.Show($"現在のスコア: {gameManager.Score}");
    }
}
public class GameManager
{
    public int Score { get; set; }
}

このコードはシンプルで理解しやすいですが、GameManagerクラスのインスタンスが複数作成される可能性があり、スコアの一貫性が失われるリスクがあります。


ステップ2: 問題点を理解する

この設計には、以下のような問題があります:

  • スコアの一貫性が欠如する可能性GameManagerクラスのインスタンスが複数存在すると、異なるスコアが管理され、一貫性が失われるリスクがあります。
  • ゲーム全体の状態管理が困難: ゲームのスコアや設定などが複数のインスタンスで管理されると、ゲームの進行において問題が生じる可能性があります。

ステップ3: シングルトンパターンの基本を学ぶ

シングルトンパターンとは?

シングルトンパターンは、クラスのインスタンスが1つしか存在しないことを保証し、そのインスタンスへのグローバルなアクセスを提供するデザインパターンです。

シングルトンパターンの構成

  • プライベートコンストラクタ: インスタンスが外部から作成されないように、コンストラクタをプライベートにします。
  • 静的なインスタンス: クラス自身のインスタンスを保持するための静的フィールドを作成します。
  • パブリックな静的メソッド: インスタンスを取得するための静的メソッドを提供します。

ステップ4: シングルトンパターンを適用する

それでは、シンプルなゲームコードをシングルトンパターンを使って改善してみましょう。

GameManager クラスをシングルトンパターンに変更する

まず、GameManagerクラスをシングルトンパターンとして実装します。

public class GameManager
{
    private static GameManager instance;
    public int Score { get; set; }

    // プライベートコンストラクタ
    private GameManager() { }

    // インスタンスを取得するための静的メソッド
    public static GameManager GetInstance()
    {
        if (instance == null)
        {
            instance = new GameManager();
        }
        return instance;
    }
}

Form1クラスを更新する

Form1クラス内でGameManagerのインスタンスをシングルトンから取得するようにします。

public partial class Form1 : Form
{
    private GameManager gameManager;

    public Form1()
    {
        InitializeComponent();
        gameManager = GameManager.GetInstance();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        gameManager.Score++;
        MessageBox.Show($"現在のスコア: {gameManager.Score}");
    }
}

ステップ5: インスタンスが複数作成されないことを確認する

シングルトンパターンが正しく実装されているか確認するために、次のようなコードを追加して、複数のインスタンスが作成されないことを確認します。

public partial class Form1 : Form
{
    private GameManager gameManager1;
    private GameManager gameManager2;

    public Form1()
    {
        InitializeComponent();
        gameManager1 = GameManager.GetInstance();
        gameManager2 = GameManager.GetInstance();

        // インスタンスが同一かどうかを確認
        if (gameManager1 == gameManager2)
        {
            MessageBox.Show("同じインスタンスが使用されています。");
        }
        else
        {
            MessageBox.Show("異なるインスタンスが作成されています。");
        }
    }

    private void button1_Click(object sender, EventArgs e)
    {
        gameManager1.Score++;
        MessageBox.Show($"現在のスコア: {gameManager1.Score}");
    }
}

上記のコードでは、gameManager1gameManager2が同一のインスタンスであることが確認されるので、シングルトンパターンが正しく機能していることが確認できます。


ステップ6: 振り返りと応用

シングルトンパターンを導入したことで、ゲームのスコアや設定が一貫して管理され、ゲーム全体の状態が正確に維持されるようになりました。また、インスタンスが複数作成されないことも確認できました。

振り返り

  • ゲーム状態の一貫性の確保: シングルトンパターンを使用することで、ゲーム内で唯一のGameManagerインスタンスが使用され、スコアや設定が一貫して管理されます。
  • 状態管理の簡素化: 同じインスタンスを再利用することで、ゲームの進行や状態管理が容易になり、バグの発生を防ぎます。
  • インスタンスの重複防止: 複数のインスタンスが作成されないことを確認することで、シングルトンパターンが正しく機能していることがわかります。

応用例

このパターンは、ゲームのスコア管理以外にも、ゲーム設定やリソース管理など、アプリケーション全体で共有する必要があるリソースに応用できます。


このチュートリアルを通じて、シンプルなゲームコードからシングルトンパターンを導入するプロセスを学び、より一貫性があり、効率的なゲームを設計できるようになりましょう。