Why shouldn’t we use Singleton?

Asked

Viewed 4,535 times

75

You finds on the internet the claim that Singletons are bad. Is this true? Why?

Would the problem be generalized to any objects with single instance? Or to anything that has been global?

If it is so bad, why was it invented? That is, what problem he wanted to solve?

What problems will I have if I use it?

There is a viable alternative?

  • Where did you see it?

  • 8

    A simple Google search finds so many fonts that I wouldn’t know where to start: http://stackoverflow.com/questions/1020312/are-singletons-really-that-bad, http://blogs.msdn.com/b/scottdensmore/archive/2004/05/25/140827.aspx, http://www.codeproject.com/Articles/634723/Singletons-Why-Are-They-Bad, http://molecularmusings.wordpress.com/2011/11/singleton-is-an-anti-pattern/, http://blog.code-cop.org/2012/why-singletons-are-evil.html, http://c2.com/cgi/wiki?SingletonsreEvil, http://accu.org/index.php/journals/337, http://tech.puredanger.com/2007/07/03/pattern-hate-singleton/

  • 4

    Um... I didn’t know about all this hatred. I find a Pattern so good and useful because Spring implements it all the time. Unfortunately there are people who do not know how to use the Pattern, and then comes out affirming this lot. =/

  • So the question is here to try to reverse this as objectively as possible :)

  • 3

    This question is being discussed at http://meta.pt.stackoverflow.com/q/1437/101

  • 3

    I honestly didn’t think the question was wrong to be here, but I was surprised at Singleton’s hatred. [=

  • 4

    Hate to Singleton is like hate to a screwdriver. It’s just another tool. Misuse does hateful things, but it’s the user’s fault.

Show 2 more comments

3 answers

75


TD;DL

A lot of the controversy occurs due to the lack of a contextualization. I don’t see anything that unbuttons the Sigleton pattern when used in the correct context.

Yes, it can cause a lot of harm, just like any improperly applied design pattern without impact analysis and side effects.

Why not use

Coupling

When using a Singleton you are attaching the code to a specific static implementation. This makes your code dependent on this class and prevents, for example, creating mocks in unit tests.

From this point of view the consequence is basically the same as making a direct new MeuObjetoImportante(), because it is a direct dependency of the class.

Scope

The Singleton standard applied according to the definition also eliminates the concept of scope. This means that if you for any reason decide that for certain application components you need another implementation you will have to manually change all classes.

No guarantee of a single body

In certain cases the pattern may lead to false security that there will be only one instance.

Let’s assume that you design a web system and want to give access to a unique file or resource. A Singleton sounds like good practice, doesn’t it? But what if tomorrow you do the deploy of the application in a cluster with N servers.

In the case of Java this is also very complicated, because there is no such thing as one class per JVM. The correct concept is a class per ClassLoader, so that on the same JEE server two different applications can each have their own version of the same class. Static variables are not shared between these versions of the same class, so the scope of Singleton in Java is by ClassLoader and not by program.

Why use

In some situations you know you will only have one instance of the class.

In a Desktop application, for example, there is no reason to have additional "frills" to get a connection to the database.

I also see no problems in an object encapsulating some global configuration attributes.

It simply wouldn’t be worth the effort to implement some very elaborate mechanism for something so simple.

How to use

When we think of Singleton, then the classic implementation of Gang of Four come to mind. Previous topics of this answer considered this.

However, with the rise of the use of Control Inversion (Ioc) and Dependency Injection (DI) frameworks, there is now a much more "secure" and flexible way to implement the standard: let the framework take care of it for you!

How to avoid Singleton

Although simpler implementations can use the Singleton standard in the "classical" way without problems, in general it would be better to apply Control Inversion simply to let the instance of its dependencies be injected.

That way, if tomorrow the requirements change and you need to inject different instances, or even want to create mocks test, just set up your container or framework without changing existing implementations.

26

Singletons are compulsive liars

Well, you’ve just joined a new project, which already has a mature and very extensive base code. Your new boss asks you to implement a new feature and, as a good developer, you start by writing a test. But since you’re new to the project, you do a lot of exploratory tests like "what happens if I run this method?". You start by writing the following code:

    public function testCreditCardCharge() {

        $cc = new CreditCard( '1234 5678 9012 3456', 5, 2013 );
        
        $cc -> charge( 100 );
    }

This code:

  • It only works when you turn it as part of the whole system, never in isolation
  • When we try to run in isolation, an exception is thrown
  • When you receive your card bill, you realize that you had to pay $100 for each test run (TENSO!)

Now I want to focus on the last item. How did a simple test cause a real charge on my credit card? Charging a credit card is not easy. The test needs to talk to a web-service credit card (third party), need to know the URL to the web-service, need to authenticate, pass credentials and identify in which business I am buying...

None of this information was present in the test. Even worse, as I don’t even know where this information is, how would I "fool" the external dependencies (through a Mock Object) so that I don’t lose $100 every time the test runs? As you are new to the project, how could you know that what the test performed would make you $ 100,00 poorer? That sounds like a ghost story.

But why do I see an exception occurring when running the program in isolation, but everything works when I test the class in conjunction with the system? And how do I fix that? Tired of searching for thousands of lines of code, you decide to go ask the developers who have been in the project the longest. After much digging, you learn that you need to initialize the Creditcardprocessor.

    public function testCreditCardCharge() {

        CreditCardProcessor::init();

        $cc = new CreditCard( '1234 5678 9012 3456', 5, 2013 );
        
        $cc -> charge( 100 );
    }

You run the test again, still unsuccessful and you still come across another type of exception. Again, you will consult your colleagues. Someone tells you that Creditcardprocessor needs a Offlinequeue to work.

    public function testCreditCardCharge() {

        OfflineQueue::init();

        CreditCardProcessor::init();

        $cc = new CreditCard( '1234 5678 9012 3456', 5, 2013 );
        
        $cc -> charge( 100 );
    }

Excited, you run the test again. Another different exception. A little more "digging" and you find that you also need to initialize the connection to the database.

    public function testCreditCardCharge() {

        Database::init();

        OfflineQueue::init();

        CreditCardProcessor::init();

        $cc = new CreditCard( '1234 5678 9012 3456', 5, 2013 );
        
        $cc -> charge( 100 );
    }

Finally the test runs in isolation mode, but again, you just lost another $100.00.

The problem is that existing Apis are compulsive liars. Creditcard pretends that you can only instantiate it and call the method Creditcard::Credit card(), but secretly, she collaborates with Creditcardprocessor.

The API of Creditcardprocessor says it can be initialized in isolation, but in fact, it needs Offlinequeue, which, in turn, needs to Database.

For the developers who wrote the code, it’s obvious that Creditcard needs Creditcardprocessor, after all, they wrote the code. But for anyone new to the project, this is a total mystery and makes the learning curve difficult.

But it’s not over yet! Suppose a colleague who joined you in the project decides to take a look at its implementation. From what he can tell, the three initiations and instantiation are independent, that is, they can happen anywhere. During some refactoring or code cleaning, maybe he rearranges the lines of code that way:

    public function testCreditCardCharge() {

        CreditCardProcessor::init();

        OfflineQueue::init();

        $cc = new CreditCard( '1234 5678 9012 3456', 5, 2013 );
        
        $cc -> charge( 100 );

        Database::init();
    }

The code simply stopped working, but my colleague would have no way of knowing, or would have?

In this simple example, until it is not difficult to see this, but in a real project, startup can usually occur for several classes and you must initialize hundreds of objects. The exact initialization order will become a mystery.

How do we fix this? With Apis that declare dependency!

    public  function testCreditCardCharge() {
        
        $db = new Database;

        $queue = new OfflineQueue( $db );

        $processor = new CreditCardProcessor( $queue );

        $cc =  new CreditCard( "1234 5678 9012 3456", 5, 2008 );

        $cc -> charge( $processor, 100 );
    }

Since the method of Creditcard declares that he needs a Creditcardprocessor I don’t need to ask anyone about it. The code just won’t run without it.

I have a very clear tip that I need to instantiate Creditcardprocessor. When I try to instantiate Creditcardprocessor I come across need to provide a Offlinequeue.

In continuity, when I try to instantiate the Offlinequeue, I’m gonna need a Database to work.

The instantiation order is clear! Not only is it clear but it is impossible to reverse the odem of the declarations but the code does not execute.

And the best of the benefits, of course, is that every time you run the test you won’t have $100 charged on your card.

Singletons are nothing more than global states. Global state allows its objects to secretly keep things undeclared in their Apis and, as a result, Singletons make their Apis pathological liars.

Think differently. You can live in a society where everyone (all classes) declares who their friends are (collaborators). If I know that José knows Maria but that neither Maria nor José know João, then it is safe for me to assume that if I say something to José he can comment with Maria, but under no circumstances will João know anything.

Now imagine that everyone (all classes) declares some of their friends (collaborators), but other friends (collaborators who are Singletons) are kept confidential. Now you wonder how John got to know that information you gave to José.

And here’s the interesting part. If you are the person who originally built the relationships (code), you know the true dependencies, but any others who come after you are perplexed since the friends you stated are not the only friends of the objects, and the information flows through secret paths that are not clear to you. You live in a society of liars.

Original Article: Singleton are Pathological Liars

Partial Translation: Henrique Barcelos

  • 1

    In short, are you saying that it is true that the Singletons are always bad? There is no proper use of them?

  • 5

    Very interesting article. The main idea is: Singleton is bad because he stays hidden in the classes they use. But, as I have already mentioned in my reply, a simple new causes the same effect. On the other hand, encapsulation is one of OO’s great assets. What to do? I think the whole question lies in the choice of the "architect" of what or not to hide from the "user" in your API and not in the default itself. All this taking away the issue of creating proper documentation.

  • 3

    @utluiz; I don’t know about the other languages but the newPHP creates a new instance of the object and, unless the object information is not defined in static properties, it will be different between its instances.

  • 2

    @bfavaretto: Singleton is good for perpetuating instances of objects and therefore is the basis of a Registry. It is also good for situations where there really should only be a single occurrence of a certain resource or scenario, such as Sessions. The "Singletonite", disease caused by the excessive use of Singleton was the one who gave the pattern a bad name because it started to be used indiscriminately replacing global variables or globalization of scope.

  • 3

    @Brunoaugusto Yes, yes. It should not change the concept of instantiation of objects. The problem is that a new can also hide a created object and possible initialized values. Although it is not the same instance, it is also depending on something undeclared. A now invented example: new Configuracao("/minha/configuracao.conf").getValorConfigurado(). This spread across several classes is equivalent (though worse) to a Singleton: Configuracao.getInstance().getValorConfigurado().

  • 1

    I think I still do not understand your point of view, perhaps by the example of configuration (which I think should not allow rewriting en Runtime), but also for differences between languages. With PHP I can imagine a scenario close to what you refer to if a "normal" object uses multiple Singletons, either in its constructor or in some invoked method.

  • 1

    The answer is very good and the question about the order of execution was very well asked. But, isn’t there a conceptual problem right from the start in the example used? Doesn’t it make sense to have the demeanor (that is, the method) charge in the class of the card. This is a behavior of another entity (maybe bank, which seems to me to be what you call professor...), and this is noted by the dependencies themselves. So keep it in the card class also induces the confusions mentioned. That is, it would naturally be more intuitive something like banco->charge(cartão, valor)...

  • Thus, it would be quite clear by the instantiation of banco whether you were going to have 100 real debited actually or not. :)

  • 1

    Whoever you were, thank you for the negative on an article that I didn’t even write <_<

  • 2

    I noticed that it was not you who wrote it but, as you know, what you publish as a response is subject to positive and negative votes. I voted negative because the question is about Ingleton and the article talks about something else: it talks about high coupling and hidden dependencies, which you can get even without singletons and from which you can escape even using singletons. I did not leave comment initially because this has already been demonstrated by @utluiz and Luizvieira.

Show 5 more comments

4

In general, the Singleton standard is generally not recommended because it is used in a manner wrong.

Many devs end up using singletons in classes that are used in several places and/or often, because, instead of throwing this object back and forth, through builders and/ or methods, they prefer to create a Singleton and call him whenever you need. Doing so makes code cleaner and easier to implement, but is wrong.

This goes against the motivation of the pattern, in addition to allocating memory unnecessarily. The motivation of Singleton is clear: provide a global access point and a single instance to a class.

People end up interpreting this as: how I’m going to use this class a lot so I’m going to create a global access point. But in this case, it’s not a need have this global access point, and yes a convenience. The devs have to stop being lazy and pass the instance through the constructors and methods, yes.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.