-
Notifications
You must be signed in to change notification settings - Fork 1
Basic Usage
-- Base class creation
local Animal = LowerClass("Animal")
-- Adding functions
function Animal:__init(sound)
self.sound = sound
end
function Animal:makeSound()
print("Animal goes '" .. self.sound .. "'")
end
-- Creating an object
dog = Animal:new("Bark!")
dog:makeSound()
Takes in a single argument and return the type of the passed argument.
local Animal = LowerClass("Animal")
local dog = Animal()
-- If a class is passed in, always returns "Class"
print(LowerClass.type(Animal)) -- "Class"
-- If an instance is passed in, returns the name of the class
print(LowerClass.type(dog)) -- "Animal"
-- If anything else is passed in, returns Lua's type()
print(LowerClass.type(5)) -- "number"
-- Single Inheritance
local Dog = LowerClass("Dog", Animal)
-- Multi Inheritance
local FurBaby = LowerClass("FurBaby")
local Dog = LowerClass("Dog", Animal, FurBaby)
-- "Super" does not exist in LowerClass, here is how you would imitate it though:
function Dog:__init()
Animal.__init(self, "Bark!")
end
Checks if an object/class inherits a class
local Dog = LowerClass("Dog", Animal)
print(Dog:is(Animal)) -- True
print(Animal:is(Dog)) -- False
local myDog = Dog()
print(myDog:is(Animal)) -- true
Unlike MiddleClass, LowerClass does not support the idea of "static" methods. Simply put when you define a function for a class, it is immediately accessible by both the class, inheriting classes, and any objects inheriting this class.
local Dog = LowerClass("Dog")
function Dog:walk()
if type(self) == "Dog" then
print("An instance of Dog is walking!")
else
print("The class 'Dog' has called walk')
end
end
Dog:walk() -- The class 'Dog' has called walk
Dog.walk() -- The class 'Dog' has called walk
local myDog = Dog:new()
myDog.walk() -- The class 'Dog' has called walk
myDog:walk() -- An instance of Dog is walking!
Mixins are simply tables that will have all their functions added to an object.
-- Simple mixin declaration
local WalkMixin = {
walk = function(self)
print("This object is walking")
end
}
-- Adding the mixin
local Dog = LowerClass("Dog", WalkMixin)
-- Using the mixin
Dog:walk()
-- or
local myDog = Dog()
myDog:walk()
-- Mixins can be a function, the only arg passed to them will be the object mixing in.
-- The return value does have to be a proper table mixin though.
local function WalkMixin(cls)
if cls.name == "Dog" then
return {
walk = function()
print("A dog is walking")
end
}
else
return {
walk = function()
print("Something is walking")
end
}
end
end
local Dog = LowerClass("Dog", WalkMixin)
Include is the foundation for Mixins and Inheritance
For example: when you call LowerClass(, ...), all args outside of name are simply passed to Include.
-- Class Include() can include both other classes and "mixins"
local FourLegged = LowerClass("FourLegged")
Dog:include(FourLegged)
local WalkMixin = {
walk = function(self)
print("I am walking!")
end
}
Dog:include(WalkMixin)
-- Object Include() can only include "mixins"
local myDog = Dog()
myDog:include(WalkMixin)
The "most complex" part of LowerClass (and MiddleClass as well) is how variables are propegated.
To best understand how they are propegated, it is easiest to look at an example:
local ClassA = LowerClass("ClassA")
local ClassB = LowerClass("ClassB", ClassA)
local objA = ClassA()
local objB = ClassB()
-- prints the value of .val for ClassA, ClassB, objA, and objB
local function printer()
print("CA=" .. tostring(ClassA.val) .. ", "
.. "CB=" .. tostring(ClassB.val) .. ", "
.. "OA=" .. tostring(objA.val) .. ", "
.. "OB=" .. tostring(objB.val))
end
-- Val hasn't been set anywhere, so all values will be nil
printer() -- CA=nil, CB=nil, OA=nil, OB=nil
-- When we set ClassA.val to 1, it will propegate to ClassB and all sub objects
ClassA.val = 1
printer() -- CA=1, CB=1, OA=1, OB=1
-- Resetting the val back to nil, will again propegate it all the way through
ClassA.val = nil
printer() -- CA=nil, CB=nil, OA=nil, OB=nil
-- Setting ClassB.val will only propegate it to classes and objects that inherit from ClassB
ClassB.val = 4
printer() -- CA=nil, CB=1, OA=nil, OB=1
-- Setting objB will only apply to objB as it's not a class.
objB.val = 10
printer() -- CA=nil, CB=1, OA=nil, OB=10
-- Setting ClassB back to nil, will ensure any objects inheriting val from it will also go back to nil
-- But objB.val is already set to 10, so it inherits it properly
ClassB.val = nil
printer() -- CA=nil, CB=nil, OA=nil, OB=10
-- Here we remove val from objB, meaing objB will revert to inheriting from ClassB
-- Then we set ClassA and ClassB to their own unique values, and we can see that their
-- objects inherit from them.
objB.val = nil
ClassA.val = 10
ClassB.val = 4
printer() -- CA=10, CB=4, OA=10, OB=4
-- Due to this behavior, we can actually do a really cool thing where we setup a class to have
-- val as a function, that will set val for us.
ClassA.val = function(self, val)
self.val = val
end
ClassB.val = nil
objB:val(5)
printer() -- CA=<function>, CB=<function>, OA=<function>, OB=5