Menu
Coddy logo textTech

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

CoddyのLuaジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 52/70。

1つのmixinは、クラスに1組の振る舞いを追加します。しかし、実際のオブジェクトは複数のソースから機能を必要とすることがよくあります。ゲームオブジェクトには、動き回ることと画面上に表示されることが必要な場合があります。それは、同じmixinに属さない2つの別々の関心事です。

解決策は単純です。同じ class に複数の mixin を apply します。それぞれの mixin が独自の methods を提供し、最終的に class はそれらすべてを持つことになります。

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

前に使った同じヘルパー関数を使用して、両方の mixin をクラスに Apply します。

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つの mixin が同じ method 名を定義している場合、後から適用された方が先の方を上書きします。予期しない動作を避けるため、mixin の method は重複しないようにしてください。

challenge icon

チャレンジ

簡単

複数の機能を mixin で組み合わせるゲーム用の Character class を作ってみましょう!この character は敵を攻撃することも、自分を回復することもできます。それぞれ別の mixin に由来する、2 つの異なる振る舞いです。

コードを 4 つのファイルに分けて整理します。

  • Attacker.lua:attack(targetName) method を持つ mixin を Create します。この method は {self.name} attacks {targetName} for {self.damage} damage! を出力します(オブジェクトが namedamage fields を has いることを Assumes します)。
  • Healer.lua:heal(amount) method を持つ mixin を Create します。この method は amount を self.health に加え、{self.name} heals for {amount}. Health: {newHealth} を出力します。
  • Character.lua:3 つすべての attributes を保存する constructor :new(name, health, damage) を持つ Character class を Create します。Attacker と Healer の両方の mixin を Apply し、すべての character が attack と heal をできるようにします。:status() method も含め、この method は {name} - Health: {health}, Damage: {damage} を出力します。
  • main.lua:ここで applyMixin helper function を記述します。character の name、health、damage、attack する target、そして heal amount の inputs を読み取ります。Character を Create し、その status を表示し、target を attack させ、自分自身を heal させ、その後もう一度 status を表示します。

5 つの inputs を受け取ります。

  1. Character name(string)
  2. 開始時の health(number)
  3. Damage value(number)
  4. attack する target name(string)
  5. heal する amount(number)

出力には、character が両方の mixin abilities を使用していることが示されます。

{name} - Health: {health}, Damage: {damage}
{name} attacks {target} for {damage} damage!
{name} heals for {healAmount}. Health: {newHealth}
{name} - Health: {newHealth}, Damage: {damage}

たとえば、inputs が Warrior10025Goblin30 の場合、出力は次のようになります。

Warrior - Health: 100, Damage: 25
Warrior attacks Goblin for 25 damage!
Warrior heals for 30. Health: 130
Warrior - Health: 130, Damage: 25

各 mixin は独自の method を Character class に提供します。character はどちらの mixin からも inherit しません。単に applyMixin function を通じて、それらの abilities を得ます。これが複数の mixin を組み合わせる力です!

自分で試してみよう

-- メインファイル

-- 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オンラインコンパイラ