About OOP and interface

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
Pedin
Prole
Posts: 16
Joined: Thu Aug 04, 2016 7:21 pm

About OOP and interface

Post by Pedin »

Hi guys, i have some questions about lua, I read in some places about oop and lua, i want to use the design pattern strategy, but i dont find anything about inheritance, polymorphism or interfaces, have it on lua? if not what i can use to simulate strategy.
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

Re: About OOP and interface

Post by airstruck »

Strategy pattern is all about polymorphism, you don't really need inheritance or interfaces for it.

Code: Select all

local function attackWithKnife (self, target)
    print(self.name .. ' stabs ' .. target.name)
end

local function attackWithGun (self, target)
    print(self.name .. ' shoots ' .. target.name)
end

local guyA = {
    name = 'Stabbicus',
    attack = attackWithKnife,
}

local guyB = {
    name = 'Blastimus',
    attack = attackWithGun,
}

guyA:attack(guyB)
guyB:attack(guyA)
Is this the strategy pattern? Sure, we can tell a "guy" to "attack" and we don't care about the details of how it happens.

Did we need inheritance? No, we have first-class functions so we can just attach whatever methods we want to our objects without any dedicated constructors or inheritance.

Did we need interfaces? Nope. From the last two lines of code, we know there's an implicit contract with objects representing "guys." We expect a "guy" to have an "attack" function with a specific function signature, and a "name" field that's a string. We can use this implicit contract without formally specifying it in an interface.
Pedin
Prole
Posts: 16
Joined: Thu Aug 04, 2016 7:21 pm

Re: About OOP and interface

Post by Pedin »

Thx man, I am very attached to some java and .net paradigms, I did not think this way, perhaps lack of study of language or vice in specific methods. so, really thx you.
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

Re: About OOP and interface

Post by airstruck »

No problem, it was an interesting question :)

I think most of the traditional design patterns become much simpler when you embrace dynamic types and first-class functions. Just for the record, though, you could also do this in a more "classical" way.

Code: Select all

-- defines a class with optional single inheritance
local function Class (superclass)
    local meta = { __index = {} }
    return setmetatable(meta.__index, {
        __call = function (_, ...)
            local instance = setmetatable({}, meta)
            if instance.init then
                instance:init(...)
            end
            return instance
        end,
        __index = superclass
    })
end

-- our Weapon interface is just a doc block

-- @interface Weapon
-- @method init(Guy owner)
-- @method attack(Guy target)

-- Knife and Gun implement Weapon

-- @class Knife
-- @implements Weapon
local Knife = Class()

-- @constructor
-- @param Guy owner
function Knife:init (owner)
    self.owner = owner
end

function Knife:attack (target)
    print(owner.name .. ' stabs ' .. target.name)
end

-- @class Gun
-- @implements Weapon
local Gun = Class()

-- @constructor
-- @param Guy owner
function Gun:init (owner)
    self.owner = owner
end

function Gun:attack (target)
    print(owner.name .. ' shoots ' .. target.name)
end

-- @class Guy
local Guy = Class()

-- @constructor
-- @param string name
function Guy:init (name)
    self.name = name
    self.weapon = nil -- this does nothing, it's just here for documentation
end

function Guy:attack (target)
    if self.weapon then self.weapon:attack(target) end
end

-- @class KnifeGuy
-- @extends Guy
local KnifeGuy = Class(Guy)

function KnifeGuy:init (name)
    Guy.init(self, name) -- call the superclass constructor
    self.weapon = Knife(self)
end

-- @class GunGuy
-- @extends Guy
local GunGuy = Class(Guy)

function GunGuy:init (name)
    Guy.init(self, name) -- call the superclass constructor
    self.weapon = Gun(self)
end

local guyA = KnifeGuy('Stabbicus')
local guyB = GunGuy('Blastimus')

guyA:attack(guyB)
guyB:attack(guyA)
If you ask me, that's a hell of a lot of work for very little gain. If you like the idea of having KnifeGuy and GunGuy classes, you might consider factory functions instead (factory pattern is also much simpler with first-class functions and dynamic typing).

Code: Select all

local function attackWithKnife (self, target)
    print(self.name .. ' stabs ' .. target.name)
end

local function attackWithGun (self, target)
    print(self.name .. ' shoots ' .. target.name)
end

local function KnifeGuy (name)
    return {
        name = name,
        attack = attackWithKnife,
    }
end

local function GunGuy (name)
    return {
        name = name,
        attack = attackWithGun,
    }
end
If you miss inheritance with these factory functions, try using decorators instead (they'll also be much less work than you're used to).
Post Reply

Who is online

Users browsing this forum: Bing [Bot] and 11 guests