命名規則
CoddyのLuaジャーニー「Object Oriented Programming」セクションの一部 — レッスン 43/70。
Luaでは、すべてのテーブルフィールドはデフォルトでパブリックにアクセス可能です。誰でも self.health を直接読み取ったり変更したりできます。Luaはプライバシーを強制しませんが、プログラマーは意図を伝えるために命名規則を使用します。これは、どのフィールドを内部の実装詳細として扱うべきかを示すものです。
プライベートフィールドの前にアンダースコアを付けるのが、標準的な慣習です:
local Player = {}
Player.__index = Player
function Player:new(name)
local obj = {
name = name, -- 公開: 直接アクセスしてもよい
_health = 100, -- 非公開: 代わりにメソッドを使用する
_score = 0 -- 非公開: 内部追跡用
}
setmetatable(obj, Player)
return obj
end
function Player:getHealth()
return self._health
end
function Player:takeDamage(amount)
self._health = self._health - amount
endアンダースコアはLuaの動作を変えるものではありません。player._health は技術的には依然としてアクセス可能です。しかし、これは他のプログラマー(そして将来の自分自身)に対する明確な警告として機能します。「このフィールドはプライベートであることを意図しています。代わりにメソッドを介してアクセスしてください。」
この慣習は、インターフェース(他者が使用すべきもの)と実装(内部でどのように動作するか)を分離するため重要です。後でバリデーションを追加したり、体力の保存方法を変更したりする必要が生じた場合でも、メソッドを修正するだけで済みます。:getHealth() を使用しているコードは、変更なしでそのまま動作し続けます。
チャレンジ
簡単アンダースコアの命名規則を使用して、どのフィールドがプライベートであることを意図しているかを適切に示す BankAccount クラスを作成しましょう!
コードは2つのファイルに分けて構成します:
BankAccount.lua: アカウント情報を管理するクラスを作成します。コンストラクタ:new(ownerName, initialBalance)は、所有者の名前をパブリックフィールド (owner) として、残高をプライベートフィールド (_balance) として保存する必要があります。以下の3つのメソッドを含めてください::getBalance()— 現在の残高を返します:deposit(amount)—_balanceに金額を加算します:getOwner()— 所有者の名前を返します
main.lua: BankAccount モジュールを読み込み、入力から所有者の名前、初期残高、および入金額を読み取ります。アカウントを作成し、入金を行い、所有者と最終残高を出力します。
3つの入力を受け取ります:
- 所有者の名前
- 初期残高(数値)
- 入金額(数値)
出力は、入金後の所有者と最終残高を示す2行にする必要があります:
Owner: {ownerName}
Balance: {finalBalance}例えば、入力が Maria、500、150 の場合、出力は以下のようになります:
Owner: Maria
Balance: 650注意:_balance のアンダースコアの接頭辞は、このフィールドが直接ではなく、:getBalance() や :deposit() のようなメソッドを通じてアクセスされるべきであることを他のプログラマーに示します。アンダースコアのない owner フィールドは、パブリックにアクセスしても問題ないことを示しています。
チートシート
Luaでは、すべてのテーブルフィールドはデフォルトでパブリックにアクセス可能です。どのフィールドを内部の実装詳細として扱うべきかを伝えるには、プライベートなフィールドの前にアンダースコア(_)を付ける**アンダースコア命名規則**を使用します。
アンダースコアはプライバシーを強制するものではありません。それは、そのフィールドが直接ではなくメソッドを通じてアクセスされるべきであることを他のプログラマーに示すシグナルです。
local Player = {}
Player.__index = Player
function Player:new(name)
local obj = {
name = name, -- パブリック: 直接アクセスしてOK
_health = 100, -- プライベート: 代わりにメソッドを使用する
_score = 0 -- プライベート: 内部的な追跡用
}
setmetatable(obj, Player)
return obj
end
function Player:getHealth()
return self._health
end
function Player:takeDamage(amount)
self._health = self._health - amount
endこの慣習は、**インターフェース**(他者が使用すべきもの)と**実装**(内部でどのように動作するか)を分離し、変更が必要な際のコードの保守性を高めます。
自分で試してみよう
-- BankAccountモジュールを読み込む
local BankAccount = require('BankAccount')
-- 入力を読み込む
local ownerName = io.read()
local initialBalance = tonumber(io.read())
local depositAmount = tonumber(io.read())
-- TODO: ownerNameとinitialBalanceを使用して新しいBankAccountを作成する
-- TODO: depositAmountを使用して預金を行う
-- TODO: 指定された形式で所有者と最終残高を表示する
-- 形式:
-- Owner: {ownerName}
-- Balance: {finalBalance}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。