Domanda

Il modello singleton è un membro interamente pagato del GoF 's libro di modelli , ma ultimamente sembra piuttosto orfano dal mondo degli sviluppatori. Uso ancora un sacco di singoli, in particolare per classi di fabbrica , e mentre devi essere un un po 'attento ai problemi del multithreading (come qualsiasi classe in realtà), non riesco a capire perché siano così terribili.

Stack Overflow sembra soprattutto supporre che tutti siano d'accordo sul fatto che i Singleton siano cattivi. Perché?

Supporta le tue risposte con " fatti, riferimenti o competenze specifiche "

È stato utile?

Soluzione

Parafrasato da Brian Button:

  1. Sono generalmente utilizzati come istanza globale, perché è così male? Perché nascondi le dipendenze della tua applicazione nel tuo codice, invece di esporle attraverso le interfacce. Rendere globale qualcosa per evitare di passarlo in giro è un odore di codice .

  2. Violano il principio di responsabilità singola : in virtù del fatto che controllano propria creazione e ciclo di vita.

  3. Per loro natura intrinsecamente il codice è strettamente accoppiato . Ciò rende in molti casi falsificarli sotto test piuttosto difficili.

  4. Portano lo stato in giro per tutta la durata dell'applicazione. Un altro colpo ai test poiché è possibile che si verifichi una situazione in cui è necessario ordinare i test, il che è un grande no per i test unitari. Perché? Perché ogni unit test dovrebbe essere indipendente dall'altro.

Altri suggerimenti

Singletons risolve un (e solo un) problema.

Contesa di risorse.

Se hai qualche risorsa che

( 1 ) può avere solo una singola istanza e

( 2 ) devi gestire quella singola istanza,

hai bisogno di un singleton .

Non ci sono molti esempi. Un file di registro è quello più grande. Non vuoi abbandonare solo un singolo file di registro. Vuoi svuotare, sincronizzare e chiuderlo correttamente. Questo è un esempio di una singola risorsa condivisa che deve essere gestita.

È raro che tu abbia bisogno di un singleton. Il motivo per cui sono cattivi è che si sentono come un globale e sono completamente pagati membro del GoF Design Patterns .

Quando pensi di aver bisogno di un globale, probabilmente stai commettendo un terribile errore di progettazione.

Alcuni snob di codifica li guardano come un globale glorificato. Allo stesso modo in cui molte persone odiano la dichiarazione goto , ce ne sono altre che odiano l'idea di usare mai un globale . Ho visto diversi sviluppatori fare di tutto per evitare un globale perché hanno considerato di usarne uno come ammissione di fallimento. Strano ma vero.

In pratica il modello Singleton è solo una tecnica di programmazione che è una parte utile del tuo insieme di concetti. Di tanto in tanto potresti trovare la soluzione ideale e quindi usarla. Ma usarlo solo per vantarti di usare un modello di progettazione è altrettanto stupido che rifiutarti di usarlo perché è solo un globale .

Misko Hevery, di Google, ha alcuni articoli interessanti proprio su questo argomento ...

I singleton sono bugiardi patologici ha un'unità esempio di test che illustra come i singoli possono rendere difficile capire le catene di dipendenze e avviare o testare un'applicazione. È un esempio abbastanza estremo di abuso, ma il punto che afferma è ancora valido:

  

I singoli non sono altro che stato globale. Lo stato globale lo rende in modo che i tuoi oggetti possano ottenere segretamente cose che non sono dichiarate nelle loro API e, di conseguenza, i Singleton trasformano le tue API in bugiardi patologici.

Dove sono finiti tutti i Singletons sottolinea che l'iniezione di dipendenza ha reso facile ottenere istanze per i costruttori che le richiedono, il che allevia il bisogno sottostante alla base dei Singleton cattivi e globali denunciati nel primo articolo.

Penso che la confusione sia causata dal fatto che le persone non conoscono la vera applicazione del modello Singleton. Non posso sottolineare abbastanza. Singleton non è non uno schema per avvolgere i globi. Il modello Singleton deve essere utilizzato solo per garantire che esista una e una sola istanza di una determinata classe durante il runtime.

Le persone pensano che Singleton sia malvagio perché lo stanno usando per i globi. È a causa di questa confusione che Singleton è guardato in basso. Per favore, non confondere Singletons e globals. Se utilizzato per lo scopo a cui era destinato, otterrai benefici estremi dal modello Singleton.

Una cosa piuttosto negativa dei singoli è che non puoi estenderli molto facilmente. Fondamentalmente devi incorporare una sorta di motivo decoratore o qualcosa del genere se vuoi cambiare il loro comportamento. Inoltre, se un giorno vuoi avere più modi di fare quell'unica cosa, può essere piuttosto doloroso cambiare, a seconda di come disponi il tuo codice.

Una cosa da notare, se usi i singleton, prova a passarli a chiunque ne abbia bisogno anziché farli accedere direttamente ... Altrimenti se scegli di avere più modi di fare ciò che singleton fa, sarà piuttosto difficile cambiare poiché ogni classe incorpora una dipendenza se accede direttamente al singleton.

Quindi sostanzialmente:

public MyConstructor(Singleton singleton) {
    this.singleton = singleton;
}

anziché:

public MyConstructor() {
    this.singleton = Singleton.getInstance();
}

Credo che questo tipo di schema sia chiamato iniezione di dipendenza ed è generalmente considerato una buona cosa.

Come ogni modello però ... Pensaci e considera se il suo utilizzo in una determinata situazione è inappropriato o no ... Le regole sono fatte per essere infrante di solito, e pattern non dovrebbero essere applicati volenti o nolenti senza pensarci.

Il modello singleton non è un problema in sé. Il problema è che il modello viene spesso utilizzato da persone che sviluppano software con strumenti orientati agli oggetti senza avere una solida conoscenza dei concetti di OO. Quando i singleton vengono introdotti in questo contesto, tendono a crescere in classi ingestibili che contengono metodi di supporto per ogni piccolo utilizzo.

Anche i singoli sono un problema dal punto di vista del test. Tendono a rendere difficili i test unitari isolati da scrivere. Inversione del controllo (IoC) e iniezione di dipendenza sono schemi pensati per superare questo problema in modo orientato agli oggetti che si presta al test unitario .

In un ambiente immondizia raccolta i singoli possono diventare rapidamente un problema per quanto riguarda gestione della memoria.

Esiste anche lo scenario multi-thread in cui i singoli possono diventare un collo di bottiglia e un problema di sincronizzazione.

Un singleton viene implementato usando un metodo statico. I metodi statici sono evitati dalle persone che effettuano i test unitari perché non possono essere derisi o sradicati. La maggior parte delle persone su questo sito sono grandi sostenitori dei test unitari. La convenzione generalmente accettata per evitarli è l'utilizzo del inversione del controllo .

Anche i singoli sono cattivi quando si tratta di raggruppamento . Perché allora non hai esattamente "un singleton" nella tua applicazione più.

Considera la seguente situazione: come sviluppatore, devi creare un'applicazione web che acceda a un database. Per garantire che le chiamate simultanee al database non siano in conflitto tra loro, è necessario creare un SingletonDao :

public class SingletonDao {
    // songleton's static variable and getInstance() method etc. omitted
    public void writeXYZ(...){
        synchronized(...){
            // some database writing operations...
        }
    }
}

Quindi sei sicuro che esiste un solo singleton nella tua applicazione e tutto il database passa attraverso questo e solo SingletonDao . Il tuo ambiente di produzione ora appare così: Singleton

Finora tutto bene.

Ora, considera di voler impostare più istanze della tua applicazione web in un cluster. Ora improvvisamente hai qualcosa del genere:

Many singletons

Sembra strano, ma ora hai molti singoli nella tua applicazione . Ed è esattamente ciò che un singleton non dovrebbe essere: avere molti oggetti. Ciò è particolarmente negativo se, come mostrato in questo esempio, si desidera effettuare chiamate sincronizzate a un database.

Ovviamente questo è un esempio di un cattivo utilizzo di un singleton. Ma il messaggio di questo esempio è: non puoi fare affidamento sul fatto che esiste esattamente un'istanza di un singleton nella tua applicazione, specialmente quando si tratta di clustering.

  1. È facilmente (ab) usato come variabile globale.
  2. Le classi che dipendono dai singoli sono relativamente più difficili da testare unitamente.

Monopoly is the devil and singletons with non-readonly/mutable state are the 'real' problem...

After reading Singletons are Pathological Liars as suggested in jason's answer I came across this little tidbit that provides the best presented example of how singletons are often misused.

Global is bad because:

  • a. It causes namespace conflict
  • b. It exposes the state in a unwarranted fashion

When it comes to Singletons

  • a. The explicit OO way of calling them, prevents the conflicts, so point a. is not an issue
  • b. Singletons without state are (like factories) are not a problem. Singletons with state can again fall in two categories, those which are immutable or write once and read many (config/property files). These are not bad. Mutable Singletons, which are kind of reference holders are the ones which you are speaking of.

In the last statement he's referring to the blog's concept of 'singletons are liars'.

How does this apply to Monopoly?

To start a game of monopoly, first:

  • we establish the rules first so everybody is on the same page
  • everybody is given an equal start at the beginning of the game
  • only one set of rules is presented to avoid confusion
  • the rules aren't allowed to change throughout the game

Now, for anybody who hasn't really played monopoly, these standards are ideal at best. A defeat in monopoly is hard to swallow because, monopoly is about money, if you lose you have to painstakingly watch the rest of the players finish the game, and losses are usually swift and crushing. So, the rules usually get twisted at some point to serve the self-interest of some of the players at the expense of the others.

So you're playing monopoly with friends Bob, Joe, and Ed. You're swiftly building your empire and consuming market share at an exponential rate. Your opponents are weakening and you start to smell blood (figuratively). Your buddy Bob put all of his money into gridlocking as many low-value properties as possible but his isn't receiving a high return on investment the way he expected. Bob, as a stroke of bad luck, lands on your Boardwalk and is excised from the game.

Now the game goes from friendly dice-rolling to serious business. Bob has been made the example of failure and Joe and Ed don't want to end up like 'that guy'. So, being the leading player you, all of a sudden, become the enemy. Joe and Ed start practicing under-the-table trades, behind-the-back money injections, undervalued house-swapping and generally anything to weaken you as a player until one of them rises to the top.

Then, instead of one of them winning, the process starts all over. All of a sudden, a finite set of rules becomes a moving target and the game degenerates into the type of social interactions that would make up the foundation of every high-rated reality TV show since Survivor. Why, because the rules are changing and there's no consensus on how/why/what they're supposed to represent, and more importantly, there's no one person making the decisions. Every player in the game, at that point, is making his/her own rules and chaos ensues until two of the players are too tired to keep up the charade and slowly give up.

So, if a rulebook for a game accurately represented a singleton, the monopoly rulebook would be an example of abuse.

How does this apply to programming?

Aside from all of the obvious thread-safety and synchronization issues that mutable singletons present... If you have one set of data, that is capable of being read/manipulated by multiple different sources concurrently and exists during the lifetime of the application execution, it's probably a good time to step back and ask "am I using the right type of data structure here".

Personally, I have seen a programmer abuse a singleton by using it as some sort of twisted cross-thread database store within an application. Having worked on the code directly, I can attest that it was a slow (because of all the thread locks needed to make it thread-safe) and a nightmare to work on (because of the unpredictable/intermittent nature of synchronization bugs), and nearly impossible to test under 'production' conditions. Sure, a system could have been developed using polling/signaling to overcome some of the performance issues but that wouldn't solve the issues with testing and, why bother when a 'real' database can already accomplish the same functionality in a much more robust/scalable manner.

A Singleton is only an option if you need what a singleton provides. A write-one read-only instance of an object. That same rule should cascade to the object's properties/members as well.

Singleton is not about single instance!

Unlike other answers I don't want to talk about what is wrong with Singletons but to show you how powerful and awesome they are when used right!

  • Problem: Singleton can be a challenge in multi-threading environment
    Solution: Use a single threaded bootstrap process to initialize all the dependencies of your singleton.
  • Problem: It is hard to mock singletons.
    Solution: Use method Factory pattern for mocking
    MyModel myModel = Factory.inject(MyModel.class); You can map MyModel to TestMyModel class that inherits it, everywhere when MyModel will be injected you will get TestMyModel instread.
  • Problem: Singletons can cause memory leeks as they never disposed.
    Solution: Well, dispose them! Implement a callback in your app to properly dispose a singletons, you should remove any data linked to them and finally: remove them from the Factory.

As I stated at the title singleton are not about single instance.

  • Singletons improves readability: You can look at your class and see what singleton it injected to figure out what is it's dependencies.
  • Singletons improves maintenance: Once you removed a dependency from a class you just deleted some singleton injection, you don't need to go and edit a big link of other classes that just moved your dependency around(This is smelly code for me @Jim Burger)
  • Singletons improves memory and performance: When some thing happens in your application, and it takes a long chain of callbacks to deliver, you are wasting memory and performance, by using Singleton you are cutting the middle man, and improve your performance and memory usage(by avoiding unnecessary local variables allocations).

My answer on how Singletons are bad is always, "they are hard to do right". Many of the foundational components of languages are singletons (classes, functions, namespaces and even operators), as are components in other aspects of computing (localhost, default route, virtual filesystem, etc.), and it is not by accident. While they cause trouble and frustration from time to time, they also can make a lot of things work a LOT better.

The two biggest screw ups I see are: treating it like a global & failing to define the Singleton closure.

Everyone talks about Singleton's as globals, because they basically are. However, much (sadly, not all) of the badness in a global comes not intrinsically from being global, but how you use it. Same goes for Singletons. Actually more so as "single instance" really doesn't need to mean "globally accessible". It is more a natural byproduct, and given all the bad that we know comes from it, we shouldn't be in such a hurry to exploit global accessibility. Once programmers see a Singleton they seem to always access it directly through its instance method. Instead, you should navigate to it just like you would any other object. Most code shouldn't even be aware it is dealing with a Singleton (loose coupling, right?). If only a small bit of code accesses the object like it is a global, a lot of harm is undone. I recommend enforcing it by restricting access to the instance function.

The Singleton context is also really important. The defining characteristic of a Singleton is that there is "only one", but the truth is it is "only one" within some kind of context/namespace. They are usually one of: one per thread, process, IP address or cluster, but can also be one per processor, machine, language namespace/class loader/whatever, subnet, Internet, etc.

The other, less common, mistake is to ignore the Singleton lifestyle. Just because there is only one doesn't mean a Singleton is some omnipotent "always was and always will be", nor is it generally desirable (objects without a begin and end violate all kinds of useful assumptions in code, and should be employed only in the most desperate of circumstances.

If you avoid those mistakes, Singletons can still be a PITA, bit it is ready to see a lot of the worst problems are significantly mitigated. Imagine a Java Singleton, that is explicitly defined as once per classloader (which means it needs a thread safety policy), with defined creation and destruction methods and a life cycle that dictates when and how they get invoked, and whose "instance" method has package protection so it is generally accessed through other, non-global objects. Still a potential source of trouble, but certainly much less trouble.

Sadly, rather than teaching good examples of how to do Singletons. We teach bad examples, let programmers run off using them for a while, and then tell them they are a bad design pattern.

See Wikipedia Singleton_pattern

It is also considered an anti-pattern by some people, who feel that it is overly used, introducing unnecessary limitations in situations where a sole instance of a class is not actually required.[1][2][3][4]

References (only relevant references from the article)

  1. ^ Alex Miller. Patterns I hate #1: Singleton, July 2007
  2. ^ Scott Densmore. Why singletons are evil, May 2004
  3. ^ Steve Yegge. Singletons considered stupid, September 2004
  4. ^ J.B. Rainsberger, IBM. Use your singletons wisely, July 2001

It's not that singletons themselves are bad but the GoF design pattern is. The only really argument that is valid is that the GoF design pattern doesn't lend itself in regards to testing, especially if tests are run in parallel.

Using a single instance of an class is a valid construct as long as you apply the following means in code:

  1. Make sure the class that will be used as a singleton implements an interface. This allows stubs or mocks to be implemented using the same interface

  2. Make sure that the Singleton is thread-safe. That's a given.

  3. The singleton should be simple in nature and not overly complicated.

  4. During the runtime of you application, where singletons need to be passed to a given object, use a class factory that builds that object and have the class factory pass the singleton instance to the class that needs it.

  5. During testing and to ensure deterministic behavior, create the singleton class as separate instance as either the actual class itself or a stub/mock that implements its behavior and pass it as is to the class that requires it. Don't use the class factor that creates that object under test that needs the singleton during test as it will pass the single global instance of it, which defeats the purpose.

We've used Singletons in our solutions with a great deal of success that are testable ensuring deterministic behavior in parallel test run streams.

Vince Huston has these criteria, which seem reasonable to me:

Singleton should be considered only if all three of the following criteria are satisfied:

  • Ownership of the single instance cannot be reasonably assigned
  • Lazy initialization is desirable
  • Global access is not otherwise provided for

If ownership of the single instance, when and how initialization occurs, and global access are not issues, Singleton is not sufficiently interesting.

I'd like to address the 4 points in the accepted answer, hopefully someone can explain why I'm wrong.

  1. Why is hiding dependencies in your code bad? There are already dozens of hidden dependencies (C runtime calls, OS API calls, global function calls), and singleton dependencies are easy to find (search for instance()).

    "Making something global to avoid passing it around is a code smell." Why isn't passing something around to avoid making it a singleton a code smell?

    If you're passing an object through 10 functions in a call stack just to avoid a singleton, is that so great?

  2. Single Responsibility Principle: I think this is a bit vague and depends on your definition of responsibility. A relevant question would be, why does adding this specific "responsibility" to a class matter?

  3. Why does passing an object to a class make it more tightly coupled than using that object as a singleton from within the class?

  4. Why does it change how long the state lasts? Singletons can be created or destroyed manually, so the control is still there, and you can make the lifetime the same as a non-singleton object's lifetime would be.

Regarding unit tests:

  • not all classes need to be unit tested
  • not all classes that need to be unit tested need to change the implementation of the singleton
  • if they do need be unit tested and do need to change the implementation, it's easy to change a class from using a singleton to having the singleton passed to it via dependency injection.

I'm not going to comment on the good/evil argument, but I haven't used them since Spring came along. Using dependency injection has pretty much removed my requirements for singleton, servicelocators and factories. I find this a much more productive and clean environment, at least for the type of work I do (Java-based web applications).

There is nothing inherently wrong with the pattern, assuming it is being used for some aspect of your model which is truly single.

I believe the backlash is due to its overuse which, in turn, is due to the fact that it's the easiest pattern to understand and implement.

Singletons are bad from a purist point of view.

From a pratical point of view, a singleton is a trade-off developing time vs complexity.

If you know your application won't change that much they are pretty OK to go with. Just know that you may need to refactor things up if your requirements change in an unexpected way (which is pretty OK in most cases).

Singletons sometimes also complicate unit testing.

Singleton is a pattern and can be used or abused just like any other tool.

The bad part of a singleton is generally the user (or should I say the inappropriate use of a singleton for things it is not designed to do). The biggest offender is using a singleton as a fake global variable.

When you write code using singletons, say, a logger or a database connection, and afterwards you discover you need more than one log or more than one database, you’re in trouble.

Singletons make it very hard to move from them to regular objects.

Also, it’s too easy to write a non-thread-safe singleton.

Rather than using singletons, you should pass all the needed utility objects from function to function. That can be simplified if you wrap all them into a helper object, like this:

void some_class::some_function(parameters, service_provider& srv)
{
    srv.get<error_logger>().log("Hi there!");
    this->another_function(some_other_parameters, srv);
}

The problems with singletons is the issue of increased scope and therefore coupling. There is no denying that there are some of situations where you do need access to a single instance, and it can be accomplished other ways.

I now prefer to design around an inversion of control (IoC) container and allow the the lifetimes to be controlled by the container. This gives you the benefit of the classes that depend on the instance to be unaware of the fact that there is a single instance. The lifetime of the singleton can be changed in the future. Once such example I encountered recently was an easy adjustment from single threaded to multi-threaded.

FWIW, if it a PIA when you try to unit test it then it's going to PIA when you try to debug, bug fix or enhance it.

Recent article on this subject by Chris Reath at Coding Without Comments.

Note: Coding Without Comments is no longer valid. However, The article being linked to has been cloned by another user.

http://geekswithblogs.net/AngelEyes/archive/2013/09/08/singleton-i-love-you-but-youre-bringing-me-down-re-uploaded.aspx

Here is one more thing about singletons which nobody said yet.

In most cases "singletonity" is a detail of implementation for some class rather than characteristic of its interface. Inversion of Control Container may hide this characteristic from class users; you just need to mark your class as a singleton (with @Singleton annotation in Java for example) and that's it; IoCC will do the rest. You don't need to provide global access to your singleton instance because the access is already managed by IoCC. Thus there is nothing wrong with IoC Singletons.

GoF Singletons in opposite to IoC Singletons are supposed to expose "singletonity" in the interface through getInstance() method, and so that they suffer from everything said above.

Singletons are NOT bad. It's only bad when you make something globally unique that isn't globally unique.

However, there are "application scope services" (think about a messaging system that makes components interact) - this CALLS for a singleton, a "MessageQueue" - class that has a method "SendMessage(...)".

You can then do the following from all over the place:

MessageQueue.Current.SendMessage(new MailArrivedMessage(...));

And, of course, do:

MessageQueue.Current.RegisterReceiver(this);

in classes that implement IMessageReceiver.

Too many people put objects which are not thread safe in a singleton pattern. I've seen examples of a DataContext (LINQ to SQL) done in a singleton pattern, despite the fact that the DataContext is not thread safe and is purely a unit-of-work object.

Because they are basically object oriented global variables, you can usually design your classes in such a way so that you don't need them.

A pattern emerges when several people (or teams) arrives at similar or identical solutions. A lot of people still use singletons in their original form or using factory templates (good discussion in Alexandrescu's Modern C++ Design). Concurrency and difficulty in managing the lifetime of the object are the main obstacles, with the former easily managed as you suggest.

Like all choices, Singleton has its fair share of ups and downs. I think they can be used in moderation, especially for objects that survive the application life span. The fact that they resemble (and probably are) globals have presumably set off the purists.

Firstly a class and its collaborators should firstly perform their intended purpose rather than focusing on deoendents. Lifecycle management (when instances are creared snd when they go out of scope) should not be part of the cladses responsibility. The accepted best practice for this is to craft or configure a new component to manage dependencies using dependency injection.

Often software gets more complicated it makes sense to have multiple independent instances of the "singleton" class with different state. Committing code to simply grab the singleton is wrong in such cases. Using Singleton.getInstance() might be ok for small simple systems but it doesn't work / scale when one might need a different instance of the same class.

No class should be thought of as a singleton but rather that should be an aplication of it's usage or how it is used to configure dependents. For a quick and nasty this does not matter- just luke hardcoding say file paths does not matter but for bigger applications such dependencies need to be factored out and managed in more appropriate way using DI.

The problems that singleton cause in testing is a symptom of their hard coded single usage case/environment. The test suite and the many tests are each individual and separate something that is not compatible with hardcoding a singleton.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top