複数のミックスインの適用
CoddyのLuaジャーニー「Object Oriented Programming」セクションの一部 — レッスン 52/70。
1つのミックスインは、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 で表示注意点:2つのミックスインが同じメソッド名を定義している場合、後から適用された方が最初の方を上書きします。予期しない動作を避けるために、ミックスインのメソッド名は重複しないようにしてください。
チャレンジ
簡単ミックスインを使用して、複数の機能を組み合わせたゲーム用の Character クラスを作成しましょう!あなたのキャラクターは、敵を攻撃することと自分自身を回復することの両方ができるようになります。これらは、別々のミックスインから提供される2つの異なる振る舞いです。
コードを4つのファイルに分けて構成します:
Attacker.lua::attack(targetName)メソッドを持つミックスインを作成します。このメソッドは{self.name} attacks {targetName} for {self.damage} damage!と出力します(オブジェクトがnameとdamageフィールドを持っていることを前提とします)。Healer.lua::heal(amount)メソッドを持つミックスインを作成します。このメソッドはself.healthに量を加え、{self.name} heals for {amount}. Health: {newHealth}と出力します。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 関数を通じてそれらの能力を獲得します。これが複数のミックスインを組み合わせる力です!
チートシート
複数のミックスインを単一のクラスに適用して、異なる動作のセットを組み合わせることができます。各ミックスインは、クラスに独自のメソッドを提供します。
異なる関心事に対して個別のミックスインを定義します:
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")
endapplyMixinヘルパー関数を使用して、複数のミックスインをクラスに適用します:
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)これで、クラスは適用されたすべてのミックスインのメソッドを持つようになります:
local obj = setmetatable({x = 0, y = 0}, GameObject)
obj:move(5, 3)
obj:show() -- Showing at 5, 3重要: 2つのミックスインが同じメソッド名を定義している場合、2番目に適用されたミックスインが最初のものを上書きします。競合を避けるために、ミックスイン間でメソッド名を一意に保つようにしてください。
自分で試してみよう
-- メインファイル
-- TODO: applyMixinヘルパー関数を作成する
-- この関数は、mixinテーブルからターゲットテーブルにすべてのメソッドをコピーする必要があります
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: 指定された名前、体力、攻撃力でCharacterを作成する
-- TODO: キャラクターのステータスを表示する
-- TODO: キャラクターにターゲットを攻撃させる
-- TODO: キャラクターに自身を回復させる
-- TODO: 再度キャラクターのステータスを表示する
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。