Design Pattern: Singleton

Permite que você garanta que uma classe tenha apenas uma instância, enquanto provê acesso global para ela.

O padrão Singleton resolve esses dois problemas de uma vez, mas por outro lado viola o princípio de responsabilidade única.

Explicando o problema

  1. Permite que você garanta que uma classe tenha apenas uma instância.

    Você deve estar se perguntando o motivo de alguém querer isso, mas um bom exemplo é quando precisamos usar em vários pontos da aplicação um recurso compartilhado, como por exemplo a conexão com o banco de dados. Quando criado um objeto, ao precisar usar ele novamente a aplicação irá te devolver o mesmo objeto criado anteriormente ao invés de criá-lo novamente.

  2. Provê acesso global para uma instância.

    Quem nunca criou aquela variável global no index para ser usado ao longo do projeto? Nunca fez? É, eu já fiz hehe

    São bem úteis, não irei mentir pra você, mas são bem inseguras, pois o seu conteúdo pode ser alterado por qualquer código e criar um efeito colateral na sua aplicação.

    Na mesma ideia de uma variável global, o Singleton permite que você acesse uma instância de uma classe globalmente em qualquer lugar da aplicação, mas previne que o seu conteúdo seja sobrescrito por outro código.

Como implementar

  • Crie um campo privado estático na classe para o guardar a instância.
<?php
class Database 
{
    private static $instance;
// Continuação da classe...
  • Declare um método de criação público e estático para obter a instância singleton Ex: getInstance.
// Inicio da classe...

public static function getInstance() 
{
    // Implementação da função 
}

// Continuação da classe...
  • Implemente a inicialização dentro do método estático. Ela deve criar um novo objeto na sua primeira chamada e colocá-lo no campo estático. O método deve sempre retornar aquela instância em todas as chamadas posteriores.
// Inicio da classe...

public static function getInstance() 
{
    if (is_null(self::$instance)) {
        echo 'Passou aqui uma só vez' . PHP_EOL;
        self::$instance = new self();
    }

    return self::$instance;
}

// Continuação da classe...

OBS: Se tiver trabalhando com multi thread, dever colocar um thread lock antes do operador new no método getInstance.

  • Pode parecer estranho, mas faça o construtor da classe ser privado. O método estático da classe vai conseguir chamar o construtor, mas irá previnir a criação através de outros objetos.
// Inicio da classe...

private function __construct() 
{
    $this->connection = 'conectado';
}

// Continuação da classe...
  • Vá para o código cliente e faça a chamada para o método estático do singleton.
$database = Database::getInstance();

Agora vamos ver todo o código da implementação.

<?php
class Database 
{
    private static $instance;
    private $connection;

    private function __construct() 
    {
        $this->connection = 'conectado';
    }

    public function query($sql) 
    {
        return 'Resultado da query: ' . $sql . PHP_EOL;
    }

    public static function getInstance() 
    {
        if (is_null(self::$instance)) {
            echo 'Passou aqui uma só vez' . PHP_EOL;
            self::$instance = new self();
        }
        return self::$instance;
    }
}

class User 
{
    public function getUser()
    {
        $database = Database::getInstance();
        return $database->query('SELECT * FROM Users;');
    }
}

class Transactions 
{
    public function getTransaction()
    {
        $database = Database::getInstance();
        return $database->query('SELECT * FROM Transactions;');
    }
}

// Case 1
$user = new User;
echo $user->getUser();

// Case 1
$transaction = new Transactions;
echo $transaction->getTransaction();

$database = new Database; //fatal error

Nas classes que representam as models User e Transactions a instância está dentro da função, mas numa implementação

Conclusão

Use o singleton quando você ver a necessidade de uma classe possuir apenas uma instância disponível e global em sua aplicação e tbm caso precise de um controle mais estrito sobre as variáveis globais.

É isso galera, chegamos no fim de mais um artigo inté o próximo!