共有と固有
CoddyのLuaジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 35/70。
継承を扱うときは、それぞれのデータがどこにあるのかを理解することが重要です。一部の属性は共有されます。親クラスで定義され、すべての子クラスからアクセスできます。一方、固有の属性は、子クラスまたはインスタンスで個別に作成されます。
次の階層を考えてみましょう。
local Animal = {}
Animal.__index = Animal
Animal.kingdom = "Animalia" -- 共有: クラスに定義される
function Animal:new(name)
local obj = {name = name} -- 固有: 各インスタンスに保存される
setmetatable(obj, Animal)
return obj
endlocal Dog = {}
Dog.__index = Dog
setmetatable(Dog, {__index = Animal})
Dog.sound = "bark" -- すべてのDogで共有
function Dog:new(name, breed)
local obj = Animal.new(Animal, name)
obj.breed = breed -- 固有: 各Dogにはそれぞれ独自のbreedがある
setmetatable(obj, Dog)
return obj
endDog インスタンスを作成すると、一部の値はオブジェクト自体に存在し、ほかの値は検索チェーンを通じて見つかります。
local rex = Dog:new("Rex", "German Shepherd")
-- このインスタンス固有(rex に保存)
print(rex.name) -- "Rex"
print(rex.breed) -- "German Shepherd"-- 共有(ルックアップで見つかる)
print(rex.sound) -- "bark"(Dog から)
print(rex.kingdom) -- "Animalia"(Animal から)重要な違いは、コンストラクターでobjに割り当てられるフィールドは各インスタンス固有である一方、クラステーブルに直接定義されるフィールドは、そのクラスとその子クラスのすべてのインスタンスで共有されることです。
チャレンジ
簡単継承階層を通じてデータがどのように流れるかを、CreatureクラスとDragonクラスのシステムを作りながら見ていきましょう。すべてのインスタンスで共有される属性と、個々のオブジェクト固有の属性を実際に確認できます。
コードを3つのファイルに分けて整理します。
Creature.lua:すべてのクリーチャーで共有されるクラスレベルの属性world = "Fantasy Realm"を持つ親クラスを定義します。クリーチャーの名前をインスタンス属性として保存する:new(name)コンストラクターも含めます。Dragon.lua:Creatureを継承する子クラスを定義します。すべてのドラゴンで共有されるクラスレベルの属性element = "fire"を追加します。:new(name, color)コンストラクターでは、名前について親クラスのコンストラクターを呼び出してから、各ドラゴン固有のインスタンス属性としてcolorを追加します。main.lua:異なる2匹のドラゴンを作成し、その属性を調べて、共有データと固有データの違いを示します。
次の2つの入力が与えられます。
- 1匹目のドラゴンの名前と色をカンマで区切ったもの(例:
Smaug,red) - 2匹目のドラゴンの名前と色をカンマで区切ったもの(例:
Draco,gold)
メインファイルで、それぞれの名前と色を使って両方のドラゴンを作成します。次に、以下の情報をこの順番どおりに出力します。
- 1匹目のドラゴンの
name - 1匹目のドラゴンの
color - 2匹目のドラゴンの
name - 2匹目のドラゴンの
color - 1匹目のドラゴンの
element(Dragonクラスから共有) - 2匹目のドラゴンの
element(Dragonクラスから共有) - 1匹目のドラゴンの
world(Creatureクラスから共有)
たとえば、入力がSmaug,redとDraco,goldの場合、出力は次のようになります。
Smaug
red
Draco
gold
fire
fire
Fantasy Realmnameとcolorはドラゴンごとに異なる(固有のインスタンス属性)一方、elementとworldは両方で同じ(検索チェーンを通じて見つかる共有クラス属性)であることに注目してください。
自分で試してみよう
-- main.lua: ドラゴンを作成し、共有属性と固有属性の違いを示す
local Dragon = require('Dragon')
-- 1匹目のドラゴンの入力を読み取る(名前、色)
local input1 = io.read()
-- 2匹目のドラゴンの入力を読み取る(名前、色)
local input2 = io.read()
-- TODO: 1つ目の入力を解析して名前と色を取り出す
-- ヒント: パターン "([^,]+),([^,]+)" を指定して string.match を使う
-- TODO: 2つ目の入力を解析して名前と色を取り出す
-- TODO: 名前と色を指定して1匹目のドラゴンを作成する
-- TODO: 名前と色を指定して2匹目のドラゴンを作成する
-- TODO: 次の項目を順番に出力する:
-- 1. 1匹目のドラゴンの名前
-- 2. 1匹目のドラゴンの色
-- 3. 2匹目のドラゴンの名前
-- 4. 2匹目のドラゴンの色
-- 5. 1匹目のドラゴンの属性(Dragonクラスから共有)
-- 6. 2匹目のドラゴンの属性(Dragonクラスから共有)
-- 7. 1匹目のドラゴンの世界(Creatureクラスから共有)
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Luaオンラインコンパイラ