Created
December 4, 2011 07:06
-
-
Save paulmoore/1429475 to your computer and use it in GitHub Desktop.
A simple Class implementation in Lua for Object-Oriented Programming.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- Animal.lua | |
-- | |
-- A simple example of a base class usng the classes library. | |
-- | |
-- @author PaulMoore | |
-- | |
-- Copyright (C) 2011 by Strange Ideas Software | |
-- | |
-- Permission is hereby granted, free of charge, to any person obtaining a copy | |
-- of this software and associated documentation files (the "Software"), to deal | |
-- in the Software without restriction, including without limitation the rights | |
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
-- copies of the Software, and to permit persons to whom the Software is | |
-- furnished to do so, subject to the following conditions: | |
-- | |
-- The above copyright notice and this permission notice shall be included in | |
-- all copies or substantial portions of the Software. | |
-- | |
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
-- THE SOFTWARE. | |
-- Import the classes library. | |
local classes = require "classes" | |
-- Create the class, with the default superclass. | |
local Animal = classes.class() | |
-- Two 'public' static class variables. | |
Animal.NOISE_1 = 0 | |
Animal.NOISE_2 = 1 | |
-- A 'private' static class variable. | |
local numAnimals = 0 | |
--- Constructor. | |
-- | |
-- @param name The name of this animal. | |
function Animal:init (name) | |
-- This instance of Animal now has its own name! | |
self.name = name | |
-- There is only one variable 'numAnimals' (it's static), so incrementing it each time we create an Animal gets as a unique id. | |
numAnimals = numAnimals + 1 | |
self.id = numAnimals | |
end | |
--- Makes a noise, specific to whatever animal I am. | |
-- This is an example of an 'abstract' method. | |
-- We want each Animal to be able to make a noise, but we want each subclass of Animal to decide what that noise is. | |
-- | |
-- @param noiseNum This is either 0 or 1, we will say that each Animal should be able to produce 2 noises. | |
function Animal:makeNoise (noiseNum) | |
error("I don't know what to do, I'm a generic Animal. This should be implemented by a subclass of Animal!") | |
end | |
--- This method is available to the Animal class, and all Subclasses of Animal as well. | |
-- | |
-- @return Returns the id of this Animal. | |
function Animal:getAnimalId () | |
return "Animal<"..self.id..">" | |
end | |
--- This is an example of a Class method. We can't access 'self', because it represents the whole Class, not an instance of this Class. | |
-- | |
-- @return Returns the total number of Animals. | |
function Animal.totalAnimals () | |
return numAnimals | |
end | |
-- This is important for module loading, the table returned by the 'require' method is the Animal class we created. | |
return Animal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- Cat.lua | |
-- | |
-- A simple example of Subclassing using the classes library. | |
-- | |
-- @author PaulMoore | |
-- | |
-- Copyright (C) 2011 by Strange Ideas Software | |
-- | |
-- Permission is hereby granted, free of charge, to any person obtaining a copy | |
-- of this software and associated documentation files (the "Software"), to deal | |
-- in the Software without restriction, including without limitation the rights | |
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
-- copies of the Software, and to permit persons to whom the Software is | |
-- furnished to do so, subject to the following conditions: | |
-- | |
-- The above copyright notice and this permission notice shall be included in | |
-- all copies or substantial portions of the Software. | |
-- | |
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
-- THE SOFTWARE. | |
-- Import the classes library. | |
local classes = require "classes" | |
-- Make sure to import the Superclass! | |
local Animal = require "Animal" | |
-- Create the Subclass. | |
local Cat = classes.class(Animal) | |
--- Constructor. In addition to a name, it also takes in a cat breed. | |
-- | |
-- @param breed The breed of the cat. | |
function Cat:init(name, breed) | |
-- Make sure to call the super constructor! | |
self.super:init(name) | |
-- Next, do some custom instantiation. | |
self.breed = breed | |
end | |
--- This is an instance method specific to the Cat class. | |
function Cat:meow () | |
print("meow "..self.name.." meow") | |
end | |
--- This is another example of an instance method, also specific to the Cat class. | |
function Cat:purr () | |
print("purr I'm a "..self.breed.." cat.") | |
end | |
--- This is an example of overriding an instance method. | |
-- When this method is called on a Cat instance, this method is used instead of the one declared in Animal. | |
-- If we didn't override this method, Animal:makeNoise would be called, and generate an error. | |
function Cat:makeNoise (noiseNum) | |
if noiseNum == Animal.NOISE_1 then | |
self:meow() | |
elseif noiseNum == Animal.NOISE_2 then | |
self:purr() | |
else | |
error("Unknown noise type: "..noiseNum.." for Cat with id: "..self.id) | |
end | |
end | |
-- Return the Cat class | |
return Cat |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- classes.lua | |
-- | |
-- The classes library enables simple OOP constructs using prototypes and meta-tables. | |
-- | |
-- @author Paul Moore | |
-- | |
-- Copyright (C) 2011 by Strange Ideas Software | |
-- | |
-- Permission is hereby granted, free of charge, to any person obtaining a copy | |
-- of this software and associated documentation files (the "Software"), to deal | |
-- in the Software without restriction, including without limitation the rights | |
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
-- copies of the Software, and to permit persons to whom the Software is | |
-- furnished to do so, subject to the following conditions: | |
-- | |
-- The above copyright notice and this permission notice shall be included in | |
-- all copies or substantial portions of the Software. | |
-- | |
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
-- THE SOFTWARE. | |
local classes = {} | |
-- Baseclass of all objects. | |
classes.Object = {} | |
classes.Object.class = classes.Object | |
--- Nullary constructor. | |
function classes.Object:init (...) | |
end | |
--- Base alloc method. | |
function classes.Object.alloc (mastertable) | |
return setmetatable({}, {__index = classes.Object, __newindex = mastertable}) | |
end | |
--- Base new method. | |
function classes.Object.new (...) | |
return classes.Object.alloc({}):init(...) | |
end | |
--- Checks if this object is an instance of class. | |
-- @param class The class object to check. | |
-- @return Returns true if this object is an instance of class, false otherwise. | |
function classes.Object:instanceOf (class) | |
-- Recurse up the supertypes until class is found, or until the supertype is not part of the inheritance tree. | |
if self.class == class then | |
return true | |
end | |
if self.super then | |
return self.super:instanceOf(class) | |
end | |
return false | |
end | |
--- Creates a new class. | |
-- @param baseclass The Baseclass of this class, or nil. | |
-- @return A new class reference. | |
function classes.class (baseclass) | |
-- Create the class definition and metatable. | |
local classdef = {} | |
-- Find the super class, either Object or user-defined. | |
baseclass = baseclass or classes.Object | |
-- If this class definition does not know of a function, it will 'look up' to the Baseclass via the __index of the metatable. | |
setmetatable(classdef, {__index = baseclass}) | |
-- All class instances have a reference to the class object. | |
classdef.class = classdef | |
--- Recursivly allocates the inheritance tree of the instance. | |
-- @param mastertable The 'root' of the inheritance tree. | |
-- @return Returns the instance with the allocated inheritance tree. | |
function classdef.alloc (mastertable) | |
-- All class instances have a reference to a superclass object. | |
local instance = {super = baseclass.alloc(mastertable)} | |
-- Any functions this instance does not know of will 'look up' to the superclass definition. | |
setmetatable(instance, {__index = classdef, __newindex = mastertable}) | |
return instance | |
end | |
--- Constructs a new instance from this class definition. | |
-- @param arg Arguments to this class' constructor | |
-- @return Returns a new instance of this class. | |
function classdef.new (...) | |
-- Create the empty object. | |
local instance = {} | |
-- Start the process of creating the inheritance tree. | |
instance.super = baseclass.alloc(instance) | |
setmetatable(instance, {__index = classdef}) | |
-- Finally, init the object, it is up to the programmer to choose to call the super init method. | |
instance:init(...) | |
return instance | |
end | |
-- Finally, return the class we created. | |
return classdef | |
end | |
return classes |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- main.lua | |
-- | |
-- A simple program to test the functionality of the classes library. | |
-- | |
-- @author Paul Moore | |
-- | |
-- Copyright (C) 2011 by Strange Ideas Software | |
-- | |
-- Permission is hereby granted, free of charge, to any person obtaining a copy | |
-- of this software and associated documentation files (the "Software"), to deal | |
-- in the Software without restriction, including without limitation the rights | |
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
-- copies of the Software, and to permit persons to whom the Software is | |
-- furnished to do so, subject to the following conditions: | |
-- | |
-- The above copyright notice and this permission notice shall be included in | |
-- all copies or substantial portions of the Software. | |
-- | |
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
-- THE SOFTWARE. | |
-- Import the classes library. | |
local classes = require "classes" | |
-- Import the Animal class. | |
local Animal = require "Animal" | |
-- Import the Cat class. | |
local Cat = require "Cat" | |
-- Create an instance of the Animal class. | |
local spider = Animal.new("Charlotte") | |
-- Create some instances of the Cat class. | |
local cat1 = Cat.new("Mio", "Orange Tabby") | |
local cat2 = Cat.new("Quill", "Maine Coon") | |
-- As you can see, each instance has its own set of instance variables. | |
-- Also note how the Cat class 'inherits' the Animal:getAnimalId() method from its Superclass, Animal. | |
print("The spider's name is: "..spider.name.." and id: "..spider:getAnimalId()) | |
print("The first cat's name is: "..cat1.name.." and id: "..cat1:getAnimalId()) | |
print("The second cat's name is: "..cat2.name.." and id: "..cat2:getAnimalId()) | |
print() | |
-- This will generate an error! Because spider is an instance of Animal and not Cat, Animal:makeNoise will be used | |
-- instead of Cat:makeNoise, thus producing an error. | |
--spider:makeNoise(Animal.NOISE_1) | |
--spider:makeNoise(Animal.NOISE_2) | |
--print() | |
-- This will not generate an error, because we have overriden the 'makeNoise' method for Cats. | |
cat1:makeNoise(Animal.NOISE_1) | |
cat1:makeNoise(Animal.NOISE_2) | |
print() | |
-- Because cat1 and cat2 are different instances of the Cat Class, we will get different results by calling the method on the other cat. | |
cat2:makeNoise(Animal.NOISE_1) | |
cat2:makeNoise(Animal.NOISE_2) | |
print() | |
-- This will generate an error, because by using '.' instead of ':', we are not giving the Cat instance reference to 'self'. | |
--cat1.makeNoise(Animal.NOISE_1) | |
-- This is a static method, thus it is called with a '.' and not a ':'. | |
print("The total number of Animals created is: "..Animal.totalAnimals()) | |
print() | |
-- Returns 'yes', a Cat is a Cat. | |
print("Is cat1 an instance of the Cat class? "..tostring(cat1:instanceOf(Cat))) | |
-- Returns 'yes', a Cat is an Animal. | |
print("Is cat1 an instance of the Animal class? "..tostring(cat1:instanceOf(Animal))) | |
-- Returns 'true', an Animal is an Animal. | |
print("Is spider an instance of the Animal class? "..tostring(spider:instanceOf(Animal))) | |
-- Returns 'false', an Animal is not a Cat. | |
print("Is spider an instance of the Cat class? "..tostring(spider:instanceOf(Cat))) | |
-- Returns 'true', a Cat is an Object. | |
print("Is cat1 an instance of the root Object class? "..tostring(cat1:instanceOf(classes.Object))) | |
-- Returns 'true', a Spider is also an Object. | |
print("Is spider an instance of the root Object class? "..tostring(spider:instanceOf(classes.Object))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment