[2/15/2013: This tutorial has been re-written and combined into a single guide. Please click here to view the complete OO-Lua tutorial.]
Welcome to the 4th and final lesson of my OO-Lua tutorials (view the first, second, and third). A mashup of all four parts will be available on Corona’s blog as well, in a couple weeks or so.
Inheritance is a feature of OO-programming that can make your code much more concise and meaningful, by sharing properties and functionality between classes. Let’s stick to our cat and mouse game example.
This diagram shows the concept of Class inheritance:
When we create a new cat or mouse in our game, we want it to pick up functions and properties from each of its Parent ClassesDefinition: The 'Parent Class' is the next Class that a particular Class inherits from
Example: Animal.lua is the Parent Class of Cat.lua and Mouse.lua; Object.lua is the Parent Class of Animal.lua. The Object Class would have code that all game objects share, such as show() and hide(). The Animals Class inherits these functions form the Object Class, but also may add move() or jump(), or properties like “lifeCount”. Finally, our Cat and Mouse Classes inherit code from the Animal Class (and thus the Object Class as well), but also may add squeak() and scurry() for the mouse, or pounce() for the cat. Here’s a side-by-side sample code of our Cat Class, next to it’s Super ClassDefinition: A 'Super Class' (synonomous to Ancestor Class) is any Class that is Parent to the current Class on some level, i.e. the Parent Class, Parent-Parent Class, etc.
Example: Animal.lua is a Super Class of Cat.lua, and Object.lua is a Super Class of both Animal.lua and Cat.lua., and Base ClassDefinition: A 'Base Class' is the bottom-most inherited Class.
Example: Object.lua is the Base Class for Animal.lua, Cat.lua, and Mouse.lua..
Hover mouse or click to view code full-sized
The Cat Class is required somewhere in our scene (when it loads), and it in turn requires the Animal class, which requires the Object class (line 4). But the important part is the setmetatable calls immediately following (lines 5 & 6). It essentially creates a new table (including the “Instances” table we’re used to), and attributes all of the Parent Class’s functions and properties to itself. This is similar to the explicit attribution we’ve done when creating New() instances around lines 12-17 (i.e. object.show = self.show), except it will attribute all functions and properties for you (we would not want to do this for New instances, because it would also attribute the Class functions like New and Destroy) . The purpose of line 4 is to allow Animal.lua to use all of Object.lua’s functions by referring to itself (self:show() in Animal.lua uses the code for self:show() in Object.lua).
Note: To re-iterate the purpose of lines 58-60, I defined these functions in the game scene, because I have several Classes (not necessarily children of Object.lua), that need to use these. You could probably even define them as global functions instead of scene functions, but it doesn’t matter too much (see OO-tutorial 2).
Once our Classes have been fully inherited and available for use, the Cat Class creates 3 cats instances (line 63). This will call Cat:New(), which immediately calls Animal:New(), which immediately calls Object:New(). The object instance is passed the cat image name (all objects are a Corona display image in this case), and then it is attributed with the Object instance functions show() and hide(). It is returned back to the Animal Class on line 19, attributed with Animal’s instance functions, and then returned to the Cat Class to do the same. So the path that our instance follows is from the Base Class to the Final ClassDefinition: A 'Final Class' is a Class that cannot be inherited.
Example: Cat.lua and Mouse.lua are Final Classes (bottom-up).
Why do we attribute “show2″ and “show3″? This is my remedy for a slight flaw in LUA. We sometimes want each instance to extend the functionality it’s inherited. If we defined show() again in Animal.lua or Cat.lua, then we would be overwriting the show() function we inherited from Object.lua, every time we create an instance. The easiest solution is to add a number at the end of each function name we know we will inherit, and then check for it at the end of its implementation (line 29 for example). If the Child ClassDefinition: A 'Child Class' is the inheriting Class
Example: Animal.lua is a Child Class of Object.lua; Cat.lua and Mouse.lua are Child Classes of Animal.lua has show2() defined, then it will run immediately after show(). Theoretically, we should always do this for every instance function that is not defined in our Final Class (the Final Class is an exception because we know we will not inherit from it). This also seems different from the way we propagate New(), but it’s actually not! The function names just have to be unique because they are attributed to each instance, instead of each Class.
Using inheritance properly allows code to be re-used and easily maintained (pretty valuable).
That’s the end of the tutorial! If you have any questions feel free to comment and I’ll respond as promptly as I can! I was also asked to provide a working sample of these OO-tutorials, but I’m a bit torn as to wether I should do it or not. I don’t mean for this to sound douchey, but I’ve learned from my tutoring experiences that not everyone is deserving of shortcuts like that, especially if you don’t know why you’re doing what you’re doing. For those pursuing this knowledge for the right reasons, please take the time to experiment, and I’m confident that you will figure it out if you get stuck
Don’t forget to like ArdentKid on facebook or G+ for more updates!