Skip to content

Getting Started Guide

Maurits edited this page Sep 10, 2017 · 1 revision

Getting Started

So you're interested in using SIMPLOO? Great!

This page will guide you through the basics of SIMPLOO. The only thing you need is a basic understanding of Lua and object oriented programming.

A Simple Class

Let's say you want to recreate an easy to understand concept such as a bank account in Lua. The usual way to create such a concept in lua requires you to:

  • create a metatable (which holds your deposit/withdrawal/getbalance functions)
  • create a table to hold your balance, and...
  • create a function to initialize a new instance

For example:

Account = {}
Account.__index = Account

function Account.create()
    local account = {} 
    account.balance = 0

    return setmetatable(account, Account)
end

function Account:deposit(amount)
    self.balance = self.balance + amount
end

function Account:getBalance()
    return self.balance
end

-- create and use an Account
acc = Account.create()
acc:deposit(100)
print(acc:getBalance())

SIMPLOO can simplify this process by providing you with an easy to use syntax which is very similar to other object oriented programming languages. The added benefit of this, is that SIMPLOO adds an extra abstraction layer that prevents you from having to think about metatables completely.

So here's an Account class in SIMPLOO:

class "Account" { -- Define a new class 'Account'
    public { -- We want the members enclosed in these brackets to have public access (to be accessible by everyone)
        deposit = function(self, amount) -- Define a new member called deposit, and assign it to a function. self will be an instance of our class
            self.balance = self.balance + amount -- take our member called balance, and add an amount
        end;
        
        withdraw = function(self, amount)
            self.balance = self.balance - amount
        end;
        
        getBalance = function(self)
            return self.balance
        end;
    };
    private { -- We want the members enclosed in these brackets to have private access (to be accessible by only this class)
        balance = 0; -- Define a new member called balance, and give it a default value of 0.
    };
}

What's nice to mention is that this is completely valid Lua. You only have to remember that after every member declaration or modifier, you have to add a trailing ; to mark it as a table entry, because we're essentially using a big table here to define our implementation. I personally use ; when I work with tables that define a class, and , when I work with tables that hold a collection of data. Both are valid ways to separate table entries.

To play with your new class, you can instantiate it and try out some class methods!

local account = Account.new()

account:deposit(100)
account:withdraw(25)
print(account:getBalance()) -- 75 !!

Access Modifiers

This page will guide you through access modifiers in SIMPLOO. The only thing you need is a basic understanding of Lua and object oriented programming.

Background

What's generally perceived as an important feature of any object-oriented language is the ability to hide the internal workings of a class from outside view, also known as encapsulation. For example; if we look at a bank account in the terms of security, you wouldn't want anyone to be able to be able to modify their balance without restrictions. The only way you'd want someone to be able to modify their account balance is by using the provided withdraw and deposit functionality, which in turn assures no-one can withdraw more than they own or deposit huge amounts without triggering alarm bells.

Access Modifiers

SIMPLOO provides you with the standard set of access modifiers:

  • public - allow anyone to access the given members
  • protected - allow the containing class and it's children to access the given members (not supported in 2.0 version at the moment)
  • private - only allow the containing class to access the given members

Members which haven't been enclosed by an access modifier will default to public.

Usage

To use access modifiers, you simple have to type the modifier name, and enclose any members to which you want to apply the modifier in curly brackets. Don't forget to end your curly brackets with a ;!

class "Person" {
    public { -- The following members can be accessed by anyone.
        askName = function(self)
            print("My name is " .. self.name) -- Because askName() is a public function that's part of the class, it does have access to it's own private variables.
        end;
        
        askSecret = function(self)
            print("I'm sorry but I'm not telling you my secret.") -- Some things aren't to be told.
        end;
    };
    
    private { -- The following members are restricted to the Person only.
        name = "John";
        secret = "My pin code is 2858"; -- If this is your pin code then SIMPLOO became popular. :)
    };
}

local person = Person.new()
person:askName() -- Ask the person for his/her name.
person:askSecret() -- Ask the person nicely for his/her secret. He probably won't tell you.

print(person.secret) -- Attempt to rip the secret out of the person's brain (this will cause an error)!

Constructors & Finalizers

This page will guide you through constructors and finalizers in SIMPLOO.

Background

When working with classes there is often the need to:

  • initialize variables as soon as you instantiate a class
  • cleanup variables as soon as an instance is no longer used

This is where constructors and finalizers come into play. As the names suggest; a constructor can be used to initialize resources and will be called every time you instantiate a class, and a finalizer can be used to free resources and will be called after your instance is no longer being used.

Constructor

The constructor has to be named __construct and has to point to a function.

Examples 1: A simple constructor

class "Account" {
    public {
        __construct = function(self, initialBalance)
            self.balance = initialBalance -- Now every time we create a new Account it will have the initial balance you pass to the new function.
        end;
        
        getBalance = function(self)
            return self.balance
        end;
    };
    private {
        balance = 0;
    };
}

local account = Account.new(512)
print(account:getBalance()) -- 512 !!!

The constructor can be restricted by access modifiers. This means that a private constructor cannot be called by anyone but the containing class, and that a protected constructor can only be called by the containing class or it's children.

Examples 2: Constructors with inheritance

class "Account" {
    public {
        __construct = function(self, initialBalance)
            self.balance = initialBalance -- Now every time we create a new Account it will have the initial balance you pass to the 'new' function.
        end;
        
        getBalance = function(self)
            return self.balance
        end;
    };
    private {
        balance = 0;
    };
}

class "SavingsAccount" extends "Account" {
    public {
        __construct = function(self, initialBalance, interestRate)
            self.Account(initialBalance) -- Call the parent class constructor.
            
            self.interest = interestRate
        end;
        
        getInterest = function(self)
            return self:getBalance() / 100 * self.interest
        end;
    };
    private {
        interest = 0;
    };
}

local account = SavingsAccount.new(512, 1.9)
print(account:getInterest())

Because SIMPLOO has multiple inheritance, you have to manually call the constructor of each parent you inherit from.

Finalizer

The finalizer has to be called __finalize and has to point to a function.

class "Account" {
    public {
        __construct = function(self)
            self.balance = math.random(0, 1000)
        end;
        
        __finalize = function(self)
            if self.balance > 0 then
                print("Good job, you just lost " .. self.balance .. " dollars!") -- Because we love witty remarks!
            end
        end;
        
        getBalance = function(self)
            return self.balance
        end;
    };
    private {
        balance = 0;
    };
}

local account = Account.new()
-- now eventually the print will show up in the console..!

An important fact about finalizers is that they will not get called immediately after you stop using your class. Lua has a build-in mechanism called the garbage collector that takes care of cleaning up unused variables. However, the garbage collector only runs periodically and at unpredictable intervals. This means that your class will not be finalized until the garbage collector decides it's time to run. Also keep in mind that there is no guarantee that __finalize will run at all when you terminate your Lua host program.

In the case that you want to run the garbage collector quicker, and thus finalize unused instances quicker, you can call the collectgarbage() function. This function does nothing more but force the garbage collection to look for unused variables and free them. You can put this function in some kind of timer that runs a few times per second in order to speed up finalization, at the cost of a little performance.

Inheritance

This page will guide you through inheritance in SIMPLOO. The only thing you need is a basic understanding of Lua and object oriented programming.

Background

Inheritance can be described as a process in which one class acquires properties of another class.

For example; you can create a class called Animal with common functionality such as eat(), sleep() and move(). After that, you can create a new class called Dog and make this class inherit all properties from the Animal class. Your dog class is now also capable of eating, sleeping and moving, but nothing stops you from adding a bark() function to your dog.

Inheritance

SIMPLOO supports multiple inheritance. This means, that you can make one class extend upon the functionality of one or more other classes. Inheritance can be achieved using the extends keyword.

Here's a example where we inherit from one class:

class "Animal" {
    public {
        doWalk = function(self)
            print("Walking!")
        end;
        
        doEat = function(self)
            print("Eating!")
        end;
        
        doSleep = function(self)
            print("Sleeping!")
        end;
    };
}

class "Dog" extends "Animal" {
    public {
        doBark = function(self)
            print("Woof!")
        end;
    };
}

local dog = Dog.new();
dog:doWalk() -- prints 'Walking!'
dog:doEat() -- prints 'Eating!'
dog:doSleep() -- prints 'Sleeping!'
dog:doBark() -- prints 'Woof!'

SIMPLOO uses a registry-like system to find direct access to all members available in a class. This means that you can chain many classes together but still maintain a constant performance when accessing class members. However, the speed of instantiation will decrease as you chain more and more classes together, because the entire class inheritance tree has to be duplicated every time you create a new instance.

Here's an example where we inherit from two classes:

class "Scanner" {
    public {
        getScannedDocument = function(self)
            print("Beep beep we're scanning!")
            
            return "This string represents a document"
        end;
    };
}

class "Printer" {
    public {
        printDocument = function(self, document)
            print("Printing: " .. document)
        end;
    };
}

class "Copier" extends "Scanner, Printer" {
    public {
        makeCopy = function(self)
            local document = self:getScannedDocument()
            
            self:printDocument(document)
        end;
    };
}

local copyMachine = Copier.new()
copyMachine:makeCopy() -- 'Beep beep we're scanning!'   'Printing: This string represents a document'

When you decide to inherit from more than one class, things may get a bit more complicated. For example; if you were to inherit from two classes which both have a member function called 'getValue()', then calling that function on yourself would lead to ambiguities: which parent function should we execute? Fortunately you can work around this in SIMPLOO by explicitly specifying which parent function to call.

Here's an example of multiple inheritance where the ambiguity problem is solved:

class "TemperatureGauge" {
    public {
        getValue = function(self)
            return math.random(-20, 50) -- This is just an example value.
        end;
    };
}

class "HumidityGauge" {
    public {
        getValue = function(self)
            return math.random(0, 100) -- This is just an example value.
        end;
    };
}

class "WeatherMonitor" extends "TemperatureGauge, HumidityGauge" {
    public {
        displayOverview = function(self)
            print("Weather Overview:")
            print("- Temperature: " .. self.TemperatureGauge:getValue()) -- Here we explicitly say that we want to get the value of our TemperatureGauge parent.
            print("- Humidity: " .. self.HumidityGauge:getValue())
        end;
    };
};

local weatherMonitor = WeatherMonitor.new()
weatherMonitor:displayOverview()

When you extend from one or more classes, the names of these classes will become class members that can be used to explicitly access a parent. This means that you cannot define usable class members with the same name as a parent class.

Static Variables

This page will guide you through statics in SIMPLOO. The only thing you need is a basic understanding of Lua and object oriented programming.

Background

When you declare a variable as static it's value will be shared across all instances of a class, and will be available even if you don't instantiate your class.

Example

Lets say you want to track the number of instances of your class. This can be done by defining a static variable instanceCount. In your constructor you add 1 to the counter, and in your finalizer you remove 1 from your counter. By defining a static function called getCount you can then request the number of instances from your class.

class "CountedClass" {
    public {
        __construct = function(self)
            self.m_iInstanceCount = self.m_iInstanceCount + 1
        end;
        
        __finalize = function(self)
            self.m_iInstanceCount = self.m_iInstanceCount - 1
        end;
        
        static {
            getCount = function(self)
                return self.m_iInstanceCount
            end;
        };
    };
    
    private {
        static {
            m_iInstanceCount = 0;
        };
    };
}

local tblInstances = {}
for i=1, 5 do
    tblInstances[i] = CountedClass.new()
end

print(CountedClass:getCount()) -- 5!

Clone this wiki locally