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: , ,

6 Comentários:

Blogger independent frame disse...

Parabéns Berardo!!! Vc conseguiu da alguma utilidade para o blogger!!!
bom trabalho!!!
:)

quarta-feira, janeiro 31, 2007 10:30:00 AM  
Blogger Felipe Ribeiro disse...

Olá Berardo!

Parabéns pelo artigo, acho que estamos seguindo a mesma linha de raciocínio.

No meu artigo quando eu falei da inexistência do conceito de aplicação é que no caso do PHP difere por exemplo do Java, onde eu crio um Singleton e a minha JVM deixa ele "vivo" lá enquanto meu container estiver rodando, que é o conceito do life-cycle da aplicação propriamente dita.

Gostei do seu exemplo do carrinho, com persistencia do objeto na sessão, é uma solução bem bacana, é a melhor solução quando se trata de PHP, mas de repente pode estar quebrando um pouco o conceito do MVC. Não acredito que gerenciar sessões seja função do Model mas sim do Controller, talvez eu esteja falando besteira, mas é como eu vejo isso.

Você está de parabéns, gostei muito do seu artigo e acho que complementa bem o que eu escrevi.

E tou colocando um link desse seu blog lá no meu!

Um abraço!

segunda-feira, fevereiro 12, 2007 4:17:00 PM  
Anonymous Anônimo disse...

Olá Belardo. Seu artigo está realmente muito bom. Mesmo trabalhando há mais de 5 anos com OO (Delphi, Java, C++) ainda me considero um iniciante por não ter tido tempo de me aprofundar no assunto.

Mas estou aqui para lhe fazer algumas perguntas.

Você conhece as funções shmop (Funções de Memória Compartilhada
) do PHP? Elas existem desde a verão 4.0.3. Já as usei para criar um contador simples de usuário (que acabou não dando 100% certo pelas limitações de troca de iformações entre Browser e Servidor WEB).
Eu pensei no seguinte. Se eu posso serializar um objeto e o salvar em Sessão pra que ele esteja disponível a toda "aplicação" então eu poderia fazer o mesmo utilizando shmop.
Eu serializaria (UFFAA!!) o objeto e o colocaria ná área de memória comum do S.O. (UNIX e Windows 2000 ou superior suportam) e entaum resgataria esses valores para qualquer thread PHP.
Eu pensei em fazer o mesmo que as extensões de BD fazem com conexões persitentes (claro que sei que quem controla isso não é o PHP e sim o próprio SGBD) capturando algo que esteja ativo.
Estaria eu delirando???

terça-feira, fevereiro 13, 2007 7:26:00 AM  
Blogger Felipe Ribeiro disse...

Beleza Berardo!

Entendi seu ponto de vista e acabei por concordar já que estamos lidando com PHP :D

Vi que você é de Recife, sou de Campina Grande, de repente podemos começar a movimentar uma comunidade PHP no nordeste, seria interessante! :D

Caso queira me contactar por e-mail ou gtalk: felipernb@gmail.com

Um abraço

terça-feira, fevereiro 13, 2007 7:30:00 AM  
Blogger Marcio Muzzi disse...

Salve Berardo,

Quando li o artigo do Felipe Ribeiro, achei que estaria lendo o primeiro e único com uma visão sóbria sobre o padrão Singleton no PHP. Após refletir sobre o artigo do Felipe, pensei também em sessões. Sem entrar no mérito onde isso deve entrar na arquitetura MVC, ou se sessão é a solução definitiva, fica agradecimento à colaboração sua e do Felipe. Ambos estão de parabéns.
Abraços.

Marcio Muzi
www.marciomuzi.eti.br

sábado, maio 05, 2007 9:45:00 AM  
Blogger Unknown disse...

uma pergunta, se tenho várias execuções de consulta na mesma página não é válido o exemplo de Singleton para conexões com o banco de dados?

sexta-feira, fevereiro 05, 2010 6:46:00 AM  

Postar um comentário

Assinar Postar comentários [Atom]

<< Página inicial