Domanda

Ammettiamolo. Il modello Singleton è argomento molto controverso con i programmatori di orde su entrambi i lati della recinzione. Ci sono quelli che pensano che Singleton non sia altro che una glorificata variabile globale, e altri che giurano secondo lo schema e lo usano incessantemente. Non voglio che Singleton Controversy risieda nel cuore della mia domanda, comunque. Tutti possono avere un tiro alla fune e combattere e vedere chi vince per quello che mi interessa . Quello che sto cercando di dire è che non credo che ci sia un'unica risposta corretta e non sto intenzionalmente cercando di infiammare il battibecco partigiano. Sono semplicemente interessato a alternative singleton quando faccio la domanda:

Esistono alternative specifiche al modello Singleton GOF?

Ad esempio, molte volte quando ho usato il modello singleton in passato, sono semplicemente interessato a preservare lo stato / i valori di una o più variabili. Lo stato / i valori delle variabili, tuttavia, possono essere conservati tra ogni istanza della classe utilizzando variabili statiche invece di utilizzare il modello singleton.

Che altra idea hai?

MODIFICA: Non voglio davvero che questo sia un altro post su " come usare correttamente il singleton. " Ancora una volta, sto cercando modi per evitarlo. Per divertimento, ok? Immagino che sto ponendo una domanda puramente accademica nella tua migliore voce del trailer del film, "In un universo parallelo dove non c'è singleton, cosa potremmo fare?"

È stato utile?

Soluzione

Alex Miller in " Patterns I Hate " ; cita quanto segue:

" Quando un singleton sembra la risposta, trovo che sia più saggio:

  1. Crea un'interfaccia e un'implementazione predefinita del tuo singleton
  2. Costruisci una singola istanza dell'implementazione predefinita in & # 8220; top & # 8221; del tuo sistema. Questo potrebbe essere in una configurazione Spring, o nel codice, o definito in vari modi a seconda del sistema in uso.
  3. Passa la singola istanza in ogni componente che ne ha bisogno (iniezione di dipendenza)

Altri suggerimenti

Per capire il modo corretto di aggirare i Singleton, devi capire cosa c'è che non va nei Singletons (e nello stato globale in generale):

I singleton nascondono le dipendenze.

Perché è importante?

Perché Se nascondi le dipendenze tendi a perdere traccia della quantità di accoppiamento.

Potresti discuterne

void purchaseLaptop(String creditCardNumber, int price){
  CreditCardProcessor.getInstance().debit(creditCardNumber, amount);
  Cart.getInstance().addLaptop();
}

è più semplice di

void purchaseLaptop(CreditCardProcessor creditCardProcessor, Cart cart, 
                    String creditCardNumber, int price){
  creditCardProcessor.debit(creditCardNumber, amount);
  cart.addLaptop();
}

ma almeno la seconda API chiarisce esattamente quali sono i collaboratori del metodo.

Quindi il modo per aggirare Singletons non è usare variabili statiche o locatori di servizi, ma cambiare le classi Singleton in istanze, che sono istanziate nell'ambito in cui hanno senso e iniettate nei componenti e nei metodi che ne hanno bisogno . Potresti usare un framework IoC per gestirlo, oppure potresti farlo manualmente, ma l'importante è sbarazzarti del tuo stato globale e rendere esplicite le dipendenze e le collaborazioni.

La migliore soluzione che ho trovato è usare il modello di fabbrica per costruire istanze delle tue classi. Usando il modello, puoi assicurare che esiste solo un'istanza di una classe condivisa tra gli oggetti che la usano.

Pensavo che sarebbe complicato gestirlo, ma dopo aver letto questo post sul blog " Where Have All the Singletons Gone? " , sembra così naturale. E a parte questo, aiuta molto nell'isolare i test unitari.

In sintesi, cosa devi fare? Ogni volta che un oggetto dipende da un altro, ne riceverà un'istanza solo attraverso il suo costruttore (nessuna nuova parola chiave nella tua classe).

class NeedyClass {

    private ExSingletonClass exSingleton;

    public NeedyClass(ExSingletonClass exSingleton){
        this.exSingleton = exSingleton;
    }

    // Here goes some code that uses the exSingleton object
}

E poi, la fabbrica.

class FactoryOfNeedy {

    private ExSingletonClass exSingleton;

    public FactoryOfNeedy() {
        this.exSingleton = new ExSingletonClass();
    }

    public NeedyClass buildNeedy() {
        return new NeedyClass(this.exSingleton);
    }
}

Poiché istanzerai la tua fabbrica una sola volta, ci sarà una singola istanza di exSingleton. Ogni volta che chiami buildNeedy, la nuova istanza di NeedyClass verrà raggruppata con exSingleton.

Spero che questo aiuti. Indica eventuali errori.

Spring o qualsiasi altro contenitore IoC fa un buon lavoro in questo senso. Poiché le classi vengono create e gestite al di fuori dell'app stessa, il contenitore può creare singleton di classi semplici e iniettarle dove necessario.

Non dovresti fare di tutto per evitare qualsiasi modello. L'uso di un motivo è una decisione di progettazione o una misura naturale (si adatta al suo posto). Quando si progetta un sistema, è possibile scegliere se utilizzare un modello o non utilizzare il modello. Tuttavia, non dovresti fare di tutto per evitare tutto ciò che è in definitiva una scelta di design.

Non evito il Singleton Pattern. O è appropriato e lo uso o non è appropriato e non lo uso. Credo che sia così semplice.

L'adeguatezza (o la mancanza) del Singleton dipende dalla situazione. È una decisione di progettazione che deve essere presa e le conseguenze di tale decisione devono essere comprese (e documentate).

Il modello singleton esiste perché ci sono situazioni in cui è necessario un oggetto singolo per fornire un set di servizi .

Anche se questo è il caso, continuo a considerare l'approccio della creazione di singoli usando un campo / proprietà statici globali che rappresentano l'istanza, inappropriata. È inappropriato perché crea una dipendenza nel codice tra il campo statico e l'oggetto no, i servizi forniti dall'oggetto.

Quindi, invece del classico modello singleton, ti consiglio di usare il modello di servizio "like" con contenitori serviti , dove invece di usare il tuo singleton attraverso un campo statico, ottieni un riferimento ad esso tramite un metodo che richiede il tipo di servizio richiesto.

*pseudocode* currentContainer.GetServiceByObjectType(singletonType)
//Under the covers the object might be a singleton, but this is hidden to the consumer.

invece del singolo globale

*pseudocode* singletonType.Instance

In questo modo, quando vuoi cambiare il tipo di un oggetto da singleton a qualcos'altro, avrai e tempo facile per farlo. Inoltre, come ulteriore vantaggio, non è necessario trasferire l'assegnazione di istanze di oggetti a tutti i metodi.

Vedi anche Inversion of Control , l'idea è che esponendo i singoli punti direttamente al consumatore, si crea una dipendenza tra il consumatore e l'istanza dell'oggetto, non i servizi oggetto forniti dall'oggetto.

La mia opinione è di nascondere l'uso del modello singleton quando possibile, perché non è sempre possibile evitarlo o desiderabile.

Il monostato (descritto nello sviluppo software agile di Robert C. Martin) è un'alternativa al singleton. In questo modello i dati della classe sono tutti statici ma i getter / setter non sono statici.

Ad esempio:

public class MonoStateExample
{
    private static int x;

    public int getX()
    {
        return x;
    }

    public void setX(int xVal)
    {
        x = xVal;
    }
}

public class MonoDriver
{
    public static void main(String args[])
    {
        MonoStateExample m1 = new MonoStateExample();
        m1.setX(10);

        MonoStateExample m2 = new MonoStateExample();
        if(m1.getX() == m2.getX())
        {
            //singleton behavior
        }
    }
}

Il monostato ha un comportamento simile al singleton ma lo fa in un modo in cui il programmatore non è necessariamente consapevole del fatto che un singleton viene utilizzato.

Se si utilizza un Singleton per rappresentare un singolo oggetto dati, è possibile invece passare un oggetto dati come parametro del metodo.

(sebbene, direi che questo è il modo sbagliato di usare un Singleton in primo luogo)

Se il problema è che si desidera mantenere lo stato, si desidera una classe MumbleManager. Prima di iniziare a lavorare con un sistema, il client crea un MumbleManager, dove Mumble è il nome del sistema. Lo stato viene mantenuto attraverso quello. È probabile che il tuo MumbleManager contenga una borsa di proprietà che contiene il tuo stato.

Questo tipo di stile sembra molto simile a C e non molto simile a un oggetto: scoprirai che gli oggetti che definiscono il tuo sistema avranno tutti un riferimento allo stesso MumbleManager.

Usa un oggetto semplice e un oggetto factory. La fabbrica è responsabile della sorveglianza dell'istanza e dei dettagli dell'oggetto semplice solo con le informazioni di configurazione (che contiene ad esempio) e il comportamento.

In realtà se si progetta da zero per evitare Singelton, potrebbe non essere necessario aggirare il non utilizzo di Singleton utilizzando variabili statiche. Quando si utilizzano variabili statiche, si sta anche creando un Singleton più o meno, l'unica differenza è che si stanno creando istanze di oggetti differenti, tuttavia internamente si comportano tutti come se stessero usando un Singleton.

Puoi forse fare un esempio dettagliato in cui usi un Singleton o dove attualmente viene usato un Singleton e stai cercando di evitare di usarlo? Ciò potrebbe aiutare le persone a trovare una soluzione più elaborata su come gestire la situazione senza un Singleton.

A proposito, personalmente non ho problemi con i Singleton e non riesco a capire i problemi che altre persone hanno nei Singletons. Non vedo nulla di male in loro. Cioè, se non li stai abusando. Ogni tecnica utile può essere abusata e se viene abusata, porterà a risultati negativi. Un'altra tecnica che viene comunemente utilizzata in modo improprio è l'eredità. Tuttavia nessuno direbbe che l'eredità sia qualcosa di brutto solo perché alcune persone ne fanno un abuso orribile.

Personalmente per me & # 1072; Un modo molto più sensato di implementare qualcosa che si comporta come singleton è usare una classe completamente statica (membri statici, metodi statici, proprietà statiche). Il più delle volte lo implemento in questo modo (non riesco a pensare a differenze di comportamento dal punto di vista dell'utente)

Penso che il posto migliore per sorvegliare il singleton sia a livello di design di classe. A questo punto, dovresti essere in grado di mappare le interazioni tra le classi e vedere se qualcosa di assolutamente, richiede sicuramente che esista solo 1 istanza di questa classe in qualsiasi momento della vita delle applicazioni.

In tal caso, hai un singleton. Se stai inserendo i singleton come comodità durante la codifica, allora dovresti davvero rivisitare il tuo design e anche smettere di codificare detti singleton :)

E sì, "polizia" è la parola che intendevo qui piuttosto che "evitare". Il singleton non è qualcosa da evitare (nello stesso modo in cui goto e le variabili globali non sono qualcosa da evitare). Invece, dovresti monitorare il suo utilizzo e assicurarti che sia il metodo migliore per ottenere ciò che desideri fatto in modo efficace.

Uso singleton principalmente come "contenitore di metodi", senza alcuno stato. Se ho bisogno di condividere questi metodi con molte classi e voglio evitare il peso dell'istanziazione e dell'inizializzazione, creo un contesto / sessione e inizializzo tutte le classi lì; tutto ciò che fa riferimento alla sessione ha anche accesso al "singleton" così contenuto.

Non avendo programmato in un ambiente fortemente orientato agli oggetti (ad esempio Java), non sono completamente all'altezza delle complessità della discussione. Ma ho implementato un singleton in PHP 4. L'ho fatto come un modo per creare un gestore di database "black-box" che si inizializza automaticamente e non ha dovuto essere passato su e giù chiamate di funzione in un framework incompleto e in qualche modo rotto.

Avendo letto alcuni link di schemi singleton, non sono del tutto sicuro che lo implementerei allo stesso modo. Ciò che era veramente necessario erano più oggetti con memoria condivisa (ad esempio l'effettivo handle del database) e questo è praticamente ciò che la mia chiamata si è trasformata.

Come la maggior parte dei modelli e degli algoritmi, usare un singleton "solo perché è bello" è The Wrong Thing To Do. Avevo bisogno di una vera chiamata "black box" che somigliasse molto a un singleton. E l'IMO è il modo di affrontare la questione: essere consapevoli del modello, ma anche guardare al suo ambito più ampio ea quale livello deve avere l'istanza per essere unica.

Cosa intendi, quali sono le mie tecniche per evitarlo?

Per " evitare " ciò implica che ci sono molte situazioni in cui mi imbatto in un modello singleton che si adatta in modo naturale, e quindi che devo prendere alcune misure per disinnescare queste situazioni.

Ma non ci sono. Non devo evitare il modello singleton. Semplicemente non sorge.

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