O padrão de design Singleton é um dos mais importantes e frequentemente utilizados na programação de software. Ele assegura que uma classe tenha apenas uma única instância durante o tempo de execução da aplicação e fornece um ponto de acesso global a essa instância. Neste artigo, discutiremos a importância do Singleton, como implementá-lo em Golang e os benefícios que ele traz, especialmente em ambientes concorrentes.
O Singleton é um padrão de design que restringe a instância de uma classe a uma única instância. É particularmente útil em situações onde um único ponto de controle ou um único recurso compartilhado é necessário, como:
Vou listar alguns pontos sobre a implementação desde Pattern que fazem mais sentido e também para mostrar que nem tudo são flores, alguns dos problemas que podemos ter com ele.
Para implementar uma singleton vou utilizar Golang. Nesta linguagem temos que ter uma atenção especial à concorrência para garantir que apenas uma instância seja criada, mesmo quando múltiplas goroutines tentam acessar a instância simultaneamente.
Para deixar nosso exemplo mais próximo do mundo real, vamos criar um Logger para nossa aplicação. Um logger é uma ferramenta comum em aplicações que precisa ser única para garantir a consistência dos logs.
Primeiro, definimos a estrutura que queremos que tenha uma única instância.
package logger import ( "fmt" "sync" ) type Logger struct {} var loggerInstance *Logger
A função NewInstance é responsável por retornar a instância única da estrutura Singleton. Utilizamos um mutex para garantir a segurança em ambientes concorrentes, implementando a verificação dupla de bloqueio (double-checked locking) para eficiência.
package logger import ( "fmt" "sync" ) type Logger struct{} var logger *Logger var mtx = &sync.Mutex{} func NewInstance() *Logger { if logger == nil { mtx.Lock() defer mtx.Unlock() if logger == nil { fmt.Println("Creating new Logger") logger = &Logger{} } } else { fmt.Println("Logger already created") } return logger }
Uma ferramenta de Log sempre tem alguns tipos de log, como por exemplo Info para apenas mostrar as informações, Error para mostrar erros e assim por diante. É uma forma de filtrarmos também o tipo de informação que queremos mostrar em nossa aplicação.
Então vamos criar um método que irá mostrar nosso log com o tipo Info. Para isso vamos criar uma função que receberá nossa mensagem de log e a formatará para o formato INFO.
package logger import ( "fmt" "sync" "time" ) const ( INFO string = "INFO" ) type Logger struct{} var logger *Logger var mtx = &sync.Mutex{} func NewInstance() *Logger { if logger == nil { mtx.Lock() defer mtx.Unlock() if logger == nil { fmt.Println("Creating new logger") logger = &Logger{} } } else { fmt.Println("Logger already created") } return logger } func (l *Logger) Info(message string) { fmt.Printf("%s - %s: %s\n", time.Now().UTC().Format(time.RFC3339Nano), INFO, message) }
E para utilizar nosso novo logger, vamos instancia-lo dentro do nosso package main e criar um log para ver como funciona essa implementação.
package main import ( "playground-go/pkg/logger" ) func main() { log := logger.NewInstance() log.Info("This is an example of log") }
Esse é o resultado quando executamos o programa:
Creating new logger 2024-07-03T19:34:57.609599Z - INFO: This is an example of log
Se quisermos testar se o NewInstance está realmente garantindo que apenas teremos uma instancia rodando, podemos fazer o seguinte teste.
package main import ( "fmt" "playground-go/pkg/logger" ) func main() { log := logger.NewInstance() log.Info("This is an example of log") log2 := logger.NewInstance() log2.Info("This is another example of log") if log == log2 { fmt.Println("same instance") } else { fmt.Println("different instance") } }
Nossos logs mudaram e agora podemos ver que bloqueamos a criação de uma nova instancia:
Creating new logger 2024-07-03T19:45:19.603783Z - INFO: This is an example of log Logger already created 2024-07-03T19:45:19.603793Z - INFO: This is another example of log same instance
O padrão Singleton é uma ferramenta poderosa para garantir que apenas uma instância de uma classe específica exista durante o tempo de execução da aplicação. No exemplo do logger, vimos como esse padrão pode ser aplicado para garantir a consistência dos logs em toda a aplicação.
Espero que isso ajude você a entender melhor o Singleton em Golang.
Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.
Copyright© 2022 湘ICP备2022001581号-3