読み取り専用テーブル
CoddyのLuaジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 46/70。
クロージャは真のプライバシーを提供しますが、コンストラクター内でメソッドを定義する必要があります。別の用途には、別のアプローチがあります。それは、作成後にオブジェクトを読み取り専用にすることです。これは、固定点の座標のように、フィールドを表示可能にしつつ変更不可能にしたい場合に便利です。
__newindex metamethod は、テーブルに新しい値を代入しようとする試みをインターセプトします。すべての代入を all ブロックするようにすることで、object を凍結できます。
local ImmutablePoint = {}
function ImmutablePoint.new(x, y)
local data = {x = x, y = y}
local proxy = {}
setmetatable(proxy, {
__index = data,
__newindex = function()
error("Cannot modify immutable object")
end
})
return proxy
end秘訣はプロキシテーブルを使うことです。actual データはdataに存在し、ユーザーは空のproxyを操作します。
読み取り時には、__index が data から取得します。書き込み時には、代わりに __newindex がトリガーされ、これをブロックします。
local p = ImmutablePoint.new(10, 20)
print(p.x) -- 10(読み取りは動作する)
p.x = 50 -- エラー: 不変オブジェクトは変更できませんこのパターンは、configuration object、数学定数、または初期化後に決して変更されるべきでない任意の data に役立ちます。object は完全に読み取り可能なままです。ただし、変更することはできません。
チャレンジ
簡単ImmutableConfig class を作成して、読み取り専用の設定オブジェクトを生成しましょう!一度作成すると、これらの config オブジェクトでは設定の読み取りはできますが、変更しようとする操作はすべて完全にブロックされます。
コードは次の 2 つのファイルに分けて整理します。
ImmutableConfig.lua:凍結された設定オブジェクトを生成する module を作成します。.new(appName, version, maxUsers)function では、これら 3 つの値を internal に保存し、すべての field の読み取りを許可しながら、modifications を防ぐ proxy table を返します。値を変更しようとしたときは、actual な error を発生させる代わりにError: Config is read-onlyを Print します。main.lua:ImmutableConfig を実際に動かしてみましょう!config の値として 3 つの inputs を読み取り、config object を作成します。その後、次の操作を行って正しく動作することを示します。- 3 つの original の値をすべて Print する
maxUsersfield の modify を試みる(Error message が発生するはずです)- 値が実際には changed されていないことを証明するため、もう一度
maxUsersを Print する
次の 3 つの inputs を受け取ります。
- application name(string)
- version(string)
- maximum users(number)
出力には config の値、modification を試みたときの error、および値が変更されていないことの確認が表示されます。
App: {appName}
Version: {version}
Max Users: {maxUsers}
Error: Config is read-only
Max Users: {maxUsers}たとえば、inputs が MyApp、2.0、100 の場合、出力は次のようになります。
App: MyApp
Version: 2.0
Max Users: 100
Error: Config is read-only
Max Users: 100Remember:proxy pattern では、user が操作する empty table を使用し、actual data は別の internal table に保持されます。__index metamethod は data table から値を取得し、__newindex はあらゆる write の試みを intercept して(そして blocks して)くれます。
自分で試してみよう
-- ImmutableConfig モジュールをインポートする
local ImmutableConfig = require('ImmutableConfig')
-- 入力を読み取る
local appName = io.read()
local version = io.read()
local maxUsers = tonumber(io.read())
-- TODO: ImmutableConfig.new() を使用して config オブジェクトを作成する
-- TODO: 次の形式ですべての3つの元の値を出力する:
-- App: {appName}
-- Version: {version}
-- Max Users: {maxUsers}
-- TODO: maxUsers フィールドを変更しようとする(これによりエラーメッセージがトリガーされるはず)
-- TODO: 変更されていないことを証明するために maxUsers を再度出力する
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Luaオンラインコンパイラ