Page 3 of 3

Re: Object inheritance

Posted: Sat Jul 13, 2019 3:22 pm
by zorg
Yeah, i usually prefer to completely separate classes and instances (whether with or without any kind of inheritance is in play), to me it feels more clean, and i don't need to think about mixing both behaviours into one table, but ultimately, it's just a matter of preference.

Re: Object inheritance

Posted: Sat Jul 13, 2019 4:48 pm
by ivan
Oh I think I finally understand. Here is a clearer view of the code:

Code: Select all

A = {}
function A:new()
  local o = {}
  setmetatable(o, self)
  self.__index = self
  return o
end
function A:foo()
  print('A')
end
B = A:new()
function B:foo()
  print('B')
end
C = B:new()
function C:foo()
  print('C')
end

a = A:new()
b = B:new()
c = C:new()
a:foo()
b:foo()
c:foo()
It does work but it's just not very clear IMO.
My issue is with the merging of metatables and interfaces or "classes" into a single table.
This results in weird stuff like "self.__index = self" which is executed everytime you call "new".
In theory this line doesn't need to be executed every time you create an "instance".
I'm not crazy with modifying the metatable/interface after an object is created.
It could lead to weird bugs like:

Code: Select all

function A:new()
  local o = {}
  setmetatable(o, self)
  self.__index = self
  o:baz() -- won't work until B:baz() is defined
  return o
end
B = A:new()
function B:baz()
  print('B')
end

Re: Object inheritance

Posted: Sat Jul 13, 2019 5:29 pm
by TheHUG
Yes that's true about self.__index, you could set A.__index = A, and then do the same whenever you define a new class, or if you prefer to keep the metatable and the superclass separate you could define local metaA = {__index=A} and set that to be the metatable of subclasses. It's an additional thing to remember to do, and a little bit of code duplication, while the efficiency savings very small unless you spawn multiple instances per frame. I suppose you could define a function to initialize a table, set its index to the superclass you want, and define its __index attribute (or the separate metatable). I might give that a try next time.

That's a good point, if you want something in initialization to depend on something implemented by the subclass you can't do it cleanly like this. The webpage I initially got this from actually did this:

Code: Select all

function A:new(o)
    local o = o or {}
    setmetatable(o, self)
    self.__index = self
    -- more code e.g
    o:baz()
    return o
end
I didn't see why you may want to provide o at first, but now I can see why it might be helpful, you can do:

so that you can do

Code: Select all

B = {}
function B:baz()
  print('B')
end
A:new(B)
hmm... though now I write it out I don't like the look of it. I'll play around with that more if I run into a situation where I want some part of initialization to depend on a method of the subclass.

edit:

Code: Select all

B = A:new({
  baz=function(self)
     ...
   end})
looks alright to me

Re: Object inheritance

Posted: Sat Jul 13, 2019 5:50 pm
by ivan
The most confusing part is that you're doing multiple things through the :new() function.
I would split that into two functions: :new() and :inherit() then the code would look much more clear.

Re: Object inheritance

Posted: Sat Jul 13, 2019 6:22 pm
by TheHUG
That's a pretty good idea

Re: Object inheritance

Posted: Sun Jul 14, 2019 11:32 am
by pgimeno
That's indeed the approach that 4vZEROv posted in the previous page (except 'inherit()' is called 'extend()' in 4vZEROv's version).

Re: Object inheritance

Posted: Sun Jul 14, 2019 9:38 pm
by raidho36
In my class implementation, inheritance workflow is set up like this:

Code: Select all

local baseclass = Class ( "baseClassName" )
function baseclass:foo ( ) ...
local derivedclass = baseclass:extend ( "derivedClassName" )
function derivedclass:bar ( ) ...
Internally, it copies over all data from base class into derived class. Not having to do multiple __index hops to find inherited functions and values makes it run a lot faster, and the memory footprint increase is microscopic (it's only the class definition, not individual instances). However, this also means that it won't pick up changes made to the base class after the inheritance call, so one should only inherit from complete classes.