Sign in to follow this  
discman1028

Rethinking singleton design... why not all static?

Recommended Posts

Hi, I'm starting to wonder... what are the differences (advantages/disadvantages) between: Having a class with a static accessor (GetPtr()) and static instance. and Having a class with all static members/functions? I have used the former (traditional). But what's the downsides of the latter? Perhaps I've come full circle with this question... why are singletons (as opposed to fully-static classes) necessary? (or are fully-static classes also singletons). Thanks.

Share this post


Link to post
Share on other sites
Quote:
Original post by discman1028
Having a class with a static accessor (GetPtr()) and static instance.

and

Having a class with all static members/functions?


One's called a singleton, one's called a monostate. A previous thread on the subject.

Basically, with singletons, it's easier to delay or not load a singleton at all if it's un-needed. Monostates on the other hand, are easier to use without /completely/ hardcoding your code around the assumption of a single instance. On the other hand, monostates can be more confusing when used in such a manner, as changing apparently unrelated variables will effect each other.

I avoid both patterns for the most part though, like the plauge. I feel dirty whenever I use global storage. Hplus seems to talk about singletons being easier to convert from "one" to "just a couple hardcoded" in that very thread, and the chances are he's had more experience with both patterns than myself.

Share this post


Link to post
Share on other sites
Quote:
Original post by discman1028
Having a class with a static accessor (GetPtr()) and static instance.

and

Having a class with all static members/functions?


Well, if you have a class with all static members and all static functions, that's not a monostate, that's just a static class, and you're basically just using the class keyword for scope definition instead of the namespace keyword. You're right in that it basically is a Singleton, which means it is a global, which means it may or may not offend you depending on how you feel about globals. You've just stuck the instance in the hands of the compiler instead of your own hands. Which is why most reasonable singleton implementation contribute some kind of creation/descruction policy to the mix.

A monostate has static state in its implementation but is completely non-static in its interface (or at least non-static in the way a traditional non-singleton class would be non-static). Thus, the advantage of a monostate is that the client code simply sees a class it can instantiate and use without concern for whether or not only a single one exists in the system at a time.

Now, I personally prefer a monostate in most situations where people recommend singletons. It at least frees any interfaces that depend on it from having knowledge about whether it's a monostate or not. The only place you normally have to re-think things if you un-monostate it into a regular class is in creation/destruction code, which is more often than not pretty minimal (and easy to find).

If you use a "classic" singleton implementation with GetPtr(), etc., and want to transform it into a non-singleton class, you'll clearly have to change all access points to the class (since none of them will be referring to instances).

The problem I have with the classic singleton approach is that it makes "single instance" a property of the class interface, thus all clients but be aware of it. This may or may not be a good thing depending on the scenario. You just need to make sure you're thinking of it that way.

A monostate, on the other hand, makes "single instance" a property of the class implementation, thus clients can be completely oblivious to the fact. Again, that may or may not be appropriate to your scenario. I personally think it's more appropriate more of the time than singleton, but some disagree.

Whenever I've got the feeling that some kind of singleton solution is called for, I try to design away from it if I can. I first prefer to pass objects to other objects; if a given object isn't responsible for the construction/destruction of a specific object, it should expect to have it passed in at some point and be notified when it's no longer valid. When I find there are too many of these objects I want to provide, I simply create a container object, usually dubbed a Manager, that I pass around.

If it makes sense, I'll even provide a global point of access for a Manager, but I don't see any reason to make it a singleton; that's just using the type system as an "instance" namespace, which is not its intended purpose. Technically there's no drawbacks to doing that, but then again there are no drawbacks to simply having a global function that does the equivalent to Singleton::GetPtr()... the results are identical with identical flexibility.

So, resist singleton if you can, be aware of monostate, be aware of the decision you're making when using a singleton, and ask yourself "what am I gaining by using a singleton beyond namespacing?"

Share this post


Link to post
Share on other sites
Quote:
Original post by Prototype
I've used monostates many times, but it always turns out that you need to add Init() and Destroy() functions to set it up, so you find yourself emulating OO which just doesn't make sense.


I don't think I quite follow... why are you having to create Init() and Destroy() functions? The advantage of a monostate is that its an instantiatable class with a single (mono-) state shared amongst all instances. When the first instance is created (through a constructor) you simply init your state then. When the last instance is destroyed (through the destructor, probably tracked with reference counting) is destroy your state (if needed).

From the other two posts in this thread, it sounds as if people have a misunderstanding of monostates...

Share this post


Link to post
Share on other sites
Quote:
Original post by Simagery
Quote:
Original post by Prototype
I've used monostates many times, but it always turns out that you need to add Init() and Destroy() functions to set it up, so you find yourself emulating OO which just doesn't make sense.


I don't think I quite follow... why are you having to create Init() and Destroy() functions? The advantage of a monostate is that its an instantiatable class with a single (mono-) state shared amongst all instances. When the first instance is created (through a constructor) you simply init your state then. When the last instance is destroyed (through the destructor, probably tracked with reference counting) is destroy your state (if needed).

From the other two posts in this thread, it sounds as if people have a misunderstanding of monostates...


Having global variables locked up in a class that is a singleton can have it's benefits. Firstly if you go global. the ide gives you all the possible globals and secondly you can use a function that saves/loads classes directly and thirdly you can duplicate states.
Ofcourse monostate means all states are the same, in which case the last advantage is pointless.

Share this post


Link to post
Share on other sites
The real question, is why does it have to have global scope in the first place?

Singletons, monostates and ordinary globals are all usually bad ideas that can and should be avoided. (Note, I said usually, not always)

Share this post


Link to post
Share on other sites
Quote:
Original post by Simagery
I don't think I quite follow... why are you having to create Init() and Destroy() functions? The advantage of a monostate is that its an instantiatable class with a single (mono-) state shared amongst all instances. When the first instance is created (through a constructor) you simply init your state then. When the last instance is destroyed (through the destructor, probably tracked with reference counting) is destroy your state (if needed).

From the other two posts in this thread, it sounds as if people have a misunderstanding of monostates...

I always thought the point of monostate was that you don't create any objects at all, and hence don't provide any constructors. But correct me if I'm wrong, I'm never good at formal matters.

EDIT: it seems you are right, I do need to read up on Design Patterns.
Excuse my error. (post removed)

[Edited by - Prototype on June 8, 2006 5:11:46 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Simagery

Whenever I've got the feeling that some kind of singleton solution is called for, I try to design away from it if I can. I first prefer to pass objects to other objects; if a given object isn't responsible for the construction/destruction of a specific object, it should expect to have it passed in at some point and be notified when it's no longer valid. When I find there are too many of these objects I want to provide, I simply create a container object, usually dubbed a Manager, that I pass around.


I created a Manager too, to solve the too-much-passing problem. I happened to make it Singleton. But why would you pass around a manager?

Quote:
Original post by Simagery
If it makes sense, I'll even provide a global point of access for a Manager, but I don't see any reason to make it a singleton; that's just using the type system as an "instance" namespace, which is not its intended purpose. Technically there's no drawbacks to doing that, but then again there are no drawbacks to simply having a global function that does the equivalent to Singleton::GetPtr()... the results are identical with identical flexibility.


From this thread, I think I like the idea of Singletons better (than monostates) because if you never use them, you never waste any space on the heap or the stack. That said, snigletons are probably slower and cause slight but negligible fragmentation (from heap storage).

Quote:
Original post by Spoonbender
The real question, is why does it have to have global scope in the first place?

Singletons, monostates and ordinary globals are all usually bad ideas that can and should be avoided. (Note, I said usually, not always)


Well, I always run into the problem of passing many things down a chain of classes to where I need them. It seems that there are just some things that I need all over the place ( D3D device :) ).

Share this post


Link to post
Share on other sites
Quote:
Original post by discman1028
I created a Manager too, to solve the too-much-passing problem. I happened to make it Singleton. But why would you pass around a manager?


To break the dependancy between the code which has the manager passed to it and the manager's allocation method (singleton). This way, only the caller/owner needs to be modified, rather than both the caller/owner and the callee, if you suddenly want the option to pick between multiple managers for whatever reason. An IPv4 listener and IPv6 listener at the same time, say.

The strong point for direct use of singletons (via GetInstance()) is where the caller/owner needn't have any knowledge, ever, of the manager to be used - in other words, most likely, you know you won't need more than 1 or some small specially hardcoded value of N (like, say, 2). The "specially hardcoded N" bit is easier if you have a layer of seperation from the allocation and use, so your handshake implementation dosn't need a rewrite just to work with IPv4 and IPv6 for example.

I hate making such assumptions though. Plus, by avoiding global storage one is required by necessity to make all the links between components, helping reveal relationships between systems, as well as encouraging placing them closer together, instead of seperated by any extra layers of roundabout cruft you might tack on by accident.

Share this post


Link to post
Share on other sites
Quote:
Original post by MaulingMonkey
...by avoiding global storage one is required by necessity to make all the links between components, helping reveal relationships between systems, as well as encouraging placing them closer together, instead of seperated by any extra layers of roundabout cruft you might tack on by accident.


That's the best reason I've ever heard for avoiding global storage (and specifically singletons). That perfectly sums up the feeling I was trying to capture in my earlier post. I'm going to use that one!

Share this post


Link to post
Share on other sites
Quote:
Original post by dawidjoubert
Having global variables locked up in a class that is a singleton can have it's benefits. Firstly if you go global. the ide gives you all the possible globals and secondly you can use a function that saves/loads classes directly and thirdly you can duplicate states.
Ofcourse monostate means all states are the same, in which case the last advantage is pointless.


Using a singleton or a monostate (or any other pattern) is a design choice and has nothing to do with your IDE. Your IDE's purpose is to ease development, not to enforce design-specific choices :)

What do you mean by "you can use a function that saves/loads classes directly" and "you can duplicate states"?

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Two problems inherant in singleton and static use:

1) Initialisation order issues.
2) Class interface mangling due to implemention being required to support it.

Generic problems it is meant to fix:

1) Class instances managed and possibly pooled, even if from a pool of 1 :)
2) No passing of widely used, deeply used, or common functionality.


My solution:
1) Fixes most initialisation order issues. Only impossible cyclic dependancies cannot be handled.
2) No class mangling. Project classes require no messing to fit.
3) Any form of instance management is doable. All [heap] construction and destruct done in factory class. Indeed it allows benchmarking and tweaking of class specific creation and destruction.
4) No need to pass widely used, deeply used, or common functionality on stack.




// toolbox classes

// Factory template classes ( all with identical interface Create(s), Release(s) )
template< typename yT > PoolFactory;
template< typename yT > SingleSharedFactory;
template< typename yT > SingleLockedFactory;
template< typename yT > DefaultFactory;

// template that takes an above factory class and counts references
template< typename yT > StaticFactory
{
// fake pass through factory interface that creates factory on first use and deletes on dead ref count. refcounting done in constructor/destructor.
static yT* pFactory;
static unsigned int uiCount;
}


// project header
class D3DRenderer;
typedef StaticFactory< SingleSharedFactory< D3DRenderer > > RendererFactory;

// closed code file
RendererFactory gRendererFactory;
D3DRenderer* RendererFactory::pFactory =NULL;
D3DRenderer* RendererFactory::uiCount =NULL;


// use
void AClass::SomeFunc( arg )
{
gRendererFactory Factory;
D3DRenderer *pRender( Factory.Create() );
// ...stuff...
Factory.Release( pRender );
}




I actually wrap the pointer use in another class that automatically uses the factory functions so I dont need to bother remembering to Create/Release and allows exception safe use.

This is basically to show the idea.

Share this post


Link to post
Share on other sites
It seems like there's no avoiding the statisticity (?) of the situation, no matter what approach is taken. The factory method is interesting though.


Quote:
Original post by Anonymous Poster
1) Initialisation order issues.


Good singleton implementations don't have this problem... have a static pointer instead of static class instance.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this