AceOO-2.0 Tutorial

From WowAce Wiki
Jump to: navigation, search

Note: In the following examples, I use the function print, which does not exist in WoW. You can take print to be

function print(text)

Starting out

In order to access AceOO-2.0, you have to put the AceOO-2.0 library in your project and add the following line to your TOC:


Then in your lua file, you have to add:

local AceOO = AceLibrary("AceOO-2.0")

which gives you a proper local reference to AceOO-2.0.

Declaring a new class

local Chicken = AceOO.Class()

This creates an Chicken class, which derives from Class. It's blank and has no extra methods.

Getting an instance of a class

local kingRooster = Chicken:new()

The king rooster is now an instantiated form of a Chicken. If I were to call :new() multiple times, each instance would be a different chicken. (Not a different type of chicken, but just another chicken, be it a hen or a rooster.)

Adding :ToString()

It's a good idea to be able to print out what your class is if you have a reference of it. So below your class declaration, put

function Chicken:ToString()
    return "Chicken"

So now this happens:

print(Chicken) -- "Chicken"
print(kingRooster) -- "<Chicken instance>"

object:ToString() will be called whenever tostring(object) is called

Adding methods

Let's make a cat that you can do two things with: pet or spray with water. (Cats don't like to be sprayed)

local AceOO = AceLibrary("AceOO-2.0")

local Cat = AceOO.Class()

Cat.prototype.happiness = "Neutral"

function Cat.prototype:Pet()
    if self.happiness == "Unhappy" then
        self.happiness = "Neutral"
        self.happiness = "Happy"

function Cat.prototype:SprayWithWater()
    if self.happiness == "Happy" then
        self.happiness = "Neutral"
        self.happiness = "Unhappy"

local reginald = Cat:new() -- one cat
local fluffers = Cat:new() -- another cat

-- reginald is cute. fluffers was bad.
print(string.format("%s - %s", reginald.happiness, fluffers.happiness)) -- "Happy - Unhappy"

Despite the silly example, it makes a good point. They are two different cats, with two different states completely independant of each other.

You may be wondering about the .prototype bit. All instances inherit from that, not from the class itself. This will be explained in detail later.

print(AceOO.inherits(reginald, Cat)) -- true
print(AceOO.inherits(fluffers, Cat)) -- true
print(reginald == fluffers) -- false

Both reginald and fluffers are both cats, but they aren't the same cat.


Classes can inherit from other classes, currently they have all inherited from the standard Class. Here, I will show you how to have one class inherit from a user-defined class.

local AceOO = AceLibrary("AceOO-2.0")

local Dog = AceOO.Class()
Dog.virtual = true -- this means that it cannot be instantiated. (cannot call :new())

function Dog.prototype:Bark()
    -- cause an error if the subclass doesn't define the method.
    error("`Bark' not implemented", 2)

local Chihuahua = AceOO.Class(Dog) -- new class, inherits from Dog.

function Chihuahua.prototype:Bark()
    return "Arf! Arf! Arf!"

local Bulldog = AceOO.Class(Dog) -- new class, inherits from Dog.

function Bulldog.prototype:Bark()
   return "Wuff! *slobber*"

local Sheepdog = AceOO.Class(Dog) -- new class, inherits from Dog.

function Sheepdog.prototype:Bark()
    return "Ruff."

local killer = Chihuahua:new()
local muffin = Bulldog:new()
local leopold = Sheepdog:new()

print(killer:Bark()) -- "Arf! Arf! Arf!"
print(muffin:Bark()) -- "Wuff! *slobber*"
print(leopold:Bark()) -- "Ruff."

They're all dogs, but they all bark differently, because they are different types of dogs.


local AceOO = AceLibrary("AceOO-2.0")

local Pig = AceOO.Class()

function Pig.prototype:init(name)
    Pig.super.prototype.init(self) -- very important. Will fail without this. = name

function Pig.prototype:tostring()

local bacon = Pig:new("Bacon") -- new pig named Bacon

print(bacon) -- should print "Bacon", cause that's its name.

the :init(...) method is the constructor for AceOO-2.0. In it, the first line _must_ call the superclass's init method, feel free to pass arguments if they are appropriate. The superclass's constructor is not called automatically in case you want to pass arguments.

The constructor is called whenever a class is instantiated. (:new(...) is called). All arguments to :new(...) are passed on to :init(...)

Constructors with inheritance

local AceOO = AceLibrary("AceOO-2.0")
local Ape = AceOO.Class()
function Ape:ToString()
    return "Ape"
function Ape.prototype:init(name)
    Ape.super.prototype.init(self) = name
function Ape.prototype:ToString()
    return ("<%s - %s>"):format(self.class:ToString(),

local Monkey = AceOO.Class(Ape)
function Monkey:ToString()
    return "Monkey"
function Monkey.prototype:init(name, suit)
    Monkey.super.prototype.init(self, name)
    self.suit = suit
function Ape.prototype:ToString()
    return ("<%s - %s, wearing a %s suit>"):format(self.class:ToString(),, self.suit)

Slappy = Monkey:new("Slappy", "Pirate")
print(Slappy) -- "<Monkey - Slappy, wearing a Pirate suit>"

Arguments can be passed to super constructors without fault.

Static vs. Instance methods

Static methods are specific to a class and subclasses do not inherit them. Contrast to instance methods, which are for a class's instances, and subclasses inherit from them.

local AceOO = AceLibrary("AceOO-2.0")

local Box = AceOO.Class()
Box.numBoxes = 0

function Box:GetNumBoxes()
    return self.numBoxes

function Box.prototype:init()
    Box.numBoxes = Box.numBoxes   1

function Box.prototype:Shake()
    print("You hear something crunch")

local blueBox = Box:new()
local redBox = Box:new()
print(Box:GetNumBoxes()) -- 2

local Crate = AceOO.Class(Box)
Crate.numCrates = 0

function Crate:GetNumCrates()
    return Crate.numCrates

function Crate.prototype:init()
    Crate.super.prototype.init(self) -- calls the Box constructor
    Crate.numCrates = Crate.numCrates   1

local greenCrate = Crate:new()
local blackCrate = Crate:new()
print(Box:GetNumBoxes()) -- 4
print(Crate:GetNumCrates()) -- 2
-- note: Crate doesn't have a :GetNumBoxes() method
blackCrate:Shake() -- You hear something crunch.

So at the end, since all crates are boxes, there are 4 boxes total. And since crates are boxes and you can shake boxes, you can obviously shake crates.

Static methods are declared on the class itself, whereas instance methods are declared on the class's prototype field.


Interfaces let you know that an object or class specifies to a contract, so that you know that all the little specialties that the interface demands, the class will definitely have them.

local AceOO = AceLibrary("AceOO-2.0")

local ISpeakable = AceOO.Interface { Speak = "function" }
-- declare a new interface, one that requires a Speak function.

local Dog = AceOO.Class(ISpeakable)

function Dog.prototype:Speak()
    return "Bark!"

local Cat = AceOO.Class(ISpeakable)

function Cat.prototype:Speak()
    return "Meow!"

local fluffy = Dog:new()
local mrPants = Cat:new()
assert(fluffy:Speak() == "Bark!")
assert(mrPants:Speak() == "Meow!")

print(not AceOO.inherits(Cat, Dog)) -- true
print(not AceOO.inherits(Dog, Cat)) -- true
print(AceOO.inherits(Cat, ISpeakable)) -- true
print(AceOO.inherits(Dog, ISpeakable)) -- true

If either Cat or Dog didn't implement a speak method, an error would be raised.

Also, it is in good taste to put I before all your interfaces.

Implicit Interfaces

local AceOO = AceLibrary("AceOO-2.0")

local Dog = AceOO.Class()

Dog.prototype.happiness = 5

function Dog:Hug()
    self.happiness = self.happiness   1

local Cat = AceOO.Class()

function Cat:Hug()

local muffin = Dog:new()
local mephistophiles = Cat:new()
mephistophiles:Hug() -- "Purr"

print(not AceOO.inherits(Cat, Dog)) -- true
print(not AceOO.inherits(Dog, Cat)) -- true

local IHuggable = AceOO.Interface { Hug = "function" }
-- declare a new interface, one that requires a Hug function.

print(AceOO.inherits(Cat, IHuggable)) -- true
print(AceOO.inherits(Dog, IHuggable)) -- true

Both Cat and Dog fit into IHuggable's interface, thus they pass the inheritance test.


Mixins are a way to properly allow multiple inheritance without all the giant issues such as diamond inheritance.

local AceOO = AceLibrary("AceOO-2.0")

local Artist = AceOO.Mixin { "Paint", "Sculpt" }
-- the Artist mixin exports the Paint and Sculpt methods

function Artist:Paint()
    print(string.format("%s paints a beautiful picture", self))
function Artist:Sculpt()
    print(string.format("%s sculpts a delightful statue", self))

local Scientist = AceOO.Mixin { "Think" }
-- the Scientist mixin exports the Think method

function Scientist:Think()
    print(string.format("%s thinks about life", self))

local Inventor = AceOO.Mixin { "Invent" }
-- the Inventor mixin exports the Invent method

function Inventor:Invent()
    print(string.format("%s invents a cool device", self))

local GreatPerson = AceOO.Class(Artist, Scientist, Inventor)
-- a GreatPerson will be able to do all the things an Artist, a Scientist, and an Inventor can do.

function GreatPerson.prototype:init(name)
    GreatPerson.super.prototype.init(self) = name

function GreatPerson.prototype:ToString()

local daVinci = GreatPerson:new("Leonardo da Vinci")

da Vinci can do a lot of cool stuff.


If you declare a method with one of the following names, it enables a lua metamethod.

There are a few metamethods:

  • self:ToString() -- tostring(self)
  • self:Add(other) -- self other
  • self:Subtract(other) -- self - other
  • self:Multiply(other) -- self * other
  • self:Divide(other) -- self / other
  • self:Exponent(other) -- self ^ other
  • self:Concatenate(other) -- self .. other
  • self:UnaryNegation() -- -self
  • self:Equals(other) -- self == other
  • self:IsLessThan(other) -- self < other
  • self:IsLessThanOrEqualTo(other) -- self <= other
  • self:CompareTo(other) -- (self.value - other.value)

There is no :IsGreaterThan(other) or :IsGreaterThanOrEqualTo(other), since not (self < other) == self >= other and not (self <= other) == self > other.

Also, :IsLessThanOrEqualTo(other) is optional if you have :IsLessThan(other), since not (other < self) == self <= other.

Also, if you have :CompareTo(other) available, it can replace :Equals(other), :IsLessThan(other), and :IsLessThanOrEqualTo(other). self:CompareTo(other) == 0 implies self == other. For < 0, self < other. For > 0, self > other.

local AceOO = AceLibrary("AceOO-2.0")

local Number = AceOO.Class()

function Number.prototype:init(value)
    self.value = value

function Number.prototype:ToString()
    return "Number(" .. self.value .. ")"

function Number.prototype:Add(other)
    return Number:new(self.value   other.value)

function Number.prototype:Equals(other)
    return self.value == other.value

print(Number:new(1)   Number:new(2)) -- "Number(3)"