Executando verificação de segurança...
2

[Design Patterns] Construindo um Singleton em Typescript

Fala pessoal!

A jornada do aprendizado na área de desenvolvimento é realmente longa, para uns traz a sensação de que não está aprendendo, e que precisa decorar todos os códigos.
Alguns pensam que a quantidade de informações é exponencial, e tende ao infinito.
Eu concordo com a segunda afirmação! hahah
Com a ressalva de que vamos estudar a vida inteira, e não vamos estar nem perto de absorver toda essa informação, logicamente.

Um dos conhecimentos mais valiosos que aprendi na universidade, foi estruturar as informações em padrões, e transformar os padrões em soluções lógicas (Design Patterns).

Fora da universidade eu me aventurei como instrutor em uma escola de programação, durante o curso de Ciência da Computação. Foi assim que aprendi a aprender, ensinando sem a pretensão de me tornar professor.

Hoje decidi voltar a escrever sobre desenvolvimento de software, e minha primeira aventura é neste forúm maravilhoso!

Onde eu explico sobre o básico para criarmos um Pattern Singleton em Typescript, e seus objetivos.

Segue!

O padrão Singleton é um dos padrões(Design patterns) mais utilizados, e está presentes na camada criacional, uma das camadas de design definidas pelo livro “Design Patterns: Elements of Reusable Object-Oriented Software” de 1994, escrito por GOF (Gang of Four: Rich Gamma, Richard Helm, Ralph Johnson e John Vlissides).
Esse pattern assegura que somente uma instancia de uma determinada classe seja criado em todo o projeto.

Vejam uma implementação simples em Typescript:

class Singleton {
    private static _instance: Singleton | null = null;
    private constructor(){}
    public static get instance(): Singleton {
        if (this._instance === null) {
            this._instance = new Singleton();
        }
        return this._instance;
    }
    anyMethod(): void {
        console.log("anyMethod")
    }
}

const singleton = Singleton.instance.anyMethod()

Uso prático

É muito comum utilizar esse padrão para criarmos classes que vão ser chamadas diversas vezes no projeto.
Por exemplo uma conexão com o banco de dados:

class PgConnection {
    private static _instance: PgConnection | null = null;
    private constructor() { }
    public static get instance(): PgConnection {
        if (this._instance === null) {
            this._instance = new PgConnection();
        }
        return this._instance;
    }

    async connect(): Promise<void> {

    }

    async disconnect(): Promise<void> {

    }
}

const connection = PgConnection.instance
connection.connect()
connection.disconnect()

Podemos oberservar que:

O construtor é privado
O método _instance() é público e estático
Retorna a única instância que é guardada em uma variável de classe

Desta forma, garante que uma classe tenha apenas uma instância e fornece um ponto global de acesso a ela.
O benefício principal de se usar esse pattern, é tornar fácil o acesso e gerência de recursos compartilhados, como variáveis globais.

Referência:
Design Patterns: Elements of Reusable Object-Oriented Software” de 1994, escrito por GOF (Gang of Four: Rich Gamma, Richard Helm, Ralph Johnson e John Vlissides)
https://homepages.dcc.ufmg.br/~figueiredo/disciplinas/aulas/padroes-gof_v01.pdf

Github:
https://github.com/mauriciocarvalho01/design-patterns-examples/tree/main/src/creational/singleton

2

Massa Maurício! Vez ou outra também dou uma estudada nos patterns.

É importante dizer que essa estrátégia embora traga facilidades, também tem alguns contras, como por exemplo:

  • Muitos argumentam que o Singleton viola o princípio de responsabilidade única do SOLID.
  • Pode ser difícil de testar

Em relação as referências, o site refactoring.guru é muito completo também. Em todas as páginas sobre um determinado pattern existe uma área que mostra os prós e contras de utilizá-lo além de dar exemplos em várias linguagens de programação.

Referência Singleton:
https://refactoring.guru/pt-br/design-patterns/singleton

1

Exato. Essa é uma questão que está muito presente.
Isso nos leva a ter que criar estratégias para uso desse pattern. Por exemplo testar a classe antes de transforma-la em um Singleton.
De fato, antes de usa-lo em um projeto, precisamos analisar as epecificidades do mesmo, e ver se realmente é necessário.
Obrigado pela contibuição!!

2

Dúvida técnica, essa implementação também não seria singleton?

//start of file

class Singleton {
    constructor(){}
    anyMethod(): void {
        console.log("anyMethod")
    }
}

const singleton = new Singleton()

export default singleton

//end of file
2

Sim, isso também é um Singleton. Nesse caso você está utilizando a flexibilidade do node para exportar do módulo a instancia da classe. Com isso, quando você for importar será sempre a mesma instancia.

Caso queira tirar a prova, apenas acrescente uma variável contadora no construtor, e importa em diferentes módulos.

class Singleton {
    private countCalls: number = 0;
    constructor() {
        this.countCalls++
    }
    anyMethod(): void {
        console.log(`Calls: ${this.countCalls}`)
        console.log("anyMethod")
    }
}

const singleton = new Singleton()

export default singleton

Resultado:

Calls: 1
anyMethod
Calls: 1
anyMethod
Calls: 1
anyMethod
Calls: 1
anyMethod
1

Muito interessante sua jornada, por ser incrívelmente similar a minha.
Quanto ao conteúdo, acho que só faltou uma aplicação mais prática para quem acredita que isso só fica na teoria.
Pra terminar, edita a postagem, para colocar o trecho de código envolto em 3 acentos no início e no final, assim vai conseguir exibir com a formatação de código. Você inclusive pode passar qual a linguagem a qual o sintaxe highlight funcione.


Fonte:🔗 Fenced code blocks