← Back to the index page

Abstract Factory

Creational pattern Abstract Factory enables the creation of families of related objects without specifying their exact classes. In other words, abstract factory is a pattern that creates and groups other factories, which logically are related to each other.

When is abstract factory appropriate?

Then, there is a family of common products; in our case, these are the monsters from factory method: goblin, skeleton, and demon. All these monsters have the same properties: name, hit points (hp), damage, and special power. What if we want to add another class of character in our RGP like NPC. Let’s imagine that we already have some complex logic which produces our monsters. I think you have no too much will to inject another logic inside the monster production/generation. This is where Abstract Factory comes to help. Abstract Factory is a kind of super-wrapper over other factories. Here one thing should be considered that sub-factories should have the same interface to produce products (monsters), in our case it is only Factory:create() method. So the interface has only one method.

---@alias FactoryIntrerface { create: fun(type: string): Monster }

Abstract Factory Scheme

Tip

Use design patterns to solve real-world problems, do not adapt your code to the problem. Think firstly, maybe abstract factory is not the best solution for your problem and makes extra overhead?

Implementation

MonsterFactory class

---@class Monster
---@field public name string
---@field public damage number
---@field public hp number
---@field public special string
local Monster = {}
Monster.__index = Monster

---@return Monster
function Monster:new(name, damage, hp, special)
    local t = {
        name = name,
        damage = damage,
        hp = hp,
        special = special
    }
    return setmetatable(t, self)
end

---@class Goblin: Monster
local Goblin = {}
---@return Goblin
function Goblin:new()
    return Monster:new("Goblin", 2, 10, "None") --[[@as Goblin]]
end

---@class Skeleton: Monster
local Skeleton = {}
---@return Skeleton
function Skeleton:new()
    return Monster:new("Skeleton", 4, 12, "Reform") --[[@as Skeleton]]
end

---@class Demon: Monster
local Demon = {}
---@return Demon
function Demon:new()
    return Monster:new("Demon", 10, 50, "Fire hit") --[[@as Demon]]
end

---@class MonsterFactory
local MonsterFactory = {}

---@return MonsterFactory
function MonsterFactory:create(type)
    if type == "Goblin" then
        return Goblin:new()
    elseif type == "Skeleton" then
        return Skeleton:new()
    elseif type == "Demon" then
        return Demon:new()
    else
        error("unknown monster")
    end
end

return MonsterFactory

NPCFactory class

---@class NPC
---@field public name string
---@field public say string
---@field public hp number
---@field public special string
local NPC = {}
NPC.__index = NPC

---@return NPC
function NPC:new(name, say, hp, special)
    local t = {
        name = name,
        say = say,
        hp = hp,
        special = special,
    }
    return setmetatable(t, self)
end

---@class Healer: NPC
local Healer = {}
---@return Healer
function Healer:new()
    return NPC:new("Healer", "Hello", 100, "Heals") --[[@as Healer]]
end

---@class Blacksmith: NPC
local Blacksmith = {}
---@return Blacksmith
function Blacksmith:new()
    return NPC:new("Blacksmith", "Welcome", 100, "Repairs") --[[@as Blacksmith]]
end

---@class NPCFactory
local NPCFactory = {}

---@return NPCFactory
function NPCFactory:create(type)
    if type == "Healer" then
        return Healer:new()
    elseif type == "Blacksmith" then
        return Blacksmith:new()
    else
        error("unknown npc")
    end
end

return NPCFactory

AbstractFactory class

local MonsterFactory = require("MonsterFactory")
local NPCFactory = require("NPCFactory")

---@class AbstractFactory
local AbstractFactory = {}
AbstractFactory.__index = AbstractFactory

---@return AbstractFactory
function AbstractFactory:new(type)
    if type == "NPC" then
        return NPCFactory
    elseif type == "Monster" then
        return MonsterFactory
    else
        error("unknown factory type")
    end
end

return AbstractFactory

Usage of Abstract Factory

local AbstractFactory = require("AbstractFactory")

local monsterFactory = AbstractFactory:new("Monster")
local npcFactory = AbstractFactory:new("NPC")

local goblin = monsterFactory:create("Goblin")
print(goblin.name, goblin.damage, goblin.hp, goblin.special)
local demon = monsterFactory:create("Demon")
print(demon.name, demon.damage, demon.hp, demon.special)

local healer = npcFactory:create("Healer")
print(healer.name, healer.speak, healer.hp, healer.special)
--[[
Goblin  2       10      None                                                    
Demon   10      50      Fire hit                                                
Healer  nil     100     Heals       
--]]

Conclusion

Abstract Factory is a factory that produces factories with similar logic.

Feedback

For feedback, please check the contacts section. Before writing, please specify where you came from and who you are. Sometimes spammers go insane. Thank you in advance for your understanding.

← Back to the index page