Singletonパターン
CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 91/104。
Singleton パターンは、プログラム全体を通してクラスのインスタンスが 1 つだけ存在することを保証し、それにアクセスするためのグローバルな窓口を提供します。これは、設定マネージャー、ロガー、データベース接続プールなど、共有すべきリソースに便利です。
重要な考え方は、誰もインスタンスを直接作成できないように constructor を 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 は「マジック静的変数」と呼ばれる C++11 の機能です。これはスレッドセーフで、1 回だけ初期化されることが保証されています。コピーコンストラクターと代入演算子を削除することで、シングルトンのコピーを誰も作成できないようにしています。
Singleton の使用は控えめにしましょう。Singleton はグローバル状態を導入するため、テストが難しくなる可能性があります。複数のインスタンスがあると問題が生じる、本当に共有すべきリソースに最も適しています。
チャレンジ
簡単ゲームアプリケーションの設定を管理する GameSettings シングルトンを構築しましょう。これは Singleton パターンの最適な使用例です。ゲームのすべての部分からアクセスできる設定マネージャーを1つだけ用意し、全体で一貫した設定を保証します。
コードを3つのファイルに分けて整理します。
GameSettings.h:ゲーム設定を管理するシングルトンクラスを定義します。GameSettingsクラスには、プライベートメンバーとして3つの設定を保持します。difficulty(文字列)、volume(0~100の整数)、fullscreen(ブール値)です。初期値として"Normal"、50、falseを設定します。Singleton パターンを正しく実装します。
- コンストラクターを private にする
- コピーコンストラクターと代入演算子を Delete する
- 単一のインスタンスへの参照を返す静的な
getInstance()メソッドを提供する
設定を操作するために、次の public メソッドを追加します。
setDifficulty(const std::string& diff):difficulty を更新しますsetVolume(int vol):volume を更新しますsetFullscreen(bool fs):fullscreen モードを更新しますdisplaySettings():現在のすべての設定を出力します
GameModule.h:シングルトンにアクセスする独立したモジュールを作成します。プリセット名(文字列)を受け取る
applyGamePresetという名前の関数を定義します。この関数はGameSettingsシングルトンにアクセスし、プリセットに基づいて設定します。"casual":difficulty を"Easy"、volume を70、fullscreen をfalseに設定します"competitive":difficulty を"Hard"、volume を30、fullscreen をtrueに設定します- その他のプリセット:デフォルト設定を変更せずに維持します
これにより、プログラムの異なる部分から同じシングルトンインスタンスにアクセスし、変更できることが示されます。
main.cpp:すべてをまとめ、シングルトンの動作を示します。2つの入力を読み取ります。
- プリセット名(文字列)
- カスタム音量レベル(整数)
まず、シングルトンに対して
displaySettings()を呼び出し、初期状態のデフォルト設定を表示します。その後に空行を出力します。次に、
applyGamePreset関数を使用してプリセットを適用し、設定をもう一度表示します。その後に空行を出力します。最後に、カスタム入力値で volume だけを上書きし、最終的な設定を表示します。
displaySettings() メソッドは、次の正確な形式で出力する必要があります。
Difficulty: [value]
Volume: [value]
Fullscreen: [yes/no]たとえば、入力が casual と 85 の場合:
Difficulty: Normal
Volume: 50
Fullscreen: no
Difficulty: Easy
Volume: 70
Fullscreen: no
Difficulty: Easy
Volume: 85
Fullscreen: no入力が competitive と 20 の場合:
Difficulty: Normal
Volume: 50
Fullscreen: no
Difficulty: Hard
Volume: 30
Fullscreen: yes
Difficulty: Hard
Volume: 20
Fullscreen: yesmain.cpp と GameModule.h の両方が同じシングルトンインスタンスにアクセスしていることに注目してください。一方の場所で行った変更は、どこからでも確認できます。これが、ゲーム設定のような共有リソースに Singleton パターンを使用する強みです。
自分で試してみよう
#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;
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
3コンストラクタとデストラクタ
デフォルトコンストラクタパラメータ付きコンストラクタコピーコンストラクタムーブコンストラクタコンストラクタ初期化リスト委譲コンストラクタデストラクタ徹底解説Rule of Three / Five / Zero復習 - Stringクラス6演算子オーバーロード
演算子オーバーロード入門算術演算子のオーバーロード比較演算子のオーバーロードストリーム演算子代入演算子のオーバーロード[] と () の演算子オーバーロード型変換演算子復習 - Matrixクラス自分で練習してみよう: C++オンラインコンパイラ