Christopher Smith on 19 Jun 2003 01:36:02 -0000


[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

Re: [ALACPP] Singleton clarification


On Wed, 2003-06-18 at 16:09, Jon Stewart wrote:
> > 1) The "only one instance" notion almost always needs to be qualified,
> > and people tend to forget to do that. So, perhaps you have it's
> > one-per-object, one-per-thread, one-per-process, one-per-network
> > interface, one-per-system, one-per-subnet, etc.
> I think the overwhelming default is one-per-process. If it's anything 
> else, you should probably stick in an object representing its 
> qualification (network, thread, etc.).

Sadly, the overwhelming default is a "whatever is easiest to code". So
for example, in Java, it tends to be "one-per-class loader". This is
particularly annoying in J2EE environments.

> > 2) Singletons tend to make refactoring a tad difficult, because they
> > dramatically effect how navigation is done to the singleton. People
> > rarely use wrapper methods, so it's all short-circuited directly to a
> > static "getInstance()" method. So, if you have to refactor so that
> > access is contextual, it tends to be a significant pain.
> 
> I think there are two issues here, which are often lumped together but 
> need to be separated sometimes:
> 
> 1). Sometimes it's nice to have a type that can only have one instance.

Hmm... I think 90% of these cases it's more like "sometimes it's nice to
have a class that is a first-class object", and singleton's are the way
to do so in languages which don't have those facilities built in. In a
lot of cases, the *good* reasons why you want this are driven by #2.

> 2). Sometimes it's nice to access resources in every context, i.e., to 
> have context-free objects.

Hehe, in C they call them functions. ;-) I know what you mean though. A
key trick is recognizing that accessing a singleton through
"Foo::getFooInstance()" makes it much harder to tack on context later
than if you do "this->getFooInstance()" and then provide a convenience
method. When people use singleton's they frequently ignore this issue,
when in a lot of ways it's the most significant impact of working with a
singleton.

> Traditionally, singletons often have both properties. For logging, it 
> makes sense (everyone should be able to log); for keyboard access, well, 

Everyone should log, but such logging takes place in a very specific
context. log4cpp provides categories to capture that context, but
because the fundamental system is tied to a singleton this can be a
problem.

> it doesn't seem to make a lot of sense.

Hehe.. keyboards can be a wicked source of "singleton excitement". ;-)

> When it comes to controlling access, I've messed around with inheriting 
> access; it's good when you want decentralized access control. When you 
> want centralized access control, perhaps the simplest way is something 
> like this:
> 
> class KeyboardAccessor : private SingletonHolder< Keyboard > {
> 
> friend class InputReader;
> friend class EventManager;
> 
> };
> 
> This can be more sophisticated, to guard against the usual band of 
> pitfalls, but I like that it uses simple language constructs: access is 
> private, unless you're listed as a friend.

So, one of the things I've tried is to have a non-static method with a
static variable inside it:

class NotASingleton {
public:
    SingletonClass& getSingleton() {
        static SingletonClass instance;
        return instance;
    }
};

Since there is no way to globally navigate to a "NotASingleton"
instance, it tends to encourage people to access the singleton from some
kind of a context, while still ensuring one instance per-process (or one
instance per code module if NotASingleton is declared static ;-). Of
course programmers can still abuse this (for example creating new
instances of NotASingleton whenever they want to get the SingletonClass
instance), but usually the options are ugly enough that there is a fair
bit of deterrence.

> Well, log4cpp is not the most flexible and/or robust system in the world. 
> Great for debugging race conditions, lousy for making your bounds 
> checker-type utility shut up.

Hehe: I just tell the bounds checker/leak detector to ignore log4cpp
completely, along with the STL. If I get mad at valgrind I contemplate
adding logging to the STL. ;-)

--Chris
_______________________________________________
alacpp mailing list
alacpp@xxxxxxxxxxx
http://lists.ellipsis.cx/mailman/listinfo/alacpp