Menu
CoddyTech

複数のミックスインの適用

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つのミックスインで同じメソッド名を定義している場合、後から適用された方が先に適用された方を上書きします。予期しない動作を避けるため、ミックスインのメソッド名はそれぞれ異なるものにしてください。

challenge icon

チャレンジ

簡単

ミックスインを使って複数の能力を組み合わせる、ゲーム用の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つです。

  1. キャラクター名(文字列)
  2. 開始時の体力(数値)
  3. ダメージ値(数値)
  4. 攻撃対象の名前(文字列)
  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: キャラクターの状態を再度表示する
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Luaオンラインコンパイラ