Corona / Lua OOP - trying to settle on a method

I have been reading up on the may ways to do OOP with Lua and Corona and some seem very complicated and some to miss the whole point entirely. The tutorial here: http://coronalabs.com/blog/2011/09/29/tutorial-modular-classes-in-corona/whilst good appears limited when discussing Polymorphism and Inheritance. I have cobbled together a very basic test and I was wondering whether people might be able to critique it and point out flaws or improvements?

main.lua

--[[This is to demonstrate creating abstract Classes with the ability to instantiate multiple independent instances. Person is the Super Class and Child is the subclass]]-- local Person = require( "Person" ) -- include the Person class local Child = require( "Child" ) -- include the Person class local myPersonOne = Person:new() -- create an instance of the Person Class local myPersonTwo = Person:new() -- create an instance of the Person Class local myChildOne = Child:new() -- create an instance of the Child Class local myChildTwo = Child:new() -- create an instance of the Child Class -- BEFORE NAMES SET print( "myPersonOne:" ) print( myPersonOne ) print( "myPersonOne.name:" ) print( myPersonOne:getFirstName() ) print( "-----------------------" ) print( "myPersonTwo:" ) print( myPersonTwo ) print( "myPersonTwo.name:" ) print( myPersonTwo:getFirstName() ) print( "-----------------------" ) print( "myChildOne:" ) print( myChildOne ) print( "myChildOne.name:" ) print( myChildOne:getFirstName() ) print( "-----------------------" ) print( "myChildTwo:" ) print( myChildTwo ) print( "myChildTwo.name:" ) print( myChildTwo:getFirstName() ) print( "-----------------------" ) -- SET THE NAMES myPersonOne:setFirstName( "Oliver" ) myPersonTwo:setFirstName( "Dave" ) myChildOne:setFirstName( "Steve" ) myChildTwo:setFirstName( "Mike" ) -- AFTER NAMES SET print( "myPersonOne:" ) print( myPersonOne ) print( "myPersonOne.name:" ) print( myPersonOne:getFirstName() ) print( "-----------------------" ) print( "myPersonTwo:" ) print( myPersonTwo ) print( "myPersonTwo.name:" ) print( myPersonTwo:getFirstName() ) print( "-----------------------" ) print( "myChildOne:" ) print( myChildOne ) print( "myChildOne.name:" ) print( myChildOne:getFirstName() ) print( "-----------------------" ) print( "myChildTwo:" ) print( myChildTwo ) print( "myChildTwo.name:" ) print( myChildTwo:getFirstName() )

Person.lua

local Person = { firstName = nil } ------------------------------------------------- -- CONSTRUCTOR ------------------------------------------------- function Person:new ( \_object ) object = \_object or {} -- create object if user does not provide one setmetatable( object, self ) self.\_\_index = self return object end ------------------------------------------------- -- PRIVATE FUNCTIONS ------------------------------------------------- ------------------------------------------------- -- PUBLIC FUNCTIONS ------------------------------------------------- function Person:setFirstName ( \_firstName ) self.firstName = \_firstName end function Person:getFirstName () return self.firstName end return Person

Child.lua

-- initially make this a new instance of Person local Child = require( "Person" ):new(); ------------------------------------------------- -- CONSTRUCTOR ------------------------------------------------- function Child:new ( \_object ) object = \_object or {} -- create object if user does not provide one setmetatable(object , self ) self.\_\_index = self return object end ------------------------------------------------- -- PRIVATE FUNCTIONS ------------------------------------------------- ------------------------------------------------- -- PUBLIC FUNCTIONS ------------------------------------------------- function Child:setFavouriteToy ( \_favouriteToy ) self.favouriteToy = \_favouriteToy end function Child:getFavouriteToy () return self.favouriteToy end function Child:setFirstName ( \_firstName ) -- this method overwrites Person.setFirstName() self.firstName = \_firstName .. " OVERWRITTEN!" end return Child

Any feedback would be really appreciated. Next I want to look into extending DisplayObjects based on this model and custom events.

Thanks in advance.

1/ google “Jesse Warden” and “Darren Osadchuk”; and read and experiment.  that Lua metatable stuff simply doesn’t work with Corona’s own C-based OO  - ie the thing that makes Corona so awesome.

2/IOW, the OO has already been done, that is the whole point of Corona, IMHO.  but it’s only IMHO so good luck!  

That’s pretty much what I do. I do change it slightly ; I have a class called ‘Base’ which all classes derive from, and it takes a parameter which it passes on to a function initialise() so you can do constructors with parameters.

The downside of your method (and mine) is that you cannot differentiate between null constructors and subclasses. If (say) you want to subclass a class which does something like create a circle in its constructor, then when you subclass it (local child = require(“person”):new()) then it runs the constructor and creates the circle. 

Generally I adopt a policy with active constructors that the null constructor is a subclassing request and if I want to use it to create an instance (I appreciate they are the same but operationally it can be different) I pass in an empty table.

I use this (as a one liner)

\_G.Base = \_G.Base or { new = function(s,...) local o = { } setmetatable(o,s) s.\_\_index = s o:initialise(...) return o end, initialise = function() end }

I also have my own system ‘Executive’ which is under development which manages object instances and has proper destructors and inter object communications https://github.com/autismuk/Executive (and is now extended to provide an FSM based scene system) though it is still under development, albeit it is now fairly stable.

… it just has its own variant display.newText() could have been done as Text:new() It is pretty much the same thing, display is a factory.

I don’t find the Corona API a problem. Most of the things you don’t want to be game objects anyway, really.

i see from your code that you are way more advanced than me, so i include this only in case it is helpful – i found this link useful when i was trying to find my own way about getting modularity into my apps:

http://www.omidahourai.com/from-zero-to-oo-ardentkids-guide-to-object-oriented-lua-with-corona-sdk

in particular, this guy addresses questions i had about messaging. (i see you have already built in messaging of enterFrames.)  

if i want my objects to interact, i do something very crude.  i make sure the objects are in the same display group and then go:

for i = 1, self.parent.numChildren do

  self.parent[i]:dispatchEvent( myEvent ) 

end

–or you can build the ID of the intended recipient as a param of myEvent and only message that target, if self.parent[i].ID = “whatever” then…

it’s blunt but it works for me.  thanks for sharing your code: a really good steer on how to take my own modularity to the next level.  

There’s messaging and messaging :slight_smile:

There’s a viewpoint that all OOP methods are really messages - this goes back to smalltalk. So when you write someObject:doSomething(withThis) you are actually sending a message to it. Then there is the messaging in executive, asynchronous from a queue, like in Windows. Then there is Coronas, the observer pattern, which I personally don’t like because it puts the responsibility for tidying listeners up on the listener. But it is more efficient than a message queue, but not as efficient as a direct message.

Your idea (the display group) is actually at heart very similar to mine, it’s just different. This is about the sixth go I have had at it in about three weeks. The first ones were component/entity systems which I found too convoluted, this was a sort of compromise between that and a classical OOP system and practical reality.

Or you could try using an open-source oop library like this one:
http://forums.coronalabs.com/topic/40640-luoop-lua-oop-library/

Features:

  • Object oriented
  • Constructors / destructors
  • Multiple inheritance
  • Method Overloading
  • newInstance() method, to get a new instance of an existing object
  • Ad-hoc constructor / destructor calling, allowing to pass specific parameters to each superclass constructor / destructor
  • Superclass method calling
  • Embedded Singleton support

Right off the bat :wink:

(Disclaimer: I created it !)
 

1/ google “Jesse Warden” and “Darren Osadchuk”; and read and experiment.  that Lua metatable stuff simply doesn’t work with Corona’s own C-based OO  - ie the thing that makes Corona so awesome.

2/IOW, the OO has already been done, that is the whole point of Corona, IMHO.  but it’s only IMHO so good luck!  

That’s pretty much what I do. I do change it slightly ; I have a class called ‘Base’ which all classes derive from, and it takes a parameter which it passes on to a function initialise() so you can do constructors with parameters.

The downside of your method (and mine) is that you cannot differentiate between null constructors and subclasses. If (say) you want to subclass a class which does something like create a circle in its constructor, then when you subclass it (local child = require(“person”):new()) then it runs the constructor and creates the circle. 

Generally I adopt a policy with active constructors that the null constructor is a subclassing request and if I want to use it to create an instance (I appreciate they are the same but operationally it can be different) I pass in an empty table.

I use this (as a one liner)

\_G.Base = \_G.Base or { new = function(s,...) local o = { } setmetatable(o,s) s.\_\_index = s o:initialise(...) return o end, initialise = function() end }

I also have my own system ‘Executive’ which is under development which manages object instances and has proper destructors and inter object communications https://github.com/autismuk/Executive (and is now extended to provide an FSM based scene system) though it is still under development, albeit it is now fairly stable.

… it just has its own variant display.newText() could have been done as Text:new() It is pretty much the same thing, display is a factory.

I don’t find the Corona API a problem. Most of the things you don’t want to be game objects anyway, really.

i see from your code that you are way more advanced than me, so i include this only in case it is helpful – i found this link useful when i was trying to find my own way about getting modularity into my apps:

http://www.omidahourai.com/from-zero-to-oo-ardentkids-guide-to-object-oriented-lua-with-corona-sdk

in particular, this guy addresses questions i had about messaging. (i see you have already built in messaging of enterFrames.)  

if i want my objects to interact, i do something very crude.  i make sure the objects are in the same display group and then go:

for i = 1, self.parent.numChildren do

  self.parent[i]:dispatchEvent( myEvent ) 

end

–or you can build the ID of the intended recipient as a param of myEvent and only message that target, if self.parent[i].ID = “whatever” then…

it’s blunt but it works for me.  thanks for sharing your code: a really good steer on how to take my own modularity to the next level.  

There’s messaging and messaging :slight_smile:

There’s a viewpoint that all OOP methods are really messages - this goes back to smalltalk. So when you write someObject:doSomething(withThis) you are actually sending a message to it. Then there is the messaging in executive, asynchronous from a queue, like in Windows. Then there is Coronas, the observer pattern, which I personally don’t like because it puts the responsibility for tidying listeners up on the listener. But it is more efficient than a message queue, but not as efficient as a direct message.

Your idea (the display group) is actually at heart very similar to mine, it’s just different. This is about the sixth go I have had at it in about three weeks. The first ones were component/entity systems which I found too convoluted, this was a sort of compromise between that and a classical OOP system and practical reality.

Or you could try using an open-source oop library like this one:
http://forums.coronalabs.com/topic/40640-luoop-lua-oop-library/

Features:

  • Object oriented
  • Constructors / destructors
  • Multiple inheritance
  • Method Overloading
  • newInstance() method, to get a new instance of an existing object
  • Ad-hoc constructor / destructor calling, allowing to pass specific parameters to each superclass constructor / destructor
  • Superclass method calling
  • Embedded Singleton support

Right off the bat :wink:

(Disclaimer: I created it !)