Algum humor e C++ Design Patterns (parte 2)

Continuamos a discussão a respeito de Singletons, iniciada na primeira parte desse artigo até chegarmos à Injeção de Dependência. As coisas agora se tornam profundamente C++. Transformamos os Singletons em contêineres, para torná-los efetivamente reutilizáveis e discutimos a teoria que existe por trás dos Templates do C++ e de como a metaprogramação é feita nessa linguagem.

[ Hits: 27.520 ]

Por: Paulo Silva Filho em 25/10/2010 | Blog: http://psfdeveloper.blogspot.com


Injeções de Dependência em Singletons



Se você leu o arquivo de código fonte Singleton.h, deve ter percebido cinco novas classes: LockerException, Locker, DummyLocker, SynchroTypleton e SynchroTempleton. E a adição da função membro estática synchroInitialize na classe SingletonController.

Por que eu fiz dessa forma? Por que copiar e modificar levemente classes existentes se eu posso usar herança para isso? A resposta é simples: esse é um daqueles casos nos quais é muito mais fácil copiar e colar do que aplicar o paradigma ou a receita de programação presente na ordem do dia. Também era preciso garantir que as contrapartidas sincronizadas de Templeton e Typleton estão isoladas dessas classes não sincronizadas. Nascemos para ser espertos, e livres mesmo para usar padrões ruins - mas de forma inteligente. Nós não nascemos para sermos os mais elegantes e rigorosos designers do mundo. Não somos escravos dos patterns: somos seus mestres.

Vamos entender o papel de cada nova classe usada para prover Singletons sincronizados. A primeira classe que precisamos entender é LockerException. Essa classe representa uma exceção de bloqueio (lock) de processamento. Programar em ambientes multitarefa é lidar com esse tipo de exceção durante todo o tempo. Então Featherns oferece uma classe padrão para essas exceções.

As próximas classes a serem compreendidas são Locker e DummyLocker. Os Singletons sincronizados (SynchroTypleton e SynchroTempleton), para serem sincronizados, precisam receber uma classe de sincronização que implemente a interface definida por Locker. Como você pode ver, Locker é uma classe com duas funções membro virtuais puras e elas definem o modo que os Singletons sincronizados restringem o acesso ao seu conteúdo. Se Locker é uma interface, DummyLocker é a sua implementação padrão. E ela é burra. DummyLocker não retringe acesso de nada. Ela é apenas uma implementação cuja função é ser ignorada. Criar um Singleton sincronizado sem definir um Locker personalizado é criar um Singleton não sincronizado.

Antes de explicar os Singletons sincronizados, eu lhes darei uma justificativa para a adição de uma nova função em SingletonController. Não era impossível, para nós, usar a mesma função initialize, presente em SingletonController, em SynchroTempleton. Mas, se fizéssemos isso, Templeton e SynchroTempleton seriam o mesmo Singleton, isto é, se alguém instanciasse um Templeton, SynchroTempleton não poderia ser instanciado, e vice-versa. Então criei uma nova função para ser usada apenas por SynchroTempleton, isolando-o completamente de Templeton.

Agora podemos seguir para os Singletons Sincronizados. Se olharmos para o código dos Singletons Sincronizados, SynchroTypleton (localizado na linha 114 de Singleton.h) e SychroTempleton (localizado na linha 230 de Singleton.h), a maior diferença notada entre eles e suas contrapartes não sincronizadas não é a infra-estrutura de sincronização que eles fornecem, mas a sua natureza como contêineres. SynchroTypleton e SynchroTempleton não mais contêm ponteiros.

Eles contêm, agora, cópias completas de objetos. Esse completo distanciamento do seu conteúdo em relação à sua antiga natureza como ponteiros tornou inviável a aplicação de qualquer tipo de herança entre os Singletons Sincronizados e os seus respectivos irmãos não sincronizados. Mas essa mudança era necessária, uma vez que a sincronização só é efetiva se estamos lidando com instâncias reais de objetos, não com seus ponteiros. Os Singletons Sincronizados apenas liberam cópias dos seus objetos conteúdo para o usuário, e, também, apenas permitem substituição sincronizada desses objetos pelos usuários.

Usar objetos no lugar de ponteiros como conteúdo nos Singletons Sincronizados nos dá as mais completas capacidades de sincronização e uma grande vantagem: nós não precisamos mais lidar com indireção de ponteiros. Isso torna o código fonte mais fácil de ler e muito mais prazeroso de ser escrito. Se você analisar os exemplos que eu vou lhes fornecer, mais tarde, verão um código mais amigável quando usamos os Singletons Sincronizados.

Mas, por outro lado, nós temos muito mais prejuízos do que benefícios: menor eficiência, uma vez que lidar com cópias de objetos precisa de muito mais processamento do que lidar com ponteiros, e restrições na definição de que objetos podem ser armazenados no contêiner. Se um contêiner armazena ponteiros, então qualquer tipo pode ser armazenado pelo contêiner.

Mas se o contêiner armazena objetos, esse objeto precisa ser de um tipo primitivo (como int, char, or um ponteiro) ou ele precisa ser uma instância de uma classe bem comportada, que implemente um constructor padrão, sem parâmetros, que implemente um constructor de cópia ou, algumas vezes, que sobreponha o operador de atribuição. Mas esse é o custo de uma infra-estrutura de sincronização, e o programador precisa pensar duas vezes antes de usar um Singleton Sincronizado no lugar de um não sincronizado.

Agora nós alcançamos o ponto realmente importante. Os Singletons Sincronizados não fornecem nenhum tipo de sincronização. Eles fornecem uma infra-estrutura. Quem fornece o código de sincronização é o usuário. Esse código de sincronização pode ser implementado usando o Boost, POSIX Threads ou CriticalSection objects da API Win32. Featherns não poderia fornecer nenhum tipo de sincronização porque esse tipo de código é dependente de plataforma.

Então ele conta com alguma implementação da classe Locker fornecida pelo usuário. Featherns depende de uma classe externa para sincronizar os seus Singletons. Você pode ver nos exemplos como implementar essas classes externas. Mas as classes no exemplo (SynchroTempleton.cpp), VerboseDummyLocker e UpperVerboseDummyLocker são apenas exemplos, e não fornecem nenhuma sincronização, apenas apresentam como elas devem ser chamadas por SynchroTypleton e SynchroTempleton. Esse tipo de dependência em recursos externos por um programa, biblioteca ou função é o que nós chamados de Injeção de Dependência.

O usuário, para usar os Singletons Sincronizados corretamente, precisa implementar um Locker e injetá-lo na classe de Singleton da sua escolha. Essa injeção é feita em tempo de compilação através de uma instanciação de template. Daí o nome do pattern: Injeção de Dependência. Mas Injeção de Dependência é muito mais do que isso, e, no próximo artigo, iremos discuti-la melhor. Então chegaremos até a Inversão de Controle.

Depois de todas essas elucidações, vamos para os exemplos. Você pode pegar os códigos de exemplo clicando nesse link. Depois disso, tenha a certeza de que você possui a última versão do CMake (CMake 2.8.2) rodando no seu sistema. CMake pode vir em algum pacote já incluso na sua Distribuição de Linux e usuários de Mac OS X podem usar o MacPorts para consegui-lo. Usuários de FreeBSD podem usar o Ports do sistema. Mas, para sistemas com versões mais antigas de CMake nos seus pacotes (como Debian Lenny) ou para usuários de Windows, vocês podem obter o CMake no seu site [1]. Eu não darei instruções de compilação para sistemas Windows (incluindo MinGW ou Cygwin), mas, para compliar e rodar os exemplos, em qualquer sistema Unix (incluindo o Mac OS X, talvez o Cygwin), apenas baixe o arquivo e proceda como o descrito abaixo:

tar -jxvf feathernscpatt-0.0.1.tar.bz2
$ mkdir BUILD
$ cd BUILD
$ cmake ../feathernscpatt/samples/
$ make
$ cd bin


Agora, com os exemplos rodando, comece a explorar a árvore de arquivos de Featherns, no diretório feathernscpatt. Você encontrará o diretório de inclusão de arquivos de cabeçalho de Featherns. Apenas adicione esse diretório ao include path de seu programa para usar os Singletons. Ou apenas copie o arquivo Singleton.h, para qualquer lugar que você quiser, para usá-lo em seu projeto. Divirta-se! Até a próxima!

Créditos:

[1] O CMake pode ser baixado nesse endereço: http://www.cmake.org/cmake/resources/software.html

Página anterior    

Páginas do artigo
   1. Introdução
   2. Qual é essa dos Templates, no final das contas?
   3. Uma reimplementação muito mais sofisticada do Singleton
   4. Erros na Implementação de Typleton?
   5. De Typleton para Templeton
   6. A implementação de Templeton
   7. Injeções de Dependência em Singletons
Outros artigos deste autor

Algum humor e C++ Design Patterns (parte 1)

Leitura recomendada

Sinais em Linux

Utilizando técnicas recursivas em C e C++

Android NDK: Desmistificando o acesso a códigos nativos em C

openCertiface: Biométrica Facial em nuvem baseada em software livre

Programação de Jogos com SDL

  
Comentários
[1] Comentário enviado por julio_hoffimann em 25/10/2010 - 23:36h

Oi Paulo,

Estou impressionado com a sua destreza no assunto! Há tempos que não aprendia coisas tão interessantes assim em POO. O artigo está incrível, conceitos de alto nível explicados de uma forma simples, sem deixar de ser filosófica.

O Templeton foi algo esplendoroso, quase cai da cadeira quando você o manifestou. Com esta série, começo a ver que metaprogramação vale muito a pena e que apesar de ser um investimento árduo, não hesitarei em aprendê-la.

Obrigado por compartilhar tanto conhecimento, desejo sucesso e boa sorte com a Featherns.

Abraço!

P.S.: Quando li no agregador de feeds: Design Patterns (parte 2), parei tudo que estava fazendo para prestigiar o artigo. :-)

[2] Comentário enviado por psfdeveloper em 26/10/2010 - 01:56h

Caro Julio,

eu fico muito feliz com tal manifestação de apreço... Eu vi que eu esqueci algumas partes do texto em inglês, mas o pessoal do Viva o Linux não tirou. Não sei se foi intencionalmente, mas não vou reclamar.

Metaprogramação é um dos tópicos mais importantes e interessantes da programação como um todo. Quase todas as linguagens possuem capacidades de metaprogramação. Algumas surpreendentemente poderosas, ao ponto de você poder quase que alterar a linguagem inteira (como o Lisp, com suas "macros" - vale a pena dar uma olhada, mesmo que seja como uma referência teórica - recomendo o Racket - http://racket-lang.org/ - que no Debian/Ubuntu pode ser obtido através do pacote plt-scheme), alterar comportamento de objetos e de seus derivados em tempo real (como em Javascript - na verdade uma capacidade de Javascript que só vi em Javascript - talvez na sua linguagem mãe, o Self), ou outras fortemente flexíveis por serem totalmente em tempo de execução - como o Python ou o Perl, em maior grau. O Java possui uma abordagem mista para a metaprogramação (com os Generics), que envolve a necessidade de Reflexion, com aspectos estáticos e dinâmicos.

Mas a linguagem que colocou a Metaprogramação na ordem do dia realmente foi C++, que foi duramente criticada por anos a fio justamente por causa do excesso de poder dado a essa capacidade, pelos templates e em momento de compilação. Quando ficou claro o poder da Metaprogramação por templates, diversas linguagens começaram a copiá-la.

Metaprogramação é um assunto árduo, cuja teoria é mais difícil que a codificação em si, mas dou toda força. Vai fundo.

E muitíssimo obrigado por prestigiar meu trabalho. Não tenho o que dizer.

Abraços.

[3] Comentário enviado por edgardiniz em 27/10/2010 - 18:36h

Ainda não li essa segunda parte, apressei o comentário para não esquecer mais tarde.

Com relação aos códigos, você pode utilizar algo como o pastebin.com, que permite fazer o embed dos códigos, e conta com tudo o que você precisa, sem a necessidade de usar imagens com o código e fornece-lo no final.

A não ser que o VOL não permita embedding nos artigos, não sei...

Mas cara, você está de parabéns, estou aprendendo muito com seus artigos.

Continue com o trabalho excelente.

[4] Comentário enviado por psfdeveloper em 27/10/2010 - 20:08h

Caro edgar,

o VOL não permite embedding de tags html no código, só com muitas restrições. E todo código fonte é posto disponível para download na última página do artigo.

Abraços.


Contribuir com comentário