Reputation: 1148
I've been reading the Lua textbook about inheritance but I'm not quite understanding how to implement it. Say I have these two classes:
Hero = {}
function Hero.new(n)
local self = {name = n, health = 100}
local dealDamage = function(self)
return 10
end
local takeDamage = function(self, h)
self.health = self.health - h
end
local getHealth = function(self, h)
self.health = self.health + h
end
local getName = function(self)
return self.name
end
local isDead = function(self)
if self.health <= 0 then
return true
end
return false
end
local __tostring = function(self)
return "Hero[Name: "..self.name..", health: "..self.health.."]"
end
local mt = {
__tostring = __tostring
}
return setmetatable({
name = self.name,
health = self.health,
dealDamage = dealDamage,
takeDamage = takeDamage,
getHealth = getHealth,
getName = getName,
isDead = isDead
}, mt)
end
return Hero
And
heroClass = require "hero"
Fighter = {}
function Fighter.new(hero)
local self = {strength = 3}
local beserk = function(health)
damage = heath * 0.33
health = health - (health * 0.25
return damage
end
local __tostring = function(self)
return "Fighter[Name: "..name..", health: "..health.."]"
end
local mt = {
__tostring = __tostring
}
return setmetatable({
strength = self.strength,
}, mt)
end
return Fighter
What I think I'm doing wrong is how the classes are structured with the metatables. Using examples like this or a couple of other SO questions I can see that our classes are very different.
How do you properly set up inheritance where the Fighter class would inherit the function and data fields of the Hero class?
Upvotes: 1
Views: 303
Reputation: 5544
There are many different ways of defining classes in Lua. Instead of choosing some OOP system as the "proper" way, let's look at what you have and try to define how your system should work.
Your Hero
class defines a constructor which returns an object where all methods and instance variables are included directly on that object. This is a pretty simple concept. The only major change I would make is to remove the self
variable from the top, because you don't really use it:
function Hero.new(name)
local dealDamage = function(self)
return 10
end
local takeDamage = function(self, h)
self.health = self.health - h
end
local getHealth = function(self, h)
self.health = self.health + h
end
local getName = function(self)
return self.name
end
local isDead = function(self)
return self.health <= 0
end
local __tostring = function(self)
return "Hero[Name: "..self.name..", health: "..self.health.."]"
end
local mt = {
__tostring = __tostring
}
return setmetatable({
name = name,
health = 100,
dealDamage = dealDamage,
takeDamage = takeDamage,
getHealth = getHealth,
getName = getName,
isDead = isDead
}, mt)
end
For your Fighter
class, we can create a base instance from Hero
, and inherit from it using __index
. (I deleted the berserk
function, because it's unused. Feel free to adapt it to this code.):
-- We need the same parameter as Hero.
function Fighter.new(name)
-- Create a base object by instantiating Hero.
local hero = heroClass.new(name)
local __tostring = function(self)
return "Fighter[Name: "..self.name..", health: "..self.health.."]"
end
local mt = {
__tostring = __tostring,
-- Inherit from the base object.
__index = hero,
}
return setmetatable({
strength = 3,
}, mt)
end
The advantages of this system are:
Less indirection than most systems. You can just look at the object for methods instead of looking up a class table.
Inheritance is flexible. You can change how it works by modifying the constructor. You could copy methods instead of using __index
, or you could implement multiple inheritance, right there in your constructor!
I leave the disadvantages of this system as an exercise to the reader.
Upvotes: 2