親メソッドの呼び出し
CoddyのLuaジャーニー「Object Oriented Programming」セクションの一部 — レッスン 38/70。
メソッドを完全にオーバーライドすることが、常に望ましいとは限りません。代わりに、親の振る舞いを拡張したい場合があります。つまり、親が行うことを実行した上で、さらに何かを追加するということです。これを実現するには、子のオーバーライド内から親メソッドを直接呼び出すことができます。
この手法では、親クラス名とともにドット構文を使用し、最初の引数として self を明示的に渡します。
local Animal = {}
Animal.__index = Animal
function Animal:new(name)
local obj = {name = name}
setmetatable(obj, Animal)
return obj
end
function Animal:speak()
print(self.name .. " makes a sound")
end
-- Dogは親の振る舞いを拡張します
local Dog = {}
Dog.__index = Dog
setmetatable(Dog, {__index = Animal})
function Dog:new(name)
local obj = Animal.new(Animal, name)
setmetatable(obj, Dog)
return obj
end
function Dog:speak()
Animal.speak(self) -- 最初に親のメソッドを呼び出します
print(self.name .. " wags tail happily")
end重要な行は Animal.speak(self) です。親クラスでドット構文を使用し、手動で self を渡すことで、独自の動作を追加する前に元のメソッドを呼び出します:
local buddy = Dog:new("Buddy")
buddy:speak()
-- 出力:
-- Buddy makes a sound
-- Buddy wags tail happilyこのパターンは、親メソッドが重複させたくない重要な処理を行っている場合に便利です。子は親のロジックを最初、最後、あるいは自身のコードの途中で実行することができ、振る舞いをどのように組み合わせるかを完全に制御できます。
チャレンジ
簡単FormalPerson が通常の Person の振る舞いを拡張する挨拶システムを構築しましょう!完全に自己紹介の方法を置き換えるのではなく、フォーマル版ではまず標準的な自己紹介を行い、その後に丁寧な動作を追加します。
コードは以下の3つのファイルに分けて構成します:
Person.lua: 名前を保存する:new(name)コンストラクタを持つベースクラスです。名前の後にsays hiと出力する:introduce()メソッドを含めてください。これが拡張されるベースの振る舞いになります。FormalPerson.lua: Person を継承する子クラスです。そのコンストラクタは、名前を処理するために親のコンストラクタを呼び出す必要があります。:introduce()メソッドをオーバーライドしますが、親の振る舞いを完全に置き換えるのではなく、Person.introduce(self)を使用して最初に親のメソッドを呼び出し、その後に名前とbows politelyを続けて出力する行を追加してください。main.lua: FormalPerson モジュールを読み込み(require)、入力から名前を読み取り、FormalPerson のインスタンスを作成して、:introduce()を呼び出して両方の振る舞いが組み合わされていることを確認します。
入力として、その人の名前を1つ受け取ります。
FormalPerson に対して :introduce() を呼び出すと、親のメソッドからの1行目と、子の追加分からの2行目の、計2行の出力が生成されるはずです:
{name} says hi
{name} bows politely例えば、入力が Eleanor の場合、出力は以下のようになります:
Eleanor says hi
Eleanor bows politelyここでの重要なポイントは、FormalPerson が "says hi" のロジックを重複して持たないことです。その処理を親メソッドに委譲し、その上に独自の振る舞いを追加しています。これによりコードがクリーンに保たれ、子クラスが親の機能を置き換えるのではなく、どのように拡張できるかが示されます。
チートシート
親メソッドの振る舞いを完全に上書きするのではなく拡張するには、ドット構文を使用して self を明示的に渡すことで、子クラスの上書きメソッド内から親メソッドを呼び出します。
function ChildClass:method()
ParentClass.method(self) -- 親メソッドを呼び出す
-- 追加の振る舞いを加える
endメソッドの拡張を示す完全な例:
local Animal = {}
Animal.__index = Animal
function Animal:new(name)
local obj = {name = name}
setmetatable(obj, Animal)
return obj
end
function Animal:speak()
print(self.name .. " makes a sound")
end
local Dog = {}
Dog.__index = Dog
setmetatable(Dog, {__index = Animal})
function Dog:new(name)
local obj = Animal.new(Animal, name)
setmetatable(obj, Dog)
return obj
end
function Dog:speak()
Animal.speak(self) -- 最初に親の振る舞いを実行する
print(self.name .. " wags tail happily") -- 子の振る舞いを追加する
end
local buddy = Dog:new("Buddy")
buddy:speak()
-- 出力:
-- Buddy makes a sound
-- Buddy wags tail happily親メソッドは、子のロジックの前、後、あるいは途中のどの時点でも呼び出すことができ、振る舞いをどのように組み合わせるかを完全に制御できます。
自分で試してみよう
-- main.lua: 挨拶システムの実行エントリポイント
local FormalPerson = require('FormalPerson')
-- 入力から人の名前を読み込む
local name = io.read()
-- TODO: 指定された名前でFormalPersonインスタンスを作成する
-- TODO: FormalPersonインスタンスのintroduceメソッドを呼び出す
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。