Singleton

From APIDesign

(Difference between revisions)
Jump to: navigation, search
(Where Have All the Singletons Gone[http://misko.hevery.com/2008/08/21/where-have-all-the-singletons-gone/])
(Where Have All the Singletons Gone[http://misko.hevery.com/2008/08/21/where-have-all-the-singletons-gone/])
Line 57: Line 57:
The title is appropriate only in context of [[dependency injection]]. The [[Injected Singleton|alternative approach]] that we are about to analyze still sticks with its [[singleton]]s. That is why we cannot investigate where did they go - they are still with us. We are just going to verify that several laudable goals are still reached and under which conditions.
The title is appropriate only in context of [[dependency injection]]. The [[Injected Singleton|alternative approach]] that we are about to analyze still sticks with its [[singleton]]s. That is why we cannot investigate where did they go - they are still with us. We are just going to verify that several laudable goals are still reached and under which conditions.
-
First of all it is easy to ''don't mix object construction and application logic''. Instead of constructing objects, one just calls the [[singleton]]s getter and gets properly initialized instance of the requested interface. Thus one codes just the application logic, the object construction is handled behind the scene by the [[Lookup|infrastructure]].
+
First of all it is easy to ''don't mix object construction and application logic''. Instead of constructing objects, one just calls the [[singleton]]'s getter and gets properly initialized instance of the requested interface. Thus one codes just the application logic, the object construction is handled behind the scene by the [[Lookup|infrastructure]].
It is easy to ''rarely call new operation'' - one does not instantiate services, those are offered via various [[API]] singletons. Enough to get them. However, there is an important limitation: In the server terminology one would say that in such system, there is only one ''application factory'', that there are no ''request factories''. The application factory is determined by the classes loaded into the system. All the classes linked together represent one factory (they can have many different [[singleton]]s, but they are ''all or nothing'' set). If I wanted to have different set of singleton values, I would need to load the classes by different classloader again (see [[co-existence]] for more details).
It is easy to ''rarely call new operation'' - one does not instantiate services, those are offered via various [[API]] singletons. Enough to get them. However, there is an important limitation: In the server terminology one would say that in such system, there is only one ''application factory'', that there are no ''request factories''. The application factory is determined by the classes loaded into the system. All the classes linked together represent one factory (they can have many different [[singleton]]s, but they are ''all or nothing'' set). If I wanted to have different set of singleton values, I would need to load the classes by different classloader again (see [[co-existence]] for more details).

Revision as of 03:50, 25 January 2010

Among other things the Chapter 7 also introduces the NetBeans pattern for doing Component Injection. The claims there are not fully aligned with common know-how of developers that use Dependency Injection. The most surprising thing is that NetBeans APIs commonly contain singletons (and yet there are no problems or design issues and testability is supported). It is quite common to see static methods like:

public abstract class WindowSystem {
  public static WindowSystem getDefault() { /* some impl */ }
}

This is something a Dependency Injection fan would have never done. Recently Witold Szczerba shared a very interesting observation on our mailing list[1]:

Now I feel totally lost. Jaroslav Tulach and Miško Hevery ...
proclaim totally contradictory theories
about the bases for building applications. Or they not? I am still
trying to figure it out. In Chapter 7: "Use modular architecture",
Jaroslav describes the concept of generic registry, which is #2 on a
list of  "Top 10 things which make your code hard to test"[2]... Do
we have two mutually exclusive yet both correct theories?

This page is my attempt to explain this paradox. It may not be complete yet, I am still on the hunt for the little differences in initial assumptions that produce such contrary suggestions. But one day we'll get them...

I started by reading the Miško's articles references by Witold. Let's thus use them as headers for now.

Contents

Singletons are Pathological Liars[3]

It is easy to agree that the sample given here shows really horrible style of programming. Nobody who wants to code in modular way can have uninitialized objects floating all over the system waiting for some proper call to initialize them:

Database.init();
OfflineQueue.init();
CreditCardProcessor.init();
CreditCard c =  new CreditCard(
  "1234 5678 9012 3456", 5, 2008
);
c.charge(100);

The fact that in order to charge a credit card one needs to initialize various subsystems needs to be made more visible otherwise proper use and maintenance of such system requires non-trivial amount of knowledge and goes directly against the principle of cluelessness.

Miško's philippic is targeted against singletons, however I feel the problem is rather in existence and usage of uninitialized objects. Yes, it is easier to let uninitialized singletons escape to foreign code, however this can be simulated with regular objects as well. As such I am going to slightly shift focus and complain about uninitialized objects, potentially finding ways to use singletons so they are properly initialized.

As an executive overview, let me say that there seem to be two approaches to prevent uninitialized objects:

  • Dependency Injection's principle is to always create objects with all their necessary environment - e.g. it is not possible to create new instance of an object without providing all services it needs. This style almost eliminates singletons.
  • Injected Singletons on the other hand solve the uninitialized objects approach by making sure singletons are properly initialized as soon as their defining class is linked together.

Obviously both of these approaches exorcise the above nightmare requiring various init class to set up your application.

Where Have All the Singletons Gone[4]

The title is appropriate only in context of dependency injection. The alternative approach that we are about to analyze still sticks with its singletons. That is why we cannot investigate where did they go - they are still with us. We are just going to verify that several laudable goals are still reached and under which conditions.

First of all it is easy to don't mix object construction and application logic. Instead of constructing objects, one just calls the singleton's getter and gets properly initialized instance of the requested interface. Thus one codes just the application logic, the object construction is handled behind the scene by the infrastructure.

It is easy to rarely call new operation - one does not instantiate services, those are offered via various API singletons. Enough to get them. However, there is an important limitation: In the server terminology one would say that in such system, there is only one application factory, that there are no request factories. The application factory is determined by the classes loaded into the system. All the classes linked together represent one factory (they can have many different singletons, but they are all or nothing set). If I wanted to have different set of singleton values, I would need to load the classes by different classloader again (see co-existence for more details).

The fact that the singleton approach supports just application factory shows the benefits of using true dependency injection, on the other hand it is not limiting by itself. For many applications (especially on desktop) it is fine to have just application factories. There is only one help system, one dialog presenter, one VGA card[5], etc. Thus this limitation does not violate any good design practices. Using singletons in this way is completely fine.

Root Cause of Singletons[6]

  • private constructor - not the Nb case
  • immutable singleton is OK. If immutable == not configurable, then everything is OK.
  • add story about main window and why NetBeans have just one - again levels of co-existence
  • global state is bad. why? It does not seem that bad in desktop applications...
  • compare. DI suggests to express dependency in constructor. Nb suggests to always provide dummy impl + allow drop in registration (classpath or mock one).
  • mention testing limitations due to levels of [Co-existence]] and solutions

Top 10 things which make your code hard to test[7]

  • test is small instance of the app
  • Lookup.getDefault() is seam.
  • inherit vs. composition
  • switch vs. polymorphism
  • value objects vs. service objects

Conclusions

Personal tools
buy