Singletons take a reusable object and give it a single point of access for an application. Without awakening the debate over whether global state is good thing or bad thing I’m going to attempt to discuss singletons. I think it’s likely everyone will be familiar with is something along the lines of: Renderer::GetInstance()->Function();
This is often accompanied by code that caches the instance pointer in an object, or locally in a function before using it. (I’m not picking on renderers specifically, they just exemplify a particular use case.)
To me though, this is a really odd way of accessing global state through a global function. The access point is within the class scope and it’s a lenghly and cumbersome interface for clients. It combines the implementation choice with the client interface, to the detriment of both. It’s simultaeously more difficult to refactor your interface or reinitialize your system and more work for clients.
In cases like this, I think your interface and your implementation should be kept separate. If you want to implement your set of renderers using inheritance, great stuff. But your clients don’t care, that’s why they’re relying on your library in the first place. They just want a consistent API to work with.
So, throwing something out there, how about:
Renderer->DoStuff();
But, oh no! That would be a global pointer. Well, yes it would, but it would also be a read only smart pointer.
SmartPtrSingleton<RendererBase> Renderer;
Renderer.Bind( new RendererDX9("Wholly cow! A parameter!") );
Renderer.Reset();
As soon as you get over the horror of using a global pointer witout a scope qualifier in front of it, you can imagine:
Kernel::Renderer->Stuff();
using namespace Kernel;
Renderer->Stuff();
void Foo( SmartPtrSingleton<RendererBase> pRenderer )
{
}
What’s really neat about a smart pointer singleton is that:
- It’s dead simple, and reusable.
- It’s very easy to integrate into an existing design using global pointers.
- No one can write to the pointer.
- No one can take a direct copy of your pointer
- It’s naturally independent of the original object.
- Object storage, construction and lifetime are all clear and separate to the access method.
- It’s simple for the client to use.
- It’s not inherently global, it can be passed as a parameter aiding incremental refactoring.
It would be perfect if you could easily change between static and dynamic binding, but as with any other method you’ll need to wrap your interface to allow swapping between m_ptr->Function(); and m_ptr->CLASS::Function();. Static binding is particularly useful to allow link-time-code-generation if you know that you will only ever need one specific class of an object (a console renderer for example).
Just a touch of late night thinking.





Post a Comment