singletons vs monostates

Started by
15 comments, last by MaulingMonkey 18 years ago
We had one of those never ending "singletons are evil" discussions in #gamedev last night, in which I argued in favour of the use of a singleton for the logger class which handles debug output, warnings, errors, etc. I want one, I only want one, Having two would be detrimental to its purpose, I want it everywhere, and I want it everywhere for a good reason, not because of a poor design. Washu, of course, pounced upon the term "singleton" like a rabid mongoose, and then asked why I didn't use a monostate. 15 minutes googling later, I feel I have a good grasp of what people think a monostate is. I also have no idea why it is in anyway preferable to a singleton in this case. I'm not trying to start a flamewar, I'm just asking for an explanation of why I would want to use a monostate in this case. One page went so far as to claim order of destruction was a problem with singletons, and then went on to completely fail to explain how a monostate improved on that, so my best guess so far is some sort of reference counting? [edit: please don't bother to reply with an "I love singletons" post - that's not the question I'm asking ;)]
Advertisement
Beyond a certain point, spending time trying to pick the perfect design gives negative benefit - ie, you spend more time trying to make the "right" decision than you gain by having made that decision.
This is one of those occasions.

Think about it: You are talking about a logging system. Unless your application is called Logger Pro 2006, Enterprise Edition, you should not be wasting time on this decision. Trying to decide between singletons, monostates, just having a global instance of a log class or just writing to plain old std::cout is not a useful way to spend your time - the differences between them, in terms of functionality, is minimal (with the possible exception of just writing to std::cout).

Pick one and then get on with something interesting.

John B
The best thing about the internet is the way people with no experience or qualifications can pretend to be completely superior to other people who have no experience or qualifications.
The question is not useful, because it arises from a bad mentality.

That mentality is that all operations should be done by objects, which is what most people mean by the term "object oriented." That mentality is unhealthy and leads down the Dark Path to languages like Java.

You should use object-aware programming instead of object oriented programming.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Quote:Original post by JohnBSmall
Think about it: You are talking about a logging system. Unless your application is called Logger Pro 2006, Enterprise Edition, you should not be wasting time on this decision.


It's an issue if it comes up again in the future, so it's not necessarily a waste of time. It's like saying it's not worth my time to find out what caused a bug that mysteriously "fixed itself". If this is a personal project, understanding and experience are probably more important than the final product.

That said, singletons are nice in that they initialize all their internals like any ohter object. Monostates are basically just a bunch of static functions. Singletons are slightly better IMHO (when you really need them) because, at least in the case of the Meyers Singleton, you can guarantee the object will be there when you first call for it, even if that is before main(). That's got me out of a few jams before. YOu can also inherit them if you do it right.
Quote:Original post by leiavoia
Quote:Original post by JohnBSmall
Think about it: You are talking about a logging system. Unless your application is called Logger Pro 2006, Enterprise Edition, you should not be wasting time on this decision.


It's an issue if it comes up again in the future, so it's not necessarily a waste of time. It's like saying it's not worth my time to find out what caused a bug that mysteriously "fixed itself". If this is a personal project, understanding and experience are probably more important than the final product.

That said, singletons are nice in that they initialize all their internals like any ohter object. Monostates are basically just a bunch of static functions. Singletons are slightly better IMHO (when you really need them) because, at least in the case of the Meyers Singleton, you can guarantee the object will be there when you first call for it, even if that is before main(). That's got me out of a few jams before. YOu can also inherit them if you do it right.


Quote:Original post by Squirm
One page went so far as to claim order of destruction was a problem with singletons, and then went on to completely fail to explain how a monostate improved on that, so my best guess so far is some sort of reference counting?

Both can handle order of construction and destruction properly in C++, so that's not really an issue. With a singleton, the easiest way to accomplish this is by having your access point return a reference-counted smart-pointer to the singleton as opposed to a raw pointer, and with a monostate you just have the reference counting performed in the constructor and destructor of the type itself. That said, I will say that monostates are more natural to many.


[edit: please don't bother to reply with an "I love singletons" post - that's not the question I'm asking ;)]



Quote:Original post by leiavoia
Quote:Original post by JohnBSmall
Think about it: You are talking about a logging system. Unless your application is called Logger Pro 2006, Enterprise Edition, you should not be wasting time on this decision.

It's an issue if it comes up again in the future, so it's not necessarily a waste of time. It's like saying it's not worth my time to find out what caused a bug that mysteriously "fixed itself". If this is a personal project, understanding and experience are probably more important than the final product.

The two situations (deciding whether to use a singleton or a monostate, versus finding and fixing a bug) are not analogous.
If a bug mysteriously "fixes itself", there is a fairly high probability that the bug is still present in your program, and is simply being masked by the change you made. There is also a very high cost if the bug is still present in the code. Therefore, searching for the bug so that you can fix it properly is a worthwhile activity - the risk if you just assume it's been fixed is greater than the cost of the time you will spend to find it.

The risk associated with choosing a monostate where you later find you want a singleton (or vice versa) is much, much less, and therefore not worth spending as much time on.
If you later find you really have to reverse your decision, then you can spend time fixing it. Refactoring code is not such a big problem that it outweighs the benefits of being able to get on with the rest of your program.

Also, if this a personal project, you'll learn a lot more by just going ahead and implementing one system, and then taking note of what parts of that system work well for your situation, and what parts work badly than you would ever learn by scratching your head for five hours to try to decide whether a monostate is a better choice than a singleton.

John B
The best thing about the internet is the way people with no experience or qualifications can pretend to be completely superior to other people who have no experience or qualifications.
I never really understood why there is such an argument in the first place. I've never ran into a situation where... "I need to use x's logger class. I'll just Logger * logger = new Logger(...) and hope everything works out." That is not how C/C++ work. You can't just hope everything works out. You have to understand what is going on. This is chess, not checkers. If you are going to use x's logger class, learn how to use it first or ask x for help. To prevent someone from destroying the integrity of the logger class, set up asserts. Assert that the file opened correctly. I assert things left and right, and when something breaks, I can easily find out why.

I never liked the idea of singletons (and monostates now that I know what they are, globals). Not that I'm against globals, its just that you have to remember that everything in C++ is converted to C, then assembly before it is run. When you call class->method(param), your compiler calls method(class, param). Its not magic. If you want to get really technical, method knows the first parameter is passed via the ECX register (I suppose that is a little magical if have never used assembly).

So, I agree with JohnBSmall. If you want to make a singleton/monostate, you can. You can do all the checks you want to make sure people aren't using your class incorrectly. But in the end, it won't really matter. Just make the call, code it up and be done with it.
-------Harmotion - Free 1v1 top-down shooter!Double Jump StudiosBlog
Quote:Original post by leiavoia
That said, singletons are nice in that they initialize all their internals like any ohter object. Monostates are basically just a bunch of static functions. Singletons are slightly better IMHO.

Not necessarily. Monostate just have all instances of the type share the same global state. This could mean that you make all member functions static, but in my opinion, that defeats one of the main purposes of using a singleton or monostate -- to more easily make the shared state an implementation detail, as it generally should be.

Quote:Original post by Squirm
One page went so far as to claim order of destruction was a problem with singletons, and then went on to completely fail to explain how a monostate improved on that, so my best guess so far is some sort of reference counting?

Both can handle order of construction and destruction properly in C++, so that's not really an issue (as a side note, using them for the sole reason being to solve this problem is a misuse). With a singleton, the easiest way to accomplish correct construction and destruction order is to perform reference counting using a separate counter per translation unit handled via the constructor and destructor of types in an unnamed namespace (though this will not cover all cases, it will cover most), or by having your access point return a reference-counted smart-pointer to the singleton as opposed to a raw pointer. With a monostate, you just have the reference counting performed in the constructor and destructor of the type itself. With that, I will say that monostates are generally simpler for users to work with as you can pass around instances of the type, allocate them with new and delete easily, and do just about anything with them that you can with regular types, without having a logical extra level of indirection that you would with a singleton.

Logically, both a monostate and singleton represent very similar concepts, and many people actually do refer to them as the same design pattern as they both provide a way of working with a single, global state. The main logical difference between the two is that a singleton represents an access point to a single instance of an object, whereas a monostate allows you to directly represent multiple objects which all just share the same state. I would go so far as to say that the latter is almost always preferably to the former as it is easier to make working with the object more consistent with the rest of the language, and therefore also more easily useable in generic code.

Rather than repeating my same posts from previous threads (this topic comes up a lot on these forums), you can read my replies from a similar thread from last year. I talk more about monostates and compare them with singletons starting in the second reply:

First reply

Second reply

Third reply
Quote:Original post by ApochPiQ
That mentality is that all operations should be done by objects, which is what most people mean by the term "object oriented." That mentality is unhealthy and leads down the Dark Path to languages like Java.


There is nothing wrong with Java. In many ways, Java is much better for development and debugging.

If you're talking about raw performance, as always that depends on the task at hand. Generalizing by saying that Java is slower than C++ is only technically true, not in reality.

When saying that Java takes away control from the programmer, that too is true but for 80% of the programmers, that's probably a good thing.

No dark path and no nothing. It all depends on your requirements.

-----------------------------He moves in space with minimum waste and maximum joyGalactic Conflict demo reel -http://www.youtube.com/watch?v=hh8z5jdpfXY
Quote:Original post by blaze02
Not that I'm against globals, its just that you have to remember that everything in C++ is converted to C, then assembly before it is run.

No.

Quote:When you call class->method(param), your compiler calls method(class, param).

No.

Quote:If you want to get really technical, method knows the first parameter is passed via the ECX register (I suppose that is a little magical if have never used assembly).

Implementation defined, so No.

I recommend you invest in The Design and Evolution of C++. Or simply stop spouting inaccuracies based on pre-Standard information.

This topic is closed to new replies.

Advertisement