Page 1 of 3

Create a class without external libraries

Posted: Thu Feb 11, 2016 9:59 pm
by Kibita
Hello!
I wonder how you guys make your own class in Lua without using external libraries like middleClass. I'm a little stuck in this part because I don't wan't to make something wrong that could imply a bad coding. So, what's is the most formal way to make classes in lua?

Thank you!

Re: Create a class without external libraries

Posted: Thu Feb 11, 2016 10:10 pm
by bobbyjones
There isn't. You can do it whatever way you like. Some use closures and some use metatables. It depends on what you want or need.

With closures - http://theindiestone.com/forums/index.p ... etatables/

With metatables - http://lua-users.org/wiki/ObjectOrientationTutorial

Re: Create a class without external libraries

Posted: Thu Feb 11, 2016 10:43 pm
by Kibita
bobbyjones wrote:There isn't. You can do it whatever way you like. Some use closures and some use metatables. It depends on what you want or need.

With closures - http://theindiestone.com/forums/index.p ... etatables/

With metatables - http://lua-users.org/wiki/ObjectOrientationTutorial
So it is right what I am doing?

Code: Select all

local Game = {}
Game.__index = Game

function Game:new(meter, gravity)
    setmetatable({}, Game)

    -- Set physics parameters
    love.physics.setMeter(meter)
    self.world = love.physics.newWorld(0, gravity*meter, true)

    -- Load background
    self.background = love.graphics.newImage("img/background.png")

    -- Create the Balls
    self.balls = {}
    self.balls.left  = Ball:new(self.world, 75, 5, 150) -- Insert the left ball
    self.balls.right = Ball:new(self.world, 75, 570, 150) -- Insert the right ball

    -- Create the player
    self.player = nil

    -- Attatch the balls to the player

    return self
end

...

Re: Create a class without external libraries

Posted: Fri Feb 12, 2016 1:16 am
by bobbyjones
Actually if you are just starting out I recommend the closure approach it's easier to understand. But anyways if your code works and does what your expect it to then yes it is right. If it doesn't then we'll you are doing it wrong. If you tested the code properly you would notice an error. Instead of returning self return the setmetatable({},Game).

Re: Create a class without external libraries

Posted: Fri Feb 12, 2016 8:47 am
by Roland_Yonaba
I would just suggest to go along with PiL's approach, chapter 16.
Also, even though I do not consider this to be anywhere formal, here is the bare-bones light class implementation I am mostly using when I need one.

Re: Create a class without external libraries

Posted: Fri Feb 12, 2016 12:36 pm
by airstruck

Code: Select all

Game.__index = Game
...
setmetatable({}, Game)
That pattern has always felt wrong to me. I'm not sure how it got so popular. The __index property does not really belong in your Game class, and none of Game's properties really belong in the metatable. It works alright, it just seems messy and semantically non-obvious.

I'd probably use a dedicated metatable instead:

Code: Select all

local meta = { __index = Game }
...
setmetatable({}, meta)
Or just create them on the fly if you don't care about a few extra tables.

Code: Select all

setmetatable({}, { __index = Game })
Also, in your Game:new method you create a new instance with setmetatable({}, Game), but you don't assign the result to anything, and then you use self (which refers to the Game class) as if it were the instance you created. This won't work the way you probably intend.

Once you fix that, there's another problem with having the "instantiator" and constructor both in one function (Game:new). If you want to use inheritance, you have no way of applying the parent constructor to an instance of a subclass, because there are no dedicated constructors, only the "instantiator."

Here is the base class I use when I need classical OOP, if you're interested. When I don't feel like including that, I sometimes use a snippet like this instead:

Code: Select all

local Foo = setmetatable({}, { 
    __call = function (self, ...)
        local object = setmetatable({}, { __index = self })
        return object, self.constructor(object, ...)
    end
})

Re: Create a class without external libraries

Posted: Fri Feb 12, 2016 3:33 pm
by rmcode
I prefer the closure based approach:

Code: Select all

local ClassName = {};

function ClassName.new()
    local self = {};

    local baz = 'Foo is Löve, All is Löve' -- Private attribute
    self.bazooka = true -- Public attribute
    
    -- Private function
    local function bar()
        -- Do stuff
    end
    
    -- Public function
    function self:foo()
        -- Do stuff
    end

    return self;
end

return ClassName;
It makes for clean and readable code and makes it easy to use inheritance.

Re: Create a class without external libraries

Posted: Fri Feb 12, 2016 4:25 pm
by airstruck
That's nice and readable, no metatables, allows private members.

Each object has its own copy of all methods and private functions, which could be a performance consideration. When using metatables, all instances of a class share the same methods. You could get that without metatables by doing this kind of thing:

Code: Select all

-- classname.lua

-- Private function
local function bar (self)
    -- Do stuff
end

-- Public function
local function foo (self)
    -- Do stuff
end

-- Constructor
return function (x, y)
    return {
        -- Constructor args
        x = x, y = y,
        -- Default properties
        bazooka = true,
        -- Public methods
        foo = foo,
    }
end
It doesn't look as nice, but you get reusable methods. You'll still have private functions, and it will be easy to expose them as public functions by adding them to the returned object. You won't have private instance variables, but you can have "static" privates.

If your module returns a table of constructors for similar things, this will give you nice mixin-like reuse of methods and you won't need inheritance.

Re: Create a class without external libraries

Posted: Fri Feb 12, 2016 10:28 pm
by Kibita
bobbyjones wrote:Actually if you are just starting out I recommend the closure approach it's easier to understand. But anyways if your code works and does what your expect it to then yes it is right. If it doesn't then we'll you are doing it wrong. If you tested the code properly you would notice an error. Instead of returning self return the setmetatable({},Game).
The unique trouble I'm facing right now is when I create two objects from the same class:

Code: Select all

-- Create the Balls
self.balls = {}
self.balls.left  = Ball:new(self.world, ballRadius, 0, gameHeight / 2) -- Insert the left ball
self.balls.right = Ball:new(self.world, ballRadius, gameWidht - ballRadius, gameHeight / 2) -- Insert the right ball
It just show one (actually I think both are created, but they share the same information like position and size, so one of the balls are covering the other, making it not visible)

Re: Create a class without external libraries

Posted: Sat Feb 13, 2016 12:44 am
by bobbyjones
Yup its because you returned self and not the setmetatable({},Game)