シンプルなミックスイン
CoddyのLuaジャーニー「Object Oriented Programming」セクションの一部 — レッスン 51/70。
コンポジションを使うと、オブジェクトを他のオブジェクトの中に入れることができます。しかし、無関係なクラス間で振る舞いを共有したい場合はどうすればよいでしょうか?
Player と MenuItem は、どちらも :describe() メソッドを必要とするかもしれませんが、それらは親クラスを共有していません。ここでミックスインの出番です。
ミックスインは、単に関数を含むテーブルです。これらの関数をクラスにコピーすることで、クラスに「ミックス」します:
local Printable = {}
function Printable:describe()
print("Object: " .. tostring(self))
end
function Printable:log(message)
print("[LOG] " .. message)
endこのミックスインをクラスに適用するには、ミックスインテーブルからクラステーブルに各関数をコピーします:
local function applyMixin(class, mixin)
for key, value in pairs(mixin) do
class[key] = value
end
end
local Player = {}
Player.__index = Player
applyMixin(Player, Printable) -- これでPlayerは :describe() と :log() を持つようになりますミックスインを適用すると、どのPlayerインスタンスも、それらのメソッドをクラスに直接定義されているかのように呼び出すことができます:
local p = setmetatable({}, Player)
p:log("Player created") -- [LOG] プレイヤーが作成されました継承とは異なり、ミックスインは親子関係を作成しません。単にあるテーブルから別のテーブルへ関数をコピーしているだけです。
これにより、ミックスインは、他に共通点のないクラス間で、ロギング、シリアライズ、記述メソッドなどのユーティリティ動作を共有するのに最適です。
チャレンジ
簡単ミックスインを使用してタグ付けシステムを構築しましょう!任意のクラスに適用できる Taggable ミックスインを作成し、オブジェクトにタグのコレクションを管理する機能を持たせます。これは、アイテムや投稿、またはラベルが必要なあらゆるエンティティを分類するのに役立ちます。
コードは3つのファイルに分けて構成します:
Taggable.lua: 2つのメソッドを含むミックスインテーブルを作成します::addTag(tag)—self.tagsにタグを追加します(テーブルが存在することを前提とします):listTags()— すべてのタグをTags: tag1, tag2, tag3の形式で表示します(空の場合はTags: none)
Article.lua: タイトルを保存し、空のtagsテーブルを初期化するコンストラクタ:new(title)を持つ Article クラスを作成します。Taggable ミックスインを適用して、Article インスタンスにタグ付け機能を持たせます。記事のタイトルを返す:getTitle()メソッドを含めてください。main.lua: ここにapplyMixinヘルパー関数を記述します(Article.lua で require を介して使用します)。次に、記事のタイトルと2つのタグの入力を読み取ります。Article を作成し、両方のタグを追加し、タイトルを表示して、すべてのタグをリスト表示します。
3つの入力を受け取ります:
- 記事のタイトル(文字列)
- 追加する1つ目のタグ(文字列)
- 追加する2つ目のタグ(文字列)
出力には、記事のタイトルとそれに続くタグを表示する必要があります:
Title: {title}
Tags: {tag1}, {tag2}例えば、入力が Lua Basics、programming、tutorial の場合、出力は以下のようになります:
Title: Lua Basics
Tags: programming, tutorial注意:ミックスインメソッドが機能するのは、Article インスタンスがミックスインメソッドの操作対象となる tags テーブルを持っているからです。ミックスインはこのテーブルを作成しません。Article コンストラクタが作成します。ミックスインは共有される振る舞いを提供するだけです!
チートシート
ミックスインは、親子関係を作成することなく、無関係なクラス間で共有できる関数を含むテーブルです。
ミックスインを作成するには、メソッドを持つテーブルを定義します:
local Printable = {}
function Printable:describe()
print("Object: " .. tostring(self))
end
function Printable:log(message)
print("[LOG] " .. message)
endクラスにミックスインを適用するには、その関数をクラスのテーブルにコピーします:
local function applyMixin(class, mixin)
for key, value in pairs(mixin) do
class[key] = value
end
end
local Player = {}
Player.__index = Player
applyMixin(Player, Printable)ミックスインを適用した後、インスタンスはミックスインのメソッドを呼び出すことができます:
local p = setmetatable({}, Player)
p:log("Player created") -- [LOG] Player createdミックスインは、継承関係のないクラス間で、ロギング、シリアル化、タグ付けなどのユーティリティ動作を共有するのに最適です。
自分で試してみよう
-- タグ付けシステムのメインファイル
local Taggable = require('Taggable')
local Article = require('Article')
-- TODO: applyMixin 関数を実装する
-- この関数は、ミックスインテーブルからターゲットテーブルにすべてのメソッドをコピーする必要があります
function applyMixin(target, mixin)
-- TODO: ミックスインをループして、各メソッドをターゲットにコピーする
end
-- 入力を読み込む
local title = io.read()
local tag1 = io.read()
local tag2 = io.read()
-- TODO: 指定されたタイトルで Article を作成する
-- TODO: 両方のタグを記事に追加する
-- TODO: 「Title: {title}」の形式でタイトルを出力する
-- TODO: listTags メソッドを使用してすべてのタグをリスト表示する
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。