複数のミックスインの適用
CoddyのLuaジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 52/70。
単一のミックスインは、クラスに1つの動作のセットを追加します。しかし、実際のオブジェクトは複数のソースから機能を必要とすることがよくあります。ゲームオブジェクトは、画面上を移動しかつ表示される必要があるかもしれません。この2つは別々の関心事であり、同じミックスインにまとめるべきではありません。
解決方法は簡単です。同じクラスに複数のミックスインを適用します。各ミックスインが独自のメソッドを追加し、最終的にクラスはそれらすべてを持つことになります。
local Movable = {}
function Movable:move(dx, dy)
self.x = self.x + dx
self.y = self.y + dy
end
local Visible = {}
function Visible:show()
print("Showing at " .. self.x .. ", " .. self.y)
end
function Visible:hide()
print("Hidden")
end前と同じヘルパー関数を使って、両方のミックスインをクラスに適用します。
local function applyMixin(class, mixin)
for key, value in pairs(mixin) do
class[key] = value
end
end
local GameObject = {}
GameObject.__index = GameObject
applyMixin(GameObject, Movable)
applyMixin(GameObject, Visible)これで、GameObjectのインスタンスは移動も表示もできるようになりました。
local obj = setmetatable({x = 0, y = 0}, GameObject)
obj:move(5, 3)
obj:show() -- 5, 3 の位置に表示注意点が1つあります。2つのミックスインで同じメソッド名を定義している場合、後から適用された方が先に適用された方を上書きします。予期しない動作を避けるため、ミックスインのメソッド名はそれぞれ異なるものにしてください。
チャレンジ
簡単ミックスインを使って複数の能力を組み合わせる、ゲーム用のCharacterクラスを作りましょう!あなたのキャラクターは敵を攻撃することも、自分を回復することもできます。これらは別々のミックスインから得られる、2つの異なる振る舞いです。
コードを4つのファイルに分けて整理します。
Attacker.lua:{self.name} attacks {targetName} for {self.damage} damage!を出力する:attack(targetName)メソッドを持つミックスインを作成します(オブジェクトにnameフィールドとdamageフィールドがあることを前提とします)。Healer.lua: 指定された量をself.healthに加え、{self.name} heals for {amount}. Health: {newHealth}を出力する:heal(amount)メソッドを持つミックスインを作成します。Character.lua: 3つすべての属性を保存するコンストラクター:new(name, health, damage)を持つCharacterクラスを作成します。AttackerとHealerの両方のミックスインを適用し、すべてのキャラクターが攻撃と回復をできるようにします。{name} - Health: {health}, Damage: {damage}を出力する:status()メソッドも含めます。main.lua: ここでapplyMixinヘルパー関数を記述します。キャラクターの名前、体力、ダメージ、攻撃対象、回復量を入力として読み込みます。Characterを作成してステータスを表示し、対象を攻撃させ、自分を回復させた後、もう一度ステータスを表示します。
入力は5つです。
- キャラクター名(文字列)
- 開始時の体力(数値)
- ダメージ値(数値)
- 攻撃対象の名前(文字列)
- 回復量(数値)
出力には、キャラクターが両方のミックスインの能力を使う様子を表示します。
{name} - Health: {health}, Damage: {damage}
{name} attacks {target} for {damage} damage!
{name} heals for {healAmount}. Health: {newHealth}
{name} - Health: {newHealth}, Damage: {damage}たとえば、入力がWarrior、100、25、Goblin、30の場合、出力は次のようになります。
Warrior - Health: 100, Damage: 25
Warrior attacks Goblin for 25 damage!
Warrior heals for 30. Health: 130
Warrior - Health: 130, Damage: 25各ミックスインは、それぞれ独自のメソッドをCharacterクラスに追加します。キャラクターはどちらのミックスインも継承しません。applyMixin関数を通じて、それらの能力を得るだけです。これが複数のミックスインを組み合わせる力です!
自分で試してみよう
-- メインファイル
-- TODO: applyMixin ヘルパー関数を作成する
-- この関数は mixin テーブルのすべてのメソッドを target テーブルにコピーする
local function applyMixin(target, mixin)
-- TODO: mixin の適用を実装する
end
-- Character.lua から使用できるように applyMixin をグローバルに公開する
_G.applyMixin = applyMixin
local Character = require('Character')
-- 入力を読み取る
local name = io.read()
local health = tonumber(io.read())
local damage = tonumber(io.read())
local targetName = io.read()
local healAmount = tonumber(io.read())
-- TODO: 指定された name、health、damage を持つ Character を作成する
-- TODO: キャラクターの状態を表示する
-- TODO: キャラクターに対象を攻撃させる
-- TODO: キャラクターに自分自身を回復させる
-- TODO: キャラクターの状態を再度表示する
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Luaオンラインコンパイラ