Design Patterns
Criacionais
Padrão de Projeto: Singleton

Padrão de Projeto: Singleton

Propósito

O Singleton é um padrão de projeto criacional que permite a você garantir que uma classe tenha apenas uma instância, enquanto provê um ponto de acesso global para essa instância.

Problema

O padrão Singleton resolve dois problemas de uma só vez, violando o princípio de responsabilidade única:

  1. Garantir que uma classe tenha apenas uma única instância: A razão mais comum para isso é controlar o acesso a algum recurso compartilhado, como uma base de dados ou um arquivo.
  2. Fornecer um ponto de acesso global para aquela instância: Assim como uma variável global, o padrão Singleton permite que você acesse algum objeto de qualquer lugar no programa, mas protege aquela instância de ser sobrescrita por outro código.

Solução

Todas as implementações do Singleton têm esses dois passos em comum:

  1. Fazer o construtor padrão privado, para prevenir que outros objetos usem o operador new com a classe singleton.
  2. Criar um método estático de criação que age como um construtor. Esse método chama o construtor privado por debaixo dos panos para criar um objeto e o salva em um campo estático. Todas as chamadas seguintes para esse método retornam o objeto em cache.

Estrutura

  1. Singleton: Declara o método estático getInstance que retorna a mesma instância de sua própria classe.
  2. Cliente: Usa o método getInstance para obter a instância única da classe Singleton.

Exemplo em PHP

Classe Singleton

class Database {
    private static $instance = null;
 
    // O construtor deve ser privado para evitar a criação direta de instâncias.
    private function __construct() {
        // Código de inicialização, como conexão com o banco de dados.
    }
 
    // Método estático para obter a instância única.
    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new Database();
        }
        return self::$instance;
    }
 
    public function query($sql) {
        // Lógica de consulta ao banco de dados.
        echo "Executando consulta: $sql\n";
    }
}

Código Cliente

$db1 = Database::getInstance();
$db1->query("SELECT * FROM users");
 
$db2 = Database::getInstance();
$db2->query("SELECT * FROM products");
 
// Ambas as variáveis $db1 e $db2 contêm a mesma instância.
if ($db1 === $db2) {
    echo "Ambas as variáveis contêm a mesma instância.\n";
}

Exemplo em Node.js

Classe Singleton

class Database {
    constructor() {
        if (Database.instance) {
            return Database.instance;
        }
 
        // Código de inicialização, como conexão com o banco de dados.
        Database.instance = this;
    }
 
    static getInstance() {
        if (!Database.instance) {
            Database.instance = new Database();
        }
        return Database.instance;
    }
 
    query(sql) {
        // Lógica de consulta ao banco de dados.
        console.log(`Executando consulta: ${sql}`);
    }
}

Código Cliente

const db1 = Database.getInstance();
db1.query("SELECT * FROM users");
 
const db2 = Database.getInstance();
db2.query("SELECT * FROM products");
 
// Ambas as variáveis db1 e db2 contêm a mesma instância.
if (db1 === db2) {
    console.log("Ambas as variáveis contêm a mesma instância.");
}

Aplicabilidade

  • Utilize o padrão Singleton quando uma classe em seu programa deve ter apenas uma instância disponível para todos seus clientes, como um objeto de base de dados único compartilhado por diferentes partes do programa.
  • Utilize o padrão Singleton quando você precisa de um controle mais estrito sobre as variáveis globais.

Prós e Contras

Prós

  • Você pode ter certeza que uma classe só terá uma única instância.
  • Você ganha um ponto de acesso global para aquela instância.
  • O objeto singleton é inicializado somente quando for pedido pela primeira vez.

Contras

  • Viola o princípio de responsabilidade única. O padrão resolve dois problemas de uma só vez.
  • O padrão Singleton pode mascarar um design ruim, por exemplo, quando os componentes do programa sabem muito sobre cada um.
  • O padrão requer tratamento especial em um ambiente multithreaded para que múltiplas threads não possam criar um objeto singleton várias vezes.
  • Pode ser difícil realizar testes unitários do código cliente do Singleton porque muitos frameworks de teste dependem de herança quando produzem objetos simulados.

Relações com outros padrões

  • Uma classe fachada pode frequentemente ser transformada em uma singleton já que um único objeto fachada é suficiente na maioria dos casos.
  • O Flyweight seria parecido com o Singleton se você, de algum modo, reduzisse todos os estados de objetos compartilhados para apenas um objeto flyweight.
  • As Fábricas Abstratas, Construtores, e Protótipos podem todos ser implementados como Singletons.