シングルトンテーブル
CoddyのLuaジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 63/70。
プログラム全体で、設定マネージャー、ロギングシステム、またはゲーム設定など、あるもののインスタンスが正確に1つだけ必要になることがあります。複数のコピーを作成すると、どれが正しいデータを持っているのかという問題が発生します。Singletonパターンは、インスタンスが1つしか存在しないことを保証します。
Luaでは、モジュールの仕組みのおかげで、シングルトンの実装は驚くほど簡単です。モジュールを require すると、Luaはその結果をキャッシュします。その後の require の呼び出しでは、新しいテーブルではなく、キャッシュされたテーブルが返されます。
-- GameConfig.lua
local GameConfig = {
volume = 80,
difficulty = "normal"
}
function GameConfig:setVolume(v)
self.volume = v
end
return GameConfig
これで、プログラム内のどこからでも:
local config1 = require("GameConfig")
local config2 = require("GameConfig")
config1:setVolume(50)
print(config2.volume) -- 50 (同じテーブルです!)
config1とconfig2の両方が、まったく同じテーブルを参照しています。一方の変数を通じて行われた変更は、もう一方の変数からも確認できます。これは、Luaのrequireが戻り値をpackage.loadedに保存して再利用するため、自動的に発生します。
新しいオブジェクトを作成するFactoryパターンとは異なり、Singletonはアプリケーション全体で共有された状態を保証します。これは、コードの複数の部分が一貫してアクセスし、変更する必要があるグローバル設定に最適です。
チャレンジ
簡単シングルトンパターンを使用して、ゲーム設定マネージャーを構築しましょう!プログラムのどの部分から呼び出されても、まったく同じ設定にアクセスできるようにする設定モジュールを作成します。何度 require されても、常に同じインスタンスが返されるようにします。
コードは2つのファイルに分けて構成します:
GameSettings.lua: ゲーム設定を保存するシングルトンモジュールを作成します。設定テーブルには、musicVolume(初期値 70)、sfxVolume(初期値 100)、およびdifficulty(初期値"normal")を含める必要があります。これらの設定を変更するために、:setMusicVolume(value)、:setSfxVolume(value)、および:setDifficulty(level)の3つのメソッドを追加してください。モジュールの最後でテーブルを返すことを忘れないでください!main.lua: GameSettings モジュールを2つの異なる変数にrequireすることで、シングルトンが機能することを実証します。1つ目の変数を使用して、音楽の音量と難易度を変更します。次に、2つ目の変数を使用して3つの設定すべてを出力します。シングルトンが正しく機能していれば、両方の変数は同じテーブルを参照しているため、一方で行った変更がもう一方にも反映されます。
2つの入力を受け取ります:
- 新しい音楽の音量(数値)
- 新しい難易度レベル(
"easy"や"hard"などの文字列)
1つ目の変数を通じて設定を変更した後、2つ目の変数を使用して3つの設定を、音楽の音量、SFXの音量、難易度の順に、それぞれ個別の行に出力してください。
例えば、入力が 50 と hard の場合、出力は以下のようになります:
50
100
hard入力が 25 と easy の場合、出力は以下のようになります:
25
100
easySFXの音量は変更していないため、デフォルト値の 100 のままですが、音楽の音量と難易度は1つ目の変数を通じて行われた変更を反映しています。これにより、両方の変数が同じシングルトンインスタンスを指していることが証明されます!
自分で試してみよう
-- main.lua - Singleton パターンのデモンストレーション
-- 入力を読み込む
local newMusicVolume = tonumber(io.read())
local newDifficulty = io.read()
-- TODO: GameSettings モジュールを require して変数(例: settings1)に代入する
-- TODO: GameSettings モジュールを「再度」別の変数(例: settings2)に require する
-- (Singleton が正しく機能していれば、両方の変数は同じテーブルを参照します!)
-- TODO: 1つ目の変数を使用して以下を行う:
-- - musicVolume を newMusicVolume に設定する
-- - difficulty を newDifficulty に設定する
-- TODO: 2つ目の変数を使用して設定を表示する
-- 各項目を個別の行に表示する: musicVolume, sfxVolume, difficulty
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。