quinta-feira, janeiro 18, 2007

Design patterns: 1 - O onipresente Singleton e o conceito de aplicação

Desde o lançamento da Zend Engine 2, núcleo do PHP5, há 3 anos, venho presenciando um crescente interesse da comunidade PHP na orientação a objetos. Não é pra menos, afinal, foi nesta área que ela trouxe maiores impactos. Vale frisar que seu novo modelo de objetos já era ansiosamente aguardado. Lembrar da OO no PHP4 hoje, dá vontade de rir - ou chorar se houver necessidade implementar algo nesse sentido - basta lembrar dos incontáveis &'s.

Como um fenômeno inexplicável em qualquer linguagem com suporte maduro à OO, a moda no PHP pós-moderno são os padrões de projeto. Por favor, não há tempo para introduções. Deixaremos o arquiteto Cristopher Alexander, Martin Fowler, Erich Gamma e seus mosqueteiros para o seu professor de faculdade.

O que me chama à atenção é a quase que instantânea explanação acerca do padrão criacional GoF Singleton. Falou em design pattern (para os que não gostam de traduzir certos termos já amplamente conhecidos abaixo da linha do Equador em sua língua pátria), explicou o Singleton. Note que ele foi concebido num livro que tratou de C++, Eiffel, SmallTalk e trouxe diagramas em formatos mais livres anteriores à UML, ou seja, para a maioria dos programadores, definitivamente, não foi cunhado em linguagem cotidiana.

Mas afinal, por que todo mundo fala em Singleton? Bom, se o intuito de se catalogar padrões de projeto for promover o conhecimento de uma solução consistente para determinados problemas corriqueiros e gerar um vocabulário comum de entendimento universal, talvez estejamos diante de uma verdadeira obra-prima. Seria a gangue dos quatro (GoF – Gang of Four) composta por Leonardo, Michelangelo, Donatello e Rafael?

Talvez a resposta esteja exatamente no livro dos livros. Presente há mais de uma década na cabeceira de nove entre dez analistas de sistemas, a obra Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley, 1995) não é de fácil leitura. Seus exemplos não são os mais comerciais (quem leu deve lembrar do labirinto ou do editor de textos), mas atingiu o status cult. É bonito falar dele. O Singleton então faz o papel de padrão de projeto mais trivial e acessível dos 23 catalogados.

Recentemente, li quatro artigos em PHP sobre Singleton. Adler Medrado, no phpavancado.net, Diego Botelho, no phpbrasil.com, Felipe Ribeiro em seu blog, aqui mesmo no Blogger e Alexandre Altair de Melo em seu artigo sobre padrões de projeto elaborado para a primeira edição da revista eletrônica brazuca PHP Magazine (não confunda com sua xará alemã). Registro aqui meus parabéns pela iniciativa. Quando crescer, quero escrever para vocês =).

Numa definição breve, Singleton é uma implementação OO para situações onde é necessário que tenhamos apenas uma única instância (objeto) de determinada classe. Não sei que fixação é essa, mas o que eu mais vi em PHP sobre o assunto trouxe o exemplo de uma classe que criasse objetos para gerenciar conexões a um banco de dados. Vamos a ela então (código retirado do artigo do Alexandre Melo – nossa única ressalva é a declaração static do método getInstancia() que ele esqueceu de colocar):

  1. /**
  2. * Classe para se conectar com o banco de dados.
  3. */
  4. class ConectaBanco {
  5. /**
  6. * Propriedade flag utilizada para verificar se já
  7. * existe uma instância do objeto
  8. */
  9. private static $instancia = null;
  10. /**
  11. * Construtor.
  12. * Aqui está a grande sacada o construtor da classe é privado.
  13. * Assim só um outro método interno pode ter acesso,
  14. * a esse construtor.
  15. *
  16. * @return void
  17. */
  18. private function __construct() {}
  19. /**
  20. * Método que irá fornecer o ponto global para se
  21. * ter acesso a instância.
  22. *
  23. * @return ConectaBanco
  24. */
  25. public static function getInstancia() {
  26. if (is_null(ConectaBanco::$instancia)) {
  27. ConectaBanco::$instancia = new ConectaBanco();
  28. }
  29. return ConectaBanco::$instancia;
  30. }
  31. }
  32. ?>


O primeiro passo para implamentação do padrão Singleton é proibir a criação de objetos da classe em questão a partir de fora dela. Isso ocorre, vedando o acesso externo ao construtor com o modificador private. Assim, new ConectaBanco(), que invoca o construtor, só pode ser feito na própria ConectaBanco. Os membros de classe (static) $instancia e do método getInstancia() independem de objeto, existem a partir do momento em que a classe existir. O método instanciou um objeto ConectaBanco e o atribuiu à variável de classe ConectaBanco::$instancia apenas se esta ainda estiver nula. A partir de chamadas sucessivas, ele apenas retornará o conteúdo já atribuído.

Assim, nas linhas abaixo:

$conn1 = ConectaBanco::getInstancia();

$conn2 = ConectaBanco::getInstancia();

As variáveis $conn1 e $conn2 referenciam o mesmo objeto.

Críticas construtivas

Perdão ao Diego, mas sua classe misturou demais o HTML ao PHP.

Perdão ao Adler, mas seu código fugiu um pouco do conceito de Singleton. O método getInstancia() (no seu caso, singleton()) retorna uma instância de mysqli e não da própria classe, como reza a norma GoFiana forjada em ambientes mais fortemente tipados.

Perdão ao Alexandre, mas se eu me deparasse com esse exemplo como argumento para utilização do Singleton há alguns anos, provavelmente continuaria trabalhando com meus códigos procedurais onde toda página incluía um simples arquivo conexao.php que atribuísse o almejado recurso de conexão com o banco de dados numa simples variável global.

O conceito de aplicação

Conforme bem lembra Felipe Ribeiro (aliás, o povo agradece por sair do lugar comum e elaborar um exemplo diferente da conexão com o banco):

“Em PHP não existe o conceito de Aplicação, ou seja, cada vez que alguém acessa sua página, o que acontece é apenas a execução de um script numa thread do seu webserver, e mesmo que várias pessoas acessam simultaneamente o *mesmo* script, essas execuções são independentes, não há relação nenhuma entre elas.”

Cada nova requisição inicia um novo processo no servidor que é encerrado no momento que não houver mais nada a devolver ao browser.


Estaria o PHP fadado a não suportar o Singleton?

Permita-me concordar não por completo com Felipe. O conceito de aplicação pode sim ser definido pelo desenvolvedor. Recentemente, ao elaborar um sistema e-commerce orientado a objetos pensei da seguinte maneira: Considerei que a aplicação deveria estar disponível para cada cliente, enquanto ele navegar. Começando na sua chegada à página inicial e encerrando no fechamento do browser ou conclusão da compra. Nesse último caso, ela reinicia automaticamente.

Se você pensou em sessões, pensou o mesmo que eu. Observe a definição da classe Carrinho que elaborei (obviamente, não trouxe seus demais atributos e métodos de negócio para não acarretar num código demasiadamente extenso):

  1. class Carrinho {
  2. /**
  3. * Instância única do Carrinho
  4. * @var Carrinho
  5. */
  6. private static $instancia;
  7. /**
  8. * Método estático de acesso ao objeto Carrinho.
  9. * Antes de construir um novo Carrinho, ele verifica
  10. * se há algum já salvo na sessão do cliente.
  11. * Se houver, retorna ela. Só em último caso, retorna uma nova.
  12. *
  13. * @return Carrinho objeto único
  14. */
  15. public static function getCarrinhoAtual() {
  16. if (!isset(Carrinho::$instancia)) {
  17. Carrinho::$instancia = (isset($_SESSION['carrinho'])) ?
  18. $_SESSION['carrinho'] :
  19. new Carrinho();
  20. }
  21. return Carrinho::$instancia;
  22. }
  23. /**
  24. * Construtor privado
  25. */
  26. private function __construct() {}
  27. /**
  28. * Apenas aproveitei o destrutor para lançar o
  29. * Carrinho na sessão.
  30. * É necessário que o script execute um sesssion_start()
  31. */
  32. public function __destruct() {
  33. $_SESSION['carrinho'] = Carrinho::$instancia;
  34. }
  35. }
  36. ?>

Dessa forma, consegui criar um objeto Carrinho que será único até o usuário fechar o browser ou encerrar a sessão de alguma forma. O que vai depender de como o sistema deve gerenciar suas sessões. Para um Singleton único por toda a aplicação, provavelmente seria necessário elaborar algum mecanismo de serialização e acesso concorrente, mas não foi o que precisei neste caso.

O argumento para utilizar o Singleton agora, diferentemente da simples conexão com o banco é que nosso objeto pode ser inserido num contexto OO completo, possuindo associações com outras classes, como o Carrinho possuir uma coleção de objetos Item, constituírem um atributo de outro objeto Compra e assim por diante.

Enfim, a idéia desse artigo não foi exatamente explicar o que é o Singleton, mas mostrar que o propósito dos padrões de projeto é atender às necessidades do desenvolvedor mostrando-o uma saída a um problema que ele já deve ter, e não forçá-lo a programar de uma maneira rígida. Não acho legal ver as pessoas querendo apenas adotar os padrões simplesmente como eles são, estejam eles adequados ou não à sua realidade.


Marcadores: , ,