Along the lines of the DRY principle (Don't Repeat Yourself), here's another method with the exact same result, but no need to repeat the code for every object.
Code:
--[[
Here is another approach to the same events methodology
Lets create a utility function that adds the event capability to any object/table you provide.
]]--
-- Here's the utility function; (its a global !!)
-- target is the object/table to which event capabilities will be added
-- eventlist is a 1 dimensional list (input to table.makeset()) with event names
-- The function returns the target object, with event capability added
table.AddEvents = function ( target, eventlist )
-- check our inputs
assert( type(target) == 'table', "Can't enable events; the target must be a table.")
assert( type(eventlist) == 'table', "Can't enable events; the eventlist must be a table.")
assert( target.Publisher == nil, "Can't add event capbility, the target already has an event publisher")
-- create event list into our object so consumers can examine available events
local Events = table.makeset(eventlist)
-- We require our own Publisher object to handle the event subscriptions
local Publisher = Classes.Publisher:New()
-- this is an internal function which can be called to raise an event
-- event must be a value from the Events list, all extra parameters will be passed
-- on to the eventhandlers of our subscribers
local Event = function (self,event,...)
-- Check if an event is provided, also make sure its a valid event
assert(event, "No event provided")
assert(Events[event] == event, "Event " .. event .. " is not a supported event")
-- now let our publisher spread the word of our event
self.Publisher:Event (event, unpack (arg))
end
-- anyone can subscribe to our events by calling this function
-- parameter f should be a function to be called when an event occurs
local Subscribe = function (self, f)
return self.Publisher:Subscribe(f)
end
-- Once subscribed, anyone can unsubscribe from our events by calling this function
-- parameter f should be the same function that was used to subscribe.
local Unsubscribe = function (self, f)
return self.Publisher:Unsubscribe (f)
end
-- Now lets add the created locals to our target object
target.Publisher = Publisher
target.Events = Events
target.Event = Event
target.Subscribe = Subscribe
target.Unsubscribe = Unsubscribe
return target
end -- table.AddEvents
print()
print("Lets try our second approach...")
-- create a table/object with just the functional code;
local TestObject = {
-- sample methods to start our object and raise an event
Start = function (self)
-- this is where actual code would go
-- Now raise the event that we started our object
self:Event(self.Events.Started, "parameter1", 2, "and parameter3")
end,
Other = function (self)
-- this is where actual code would go
-- Now raise the event that we did something
self:Event(self.Events.SomeOther, "No parameters")
end,
}
-- Call AddEvents, to add the Event capability to our TestObject
table.AddEvents (
TestObject,
{
'Add',
'Stopped',
'Started',
'SomeOther',
'OneMore',
} )
-- Here is our test event handler function
local myEventHandler = function (event, ...)
if event == TestObject.Events.Started then
print ("Object informed us that it started, with parameters: ", unpack(arg) )
else
print ("Object informed us about some other event ('" .. event .. "'), with parameters: ", unpack(arg))
end
end
-- Now test again, the results should be identical;
-- now register for events, using the same test eventhandler.
TestObject:Subscribe(myEventHandler)
-- go call Start method so we get an event, and see whether our handler prints them in the lua console
TestObject:Start() --> Object informed us that it started, with parameters: parameter1 2 and parameter3
TestObject:Other() --> Object informed us about some other event ('SomeOther'), with parameters: No parameters
-- Now unregister
TestObject:Unsubscribe(myEventHandler)
Additional remark; the event capability can also be added inline like this;
Code:
local TestObject = table.AddEvents(
-- create target
{
Start = function (self)
-- bla bla
end,
Other = function (self)
-- bla bla
end,
},
-- create eventlist
{
'Add',
'Stopped',
}
)