Construct OOP Manager

The Changelog:

3292018:

  • began experimental concept
  • finished experimental concept, tested, release canidate published

3302018:

  • bug + security fixes
  • better error handling
  • example method of securely passing args now included in source

422018:

  • new onCreate method for initializing new Objects. During classdef, declare __private:onCreate and initialize.
  • naming conventions and organized cleaned up

442018:

  • further fixes and improvements

What is Construct?

Construct is an OOP Manager loosely based around ROBLOX Instances and how they’re created/managed. Using Construct, you can get the following features:

  • Actually private variables, allowing you to create secure Objects which are all managed by ObjectsManager. Keep ObjectsManager private, as it holds a reference to all internal private data for class building purposes.

  • Classes/Objects neatly contained and organized, all wrapped up in the ObjectsManager Object. Want to expose your Instances to a Public API? Simply make a reference to ObjectsManager.new for securely creating new Instances without exposing the ObjectsManager.

  • Maintains the simplicity of traditional Lua-OOP all while improving on the concept, allowing for private data, maintaining inheritance, and organization of classes/Objects.

How to use Construct

Construct is extremely easy and simple to use! An example below can be used as guidance toward how to create and implement classes, inheritance, and new Objects.

----  @class  BaseObject
----  @desc  Abstract class for all other Objects
local classBaseObject = ObjectsManager.createClass("BaseObject") do
	------  CLASS PRE-INITIALIZATION
	local __private = ObjectsManager.__private.registeredClasses.BaseObject
	------  CLASS LOGIC
	function classBaseObject:getClass()
		return ObjectsManager.__private.loadedObjects[self].objectClassname
	end
	--	@param  none
	--  @desc  Returns what the Object inherits from
	--  @returns  string
	function classBaseObject:getInheritsFrom()
		return ObjectsManager.__private.loadedObjects[self].inheritObjectPrivate.objectTostring
	end
	--  @param  string isA
	--  @desc  Returns boolean whether SGObject is or isn't a inheritent of Class isA
	--  @returns  boolean
	function classBaseObject:isA(isA)
		local lastSearched = ObjectsManager.__private.loadedObjects[self]
		while true do
			if lastSearched.objectClassname:sub(1, #isA):match(isA) then
				return true
			end
			if lastSearched == __private then
				break
			end
			lastSearched = lastSearched.inheritObjectPrivate
		end
		return false
	end
	--  @param  none
	--  @desc  Destroys the Object and all of its associated data
	--  @returns  none
	function classBaseObject:destroy()
		local thisPrivate = ObjectsManager.__private.loadedObjects[self]
		for key in next, thisPrivate.objectRoot do
			thisPrivate.objectRoot[key] = nil
		end
		for key in next, thisPrivate.objectMetatable do
			thisPrivate.objectMetatable[key] = nil
		end
		for key in next, thisPrivate do
			thisPrivate[key] = nil
		end
		ObjectsManager.__private.loadedObjects[self] = nil
		return 
	end
end
------	EXAMPLES/TESTING
local BaseObject = ObjectsManager.new("BaseObject")
----  @class  Fruit
----  @desc  Represents a Fruit Object
local classFruit = ObjectsManager.createClass("Fruit", BaseObject) do
	local __private = ObjectsManager.__private.registeredClasses.Fruit
	------  CLASS LOGIC
	__private.sweetness_thres = 2
	--  @param  none
	--  @desc  Checks to see whether the Fruit matches the required
	--  sweetness threshold
	--  @returns  boolean
	function classFruit:isSweet()
		return self.sweetness >= __private.sweetness_thres
	end
end
----  @object  Apple
----  @desc  Apple which is Class of Fruit
local Apple = ObjectsManager.new("Fruit", "Apple")
Apple.sweetness = 3
print(Apple:isSweet())
print(Apple:isA("Fruit"))

Need a more detailed example?
Take a look at the Bleu Pigs PigBot, the first project to officially integrate and utilize Construct.

Why Construct?

Construct was largely built around wanting a managed OOP experience while being able to have truly private variables. If you desire organization and security of private variables, Construct provides this all within a neatly wrapped up package. Take a copy today!

Feedback, Questions, Concerns

This is a release candidate. Although I’ve tested it, there could be an unexpected bug or two within the framework. If you find any, please report them through this thread so I can make a swift response.

15 Likes

I was grappling with something similar to this the other day, so when I had a look through your code, I noticed it uses a pattern similar to this:

local lookup = setmetatable({}, {__mode="k"})
local proxy = {}
local data = {proxy = proxy}
lookup[proxy] = data

function getData(proxy)
	return lookup[proxy]
end

function getProxy(data)
	return data.proxy
end

That is, a lookup table is used to create a weak reference from a proxy value to some actual data. Then, to refer back to the proxy, a reference to it is stored somewhere in the data.

You should be aware that this pattern creates a reference loop that will cause the objects involved to never be garbage collected, despite the weaktable. This issue is solved with emphemeron tables, which were introduced in Lua 5.2. Since Roblox Lua is based on 5.1, the issue is still present here.

This can be worked around by using a second weaktable:

local dataLookup = setmetatable({}, {__mode="k"})
local proxyLookup = setmetatable({}, {__mode="v"})
local proxy = {}
local data = {}
dataLookup[proxy] = data
proxyLookup[data] = proxy

function getData(proxy)
	return dataLookup[proxy]
end

function getProxy(data)
	return proxyLookup[data]
end
2 Likes

Thanks for the heads up. I’m aware of said issue, and is why I implemented a generic “:Destroy” function onto the BaseObject in which all of my Objects inherit from first. I intentionally keep a hard reference and a weak reference cause I don’t want to destroy the Object unless its needed (meaning you need to manage your own Objects like you should anyways).

Though it most certainly seems I haven’t updated it since I’ve made some changes to how I look up both classes/actual objects. I’ll be sure to correct this once I get back to my computer later.

Requested close by author.