Menu
Coddy logo textTech

シングルトンパターン

CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 91/104。

シングルトンパターンは、クラスのインスタンスがプログラム全体で1つだけであることを保証し、それへのグローバルなアクセスポイントを提供します。これは、設定マネージャー、ロガー、データベース接続プールなど、共有されるべきリソースに役立ちます。

主な考え方は、コンストラクタをprivateにして誰も直接インスタンスを作成できないようにし、単一のインスタンスを返す静的メソッドを提供することです。

#include <iostream>
#include <string>

class Logger {
private:
    std::string logFile;
    
    // プライベートコンストラクタ - 直接のインスタンス化を防ぐ
    Logger() : logFile("app.log") {}
    
    // コピーコンストラクタと代入演算子を削除
    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;

public:
    // 単一のインスタンスにアクセスするための静的メソッド
    static Logger& getInstance() {
        static Logger instance;  // 一度だけ作成され、プログラムが終了するまで保持される
        return instance;
    }
    
    void log(const std::string& message) {
        std::cout << "[LOG] " << message << "\n";
    }
};

int main() {
    Logger::getInstance().log("Application started");
    
    Logger& logger = Logger::getInstance();
    logger.log("Processing data");
}

getInstance() 内の static Logger instance は、「マジックスタティック(magic statics)」と呼ばれる C++11 の機能です。これはスレッドセーフであり、一度だけ初期化されることが保証されています。コピーコンストラクタと代入演算子を削除することで、シングルトンのコピーが作成されるのを防ぎます。

シングルトン(Singleton)の使用は控えめにしてください。グローバルな状態を導入するため、テストが困難になる可能性があります。複数のインスタンスが存在すると問題が発生するような、真に共有されるリソースに最も適しています。

challenge icon

チャレンジ

簡単

ゲームアプリケーションの設定を管理する GameSettings シングルトンを構築しましょう。これはシングルトンパターンの完璧なユースケースです。ゲームのすべての部分からアクセスできる設定マネージャーをただ1つだけ用意することで、全体を通して一貫した設定を保証したいからです。

コードは以下の3つのファイルに分けて構成します:

  • GameSettings.h: ゲーム設定を管理するシングルトンクラスを定義します。

    GameSettings クラスは、3つの設定をプライベートメンバとして保持する必要があります:difficulty (string)、volume (0-100のinteger)、および fullscreen (boolean)。これらをデフォルト値 "Normal"50false で初期化してください。

    シングルトンパターンを適切に実装してください:

    • コンストラクタを private にする
    • コピーコンストラクタと代入演算子を削除(delete)する
    • 唯一のインスタンスへの参照を返す静的な getInstance() メソッドを提供する

    設定を操作するために、以下のパブリックメソッドを追加してください:

    • setDifficulty(const std::string& diff) — 難易度を更新します
    • setVolume(int vol) — 音量を更新します
    • setFullscreen(bool fs) — フルスクリーンモードを更新します
    • displaySettings() — 現在のすべての設定を表示します
  • GameModule.h: シングルトンにアクセスする別のモジュールを作成します。

    プリセット名 (string) を受け取る applyGamePreset という関数を定義してください。この関数は GameSettings シングルトンにアクセスし、プリセットに基づいて設定を行います:

    • "casual" — 難易度を "Easy"、音量を 70、フルスクリーンを false に設定します
    • "competitive" — 難易度を "Hard"、音量を 30、フルスクリーンを true に設定します
    • それ以外のプリセット — デフォルト設定をそのまま維持します

    これにより、プログラムの異なる部分が同じシングルトンインスタンスにアクセスし、変更を加える方法が示されます。

  • main.cpp: すべてを組み合わせて、シングルトンの動作を確認します。

    2つの入力を読み取ります:

    1. プリセット名 (string)
    2. カスタム音量レベル (integer)

    まず、シングルトンの displaySettings() を呼び出して、初期のデフォルト設定を表示します。その後に空行を1つ出力してください。

    次に、applyGamePreset 関数を使用してプリセットを適用し、再度設定を表示します。その後に空行を1つ出力してください。

    最後に、入力されたカスタム値で音量のみを上書きし、最終的な設定を表示します。

displaySettings() メソッドは、以下の形式で正確に出力する必要があります:

Difficulty: [value]
Volume: [value]
Fullscreen: [yes/no]

例えば、入力が casual85 の場合:

Difficulty: Normal
Volume: 50
Fullscreen: no

Difficulty: Easy
Volume: 70
Fullscreen: no

Difficulty: Easy
Volume: 85
Fullscreen: no

入力が competitive20 の場合:

Difficulty: Normal
Volume: 50
Fullscreen: no

Difficulty: Hard
Volume: 30
Fullscreen: yes

Difficulty: Hard
Volume: 20
Fullscreen: yes

main.cppGameModule.h の両方が同じシングルトンインスタンスにアクセスしていることに注目してください。一方で行われた変更は、どこからでも確認できます。これが、ゲーム設定のような共有リソースにおけるシングルトンパターンの強力な点です。

チートシート

シングルトン・パターン(Singleton pattern)は、プログラム全体でクラスのインスタンスが1つだけであることを保証し、それへのグローバルなアクセスを提供します。設定マネージャー、ロガー、データベース接続プールなどの共有リソースに役立ちます。

主な実装手順:

  • 直接のインスタンス化を防ぐために、コンストラクタをprivateにする
  • コピーを防止するために、コピーコンストラクタと代入演算子を削除(delete)する
  • 唯一のインスタンスへの参照を返す静的メソッドを提供する
class Logger {
private:
    std::string logFile;
    
    // privateコンストラクタ
    Logger() : logFile("app.log") {}
    
    // コピーコンストラクタと代入演算子を削除
    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;

public:
    // 唯一のインスタンスにアクセスするための静的メソッド
    static Logger& getInstance() {
        static Logger instance;  // 一度だけ作成され、プログラムが終了するまで生存する
        return instance;
    }
    
    void log(const std::string& message) {
        std::cout << "[LOG] " << message << "\n";
    }
};

// 使用例
Logger::getInstance().log("Application started");

Logger& logger = Logger::getInstance();
logger.log("Processing data");

getInstance() 内の static Logger instance は、C++11の「マジックスタティック(magic statics)」を使用しています。これはスレッドセーフであり、一度だけ初期化されることが保証されています。

シングルトンはグローバルな状態を導入し、テストを困難にする可能性があるため、控えめに使用してください。複数のインスタンスが存在すると問題が発生するような、真に共有されるリソースに最適です。

自分で試してみよう

#include <iostream>
#include <string>
#include "GameSettings.h"
#include "GameModule.h"

using namespace std;

int main() {
    // 入力を読み込む
    string presetName;
    int customVolume;
    cin >> presetName;
    cin >> customVolume;
    
    // TODO: GameSettings のシングルトンインスタンスを取得する
    
    // TODO: 初期のデフォルト設定を表示する
    
    // TODO: 空行を出力する
    
    // TODO: applyGamePreset 関数を使用してプリセットを適用する
    
    // TODO: プリセット適用後の設定を表示する
    
    // TODO: 空行を出力する
    
    // TODO: 音量を customVolume で上書きする
    
    // TODO: 最終的な設定を表示する
    
    return 0;
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン