Singleton in go
-
22-07-2019 - |
Domanda
Come si implementa il modello di progettazione Singleton nel linguaggio di programmazione go?
Soluzione
Mettendo da parte l'argomento se l'implementazione del pattern singleton sia una buona idea, ecco una possibile implementazione:
package singleton
type single struct {
O interface{};
}
var instantiated *single = nil
func New() *single {
if instantiated == nil {
instantiated = new(single);
}
return instantiated;
}
single
e istanziati
sono privati, ma New ()
è pubblico. Pertanto, non è possibile creare un'istanza diretta di single
senza passare attraverso New ()
e tiene traccia del numero di istanze con il istanziato
booleano privato. Modifica la definizione di single
a piacere.
Tuttavia, come molti altri hanno annotato , questo non è thread-safe, a meno che tu non stia inizializzando il tuo singleton in init ()
. Un approccio migliore sarebbe quello di sfruttare sync.Once
per fare il duro lavoro per te:
package singleton
import "sync"
type single struct {
O interface{};
}
var instantiated *single
var once sync.Once
func New() *single {
once.Do(func() {
instantiated = &single{}
})
return instantiated
}
Vedi anche, il suggerimento di hasan j di pensare solo a un pacchetto come un singleton. E infine, considera ciò che gli altri suggeriscono: che i singoli sono spesso un indicatore di un'implementazione problematica.
Altri suggerimenti
Prima di provare a trovare un modo per piegare Vai alla tua volontà, potresti dare un'occhiata ad alcuni articoli:
In breve, nel tempo le persone hanno scoperto che i singoli non sono ottimali e imho specialmente se stai cercando di fare uno sviluppo guidato dai test: a molti livelli sono praticamente cattivi come variabili globali.
[disclaimer: so che non è una risposta rigorosa alla tua domanda ma è davvero pertinente]
Metti solo le tue variabili e funzioni a livello di pacchetto.
Vedi anche una domanda simile: Come creare un singleton in Python
Penso che in un mondo concorrente dobbiamo essere un po 'più consapevoli del fatto che queste linee non sono eseguite atomicamente:
if instantiated == nil {
instantiated = new(single);
}
Seguirei il suggerimento di @marketer e utilizzerei il pacchetto " sync "
import "sync"
type MySingleton struct {
}
var _init_ctx sync.Once
var _instance *MySingleton
func New() * MySingleton {
_init_ctx.Do( func () { _instance = new(MySingleton) } )
return _instance
}
L'approccio migliore sarà:
package singleton
import "sync"
type singleton struct {
}
var instance *singleton
var once sync.Once
func GetInstance() *singleton {
once.Do(func() {
instance = &singleton{}
})
return instance
}
Dovresti leggere questo Link
Puoi effettuare l'inizializzazione utilizzando il pacchetto una volta :
Questo assicurerà che i tuoi metodi init vengano chiamati una sola volta.
Basta avere un'unica istanza statica, finale, costante, globale, a livello di applicazione dell'oggetto desiderato.
Ciò tuttavia contraddice il paradigma OO. Il suo uso dovrebbe essere limitato ai primitivi e agli oggetti immutabili, non agli oggetti mutabili.
Dovresti essere consapevole che Once.Do
è serio nell'esecuzione del codice una sola volta. Ciò significa che il codice viene sempre eseguito una sola volta, anche se potrebbe essere stato preso dal panico:
da https://golang.org/pkg/sync/#Once.Do
Se f (nota: la logica una volta) va nel panico, Do lo considera restituito; le chiamate future di Do ritornano senza chiamare f.
Ho usato invece i mutex per garantire l'inizializzazione unica di una variabile di configurazione globale per superare questa restrizione: