Java: Introduzione Ai Design Pattern: Singleton

by Alessio Mungelli Date: 11-09-2019 java programming programmazione pattern design sviluppo tech singleton


Chiunque abbia anche una minima esperienza di programmazione, si sarà reso conto di come i problemi
sianoricorrenti. Infatti troviamo spesso problemi con uno stesso pattern ma con contesti differenti.
Ad esempio, un gestionale per un magazzino di un supermercato e quello per un magazzino di una grande industria avranno presumibilmente dati diversi, ma il comportamento del programma sarà pressochè lo stesso.
Allargando questo tipo di concetto a tutta la programmazione, si può affermare che i
problemi incontrati nello sviluppare grossi progetti software sono spesso ricorrenti e prevedibili.
Questo tipo di ragionamento da luogo ai pattern, meglio conosciuti come design pattern.

Cosa significa usare un pattern?

Usare un pattern significa non inventare da capo nuove soluzioni, usando soluzioni già create e consolidate nel tempo. L'idea è quindi quella di fornire un vocabolario "comune" alla maggior parte dei progettisti.

Dando una definizione più teorica, un pattern è un insieme di classi e oggetti comunicanti adattabili per risolvere un problema ricorrente in un contesto specifico.

La cosa importante è che sono stati concepiti come non domain-specific, per cui non sono rivolti ad applicazioni specifiche, ma sono riutilizzabili in parti di applicazioni diverse.

Questo tipo di strategia di progettazione viene anche usato in classi Java che usiamo comunemente, come Abstract, Iterator, Factory, Singleton.

Ad oggi, i pattern possono essere classificati come:

  • pattern crazionali:riguardano il processo di creazione degli oggetti;
  • pattern strutturali:hanno a che fare con l'aspetto di composizione delle classi e degli oggetti;
  • pattern comportamentali: si occupano dell'aspetto riguardante l'interazione e la distribuzione della responsabilità tra le classi;

Un esempio molto semplice: Singleton

La necessità di questo tipo di design pattern nasce dal fatto che si potrebbe aver bisogno di una classe con una sola istanza.

Possibilmente, l'implementazione deve essere thread-safe. Vediamo meglio.

Implementazione classica

Ecco il diagramma UML:

Singleton

public class Singleton{
/*
Questa sara' l'unica istanza di questa classe.
Ogni accesso avverrà tramite quest oggetto
e non ne verranno creati altri.
*/
private static Singleton instance = null; 
/*
Costruttore privato per evitare la creazione di altre
istanze dell'oggetto
*/
private Singleton(){
}
/*
Unico punto di accesso all'istanza
*/
public static Singleton factory(){
//creo l'oggetto se non esiste
if(instance==null)
instance = new Singleton();
return instance;
}
//eventuali altri metodi
}

Questo tipo di implementazione non è ottimale per un ambiente multithread. Vediamo una possibile modifica

public class Singleton{
/*
Questa sara' l'unica istanza di questa classe.
Ogni accesso avverrà tramite quest oggetto
e non ne verranno creati altri.
*/
private static Singleton instance = null; 
/*
Costruttore privato per evitare la creazione di altre
istanze dell'oggetto
*/
private Singleton(){
}
private synchronized static Singleton createInstance(){
if(instance==null)
instance=new Singleton();
return instance;
}
public static Singleton factory(){
if(instance==null)
//richiamo il metodo synchr solo se 
//l'istanza non esiste
createInstance();
return instance;
}
//eventuali altri metodi
}

Capiamo il codice

I due esempi riportati svolgono pressochè le stesse funzioni se pur con modalità leggermente diverse.

Il primo esempio ha un attributo statico che rappresenta l'unica istanza accessibile della classe. Si è deciso di rendere il costruttore privato in modo tale da impedire la creazione di istanze indesiderate della classe.

L'accesso alla classe può essere fatto solo mediante un unico metodo factory che controlla lo stato dell'attributo statico. Nel caso in cui non esistesse, lo istanzia. Il metodo termina restituendo al chiamante l'attributo.

Ci saranno quindi due casi possibli:

  • instance == null: il meteodo istanzierà instance e la restituirà al chiamante;
  • instance != null: il metodo restituirà al chiamante il puntatore ad instance.

A prima vista l'implementazione è relativamente facile per chiunque abbia un minimo di manualità. In un ambiente multithread potrebbero sorgere problemi di sincronizzazione, per cui si è deciso di realizzare una versione thread-safe.

La logica del secondo esempio è la medesima del primo, così come il risultato finale, se pur con una logica applicativa leggermente diversa.

Si nota subito che qui i metodi sono due e non più uno. Il primo metodo è necessariamente synchronized al fine di garantire una corretta sincronizzazione tra i vari thread.

Ricordiamo che un metodo synchronized è un metodo tale che quando un thread richiama il metodo synchronized su un oggetto, tutti gli altri thread che richiamano quel metodo dopo sospendono la loro esecuzione fino a che non termina l’esecuzione del metodo (richiamato dal primo thread).

Il metodo createInstance() ha il compito di controllare lo stato dell'attributo statico e in base al suo valore, istanziarlo o no.

Il metodo factory si limita a fare da wrapper a createInstance() controllando anche lo stato di instance.

Questo doppio controllo, che può sembrare apparentemente insensato, serve a diminuire l'overhead. Strategie come questa vengono definite double-checked locking.

Volendo ci sono anche altre strategia man mano più complesse per implementare un Singleton, che diminuiscono man mano l'overhead.

Una possibile alternativa è la seguente:

public class Singleton{
	private static Singleton instance = null; 
	private Singleton(){
	}
	public static Singleton getIstance() {
        if(istance==null)
                synchronized(Singleton.class) {
                        if( instance == null )
                                istance = new Singleton();
                        }
        return istance;
}
	//eventuali altri metodi
}
 

Bisogna dire anche che oggi vengono mosse diverse critiche verso questo pattern, perchè il problema del Singleton Pattern viene frainteso e spesso utilizzato per introdurre il concetto di variabili globali nel proprio sistema introducendo nell’applicazione lo stato globale nel dominio dell’applicazione. Questo è un male, perchè le variabili gloabli notoriamente non si preoccupano della stuttura del programma.

Un'altra critica piuttosto forte che viene mossa è che la maggior parte delle classi Singleton sono contro i principi generali dell'ingengeria del software, funzionando quasi da aggreganti per funzionalità diverse spesso senza relazioni, introducendo così il concetto di dipendenza, violando il concetto di responsabilità singola.

Infatti, secondo i principi dell'ingengeria del software, ogni elemento del programma deve avere una sola responsabilità e tale responsabilità deve essere interamente incapsulata dall'elemento stesso.

Il Singleton permette di accedervi mediante un unico metodo statico e questa caratteristica rende possibile l'utilizzo dell'istanza all'interno di altri metodi senza passare attraverso i parametri. Per quanto possa sembrare comodo, questo significa che le signature dei metodi non mostrano più le dipendenze, rendendo necessaria agli utenti la conoscenza delle logiche interne del codice. In questa maniera il software sarà nettamente più difficile da usare e testare.

Un terzo difetto è la violazione del Principio di Sostituzione di Liskov. Infatti, non presentando relazioni di ereditarietà, è impossibile sostituire gli oggetti legati da un vincolo di parentela.

Nonostante le critiche, se non abusato il Singleton può rappresentare una risorsa, anche se vedremo più avanti altre strategie per risolvere problemi analoghi.

 
by Alessio Mungelli Date: 11-09-2019 java programming programmazione pattern design sviluppo tech singleton visite : 526  
 
 
 
 

Articoli correlati

    Java algoritmi di ordinamento: Bubble Sort

    Programmando, nasce spesso la necessità di ordinare le collezioni di dati o oggetti che devono poi essere manipolate. Ordinare una lista può essere utile nei casi in cui si debbano…

    Java Design Pattern: Prototype Pattern

    Andremo ora a parlare di un pattern creazionale che ci permette di "copiare con classe". Sì, anche se sembra strano, il compito fondamentale di questo pattern è copiare. Sto parlando…

    Java Design Pattern: Builder Pattern

    Andiamo oggi a parlare di un pattern creazionale che in molte situazioni può rappresentare una valida alternativa alla costruzione di oggetti mediante costruttori: il Builder Pattern. La necessità di introdurre meccanismi…

    Java Design Pattern: Strategy Pattern

    Uno dei pattern che gode di una notevole popolarità ed è al contempo piuttosto semplice è lo Strategy Pattern. Membro della famiglia dei pattern comportamentali, ha il compito di gestire algoritmi,…

    Java Design Pattern: Factory Pattern

    Continuando il discorso sui design pattern iniziato precedentemente, andiamo oggi a vedere un altro pattern molto utilizzato: il Factory Method Pattern. Il GoF (Gang of Four Design Patterns) lo definisce così: Definisce…

    Java 12, finalmente meno prolisso?

    Conosciamo tutti Java per le sue caratteristiche grazie alle quali, nonostante siano passati più di 20 anni dalla prima versione,è tutt'oggi uno dei linguaggi più studiati e più utilizzati, malgrado…

    JQuery morirà nel 2019?

    Per un po' di tempo, la centralità di JQuery è stata oggetto di dibattito tra gli sviluppatori web. Come programmatori web interessati a Javascript, eravamo curiosi di sapere che opinioni…

    45 utili siti che avresti voluto conoscere prima

    In rete sono presenti talmente tanti siti web dedicati alla sviluppo web e alla grafica, che risulta molto complicato conoscerli tutti. Oggi, vi proponiamo una lista di siti web non…

    30 Manuali di riferimento per JavaScript: jQuery, Node.js, React

    Questa lista ha lo scopo di introdurre i nuovi sviluppatori a JavaScript (jQuery, Node.js e React) e agevolare il compito degli sviluppatori più esperti. jQuery jQuery API (Official) Leggi → jQuery Cheatsheet (Devhints) Leggi → jQuery Cheat Sheet (JavaScript Toolbox) Leggi…

    Le migliori librerie JavaScript del 2018

    Dal momento che Javascript si è rivelato il linguaggio di programmazione più popolare e ampiamente utilizzato nel 2018, l'ecosistema che si sviluppa intorno ad esso sta cominciando a diventare importante. JavaScript…

    Convertire il testo in voce e viceversa con Javascript: tutorial+codice

    In questo tutorial sperimenteremo la Web Speech API: un'interfaccia browser molto potente che consente di registrare la voce umana e convertirla in testo. La useremo anche per fare il contrario: interpretare…

    I 5 Migliori Frameworks JavaScript per Applicazioni Desktop

    Non molto tempo fa era impossibile costruire un'applicazione desktop con JavaScript. Fortunatamente, questi tempi sono passati, e ora gli sviluppatori JS possono utilizzare le loro conoscenze e competenze di sviluppo…