Hmm I think you have made me spot another weakness on middleclass.
The _call parameter is calling Object.new, but it should really be invoking the class:new. In other words when you do this:
It is really doing this:
It should be doing this instead:
This way, if you override Game.new, the Game() call would just use that new implementation - you would not have to repeat the overriding on _call.
I'll try to make that change this evening.
Now, to your question: singletons. Here's my view of them: their implementation requirements are very dependent on their target users.
On your example, you will be the only one using your Game object, so in reality, you would be just fine without singletons; you can just create one single global variable and trust that you will not create another one later on. Small improvement just for the laughts: making the whole Game class private, and a game instance public.
Code: Select all
-- File 'Game.lua'
local Game = class('Game', StatefulObject) -- notice the local here
function Game:initialize()
...
end
...-- states, etc
game = Game:new()
Now you can't create other games. Well, not directly: You can still do game.class:new() ... so you might probably want to override new() method to throw an error after the game variable is created (after the fix I'll do today you will not have to mess around with _call).
Code: Select all
game = Game:new()
function Game:new()
error('Explicit creation of games is forbidden. Use the game global variable instead')
end
But again, if you are going to be the only user of your class, those security measures aren't really necessary.
But what about Singletons in general? What would be the best way to create them with MiddleClass?
Well, consider the following: Every time you create a new singleton you would have to do the same things: create the class, then create a single private instance variable, then override create a getter for that private instance, and then override the new function so it throws an error.
Well, I'm a very big fan of code reuse. This repetitiveness suggests that it should be probably managed with code.
Mixins are a great tool for this job. Consider the following Singleton Mixin:
Code: Select all
-- File 'Singleton.lua'
local private = {}
Singleton = {
included=function(class, ...)
private[class] = { instance = class:new(...) }
end
}
function Singleton:getInstance()
return private[self].instance
end
function Singleton:new()
error("Object creation is forbidden on singletons")
end
The 'included()' method is executed when the Mixin is included in a class (by the way I need to add the '...' parameters to middleClass). This is saying: when the mixin is included, generate a private variable for that class called 'instance' invoking class:new.
The other two functions are just adding a new function to class (getInstance) that returns the private variable and overriding new() so it throws an error.
Here's how you would use it with Game:
Code: Select all
-- File 'Game.lua'
require('singleton.lua')
Game = class('Game', StatefulObject)
function Game:initialize()
...
end
...-- states, etc
-- It is important that this is done at the end, or at least after the Game:initialize() function is defined. Otherwise it will not work
Game:includes(Singleton, ...) -- the ... means 'parameters for creating the game instance, if any'
Finally, a couple comments about nomenclature - These are just recommendations, feel free to not adhere to them.
- I think methods should be "verbal" when possible. I would have called the "instance" method "getInstance"
- Uppercases are better left for classes and mixins. Attributes and methods, lowercased. So I would have called the instance gameInstance instead of GameInstance.
This evening I'll make the two code changes in MiddleClass, and I'll add one Naming convention section to the wiki.