You are on page 1of 19

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

Autor: Paulo Silva Filho <psfilho.developer at gmail.com> Data: 13/09/2010 Introduo estranho que exista documentao a respeito de Java Design Patterns aos montes (d uma procurada no Google), muita documentao disponvel a respeito de tcnicas de programao em Perl e Python e uma grande quantidade de documentao a respeito de estratgias de programao sobre as linguagens PHP, Lisp e mesmo sobre Haskell. Mas no temos tanta documentao simples e acessvel a respeito de padres de desenvolvimento em C ou C++ (faa com C++ a mesma pesquisa que fez com o Java). Ser isso algum tipo de preconceito a respeito dessas linguagens ou algum tipo de inveja sobre as mais disseminadas ferramentas de programao do mundo? Os desenvolvedores C++ so to inteligentes que eles no precisam aprender nada a respeito de engenharia de software, incluindo os padres de desenvolvimento (ou patterns, somente)? Voc precisa concordar comigo que C++, e C, tambm, no possuem o maior nmero de programadores, nem so as linguagens de programao mais populares na Internet, nem no mundo, mas stacks inteiras de software foram desenvolvidas utilizando tais linguagens. Se o C for completamente includo nessa cota, podemos dizer que praticamente todos os kernels dos sistemas operacionais, bancos de dados, interfaces grficas, drivers de dispositivos e perifricos, sistemas avanados de jogos e um monte de aplicativos foram feitos utilizando a dupla C/C++. Ento, por que to pouca documentao, em comparao com as outras linguagens, a respeito de estratgias de programao nessas linguagens to influentes? Eu penso que os programadores C++ esto to ocupados programando com uma linguagem to difcil e ilegvel que eles no podem lidar com coisas inteis como tutoriais ou documentao. Mas, eu, um Analista de Sistemas ocioso, que sabe programar em C++ muito bem (estou impressionado com isso: um analista de sistemas programando em C++?), decidi preencher essa falta de tutoriais e estou oferecendo a vocs essa pequena pea dourada de programao: Design Patterns em C++. Na realidade esses patterns no so especficos de C++, mas eles so uma implementao muito especfica deles nessa linguagem. E eu vou usar fortemente do estilo do C++! Eu adoro montes de ponteiros, atributos constantes, herana virtual e mltipla, class friends e tudo o que o C++ possui que poderoso, difcil de ler e duro de entender. No por causa disso que adoramos tanto C++, no final das contas? Alguns padres

apresentados aqui possuem nomes diferentes dos dados pela literatura, mas eu no me lembro de seus nomes originais, ento eu os batizei com outros nomes que me parecem mais intuitivos. Se vocs souberem os nomes dos patterns que eu apresentar com um nome diferente, me mandem um comentrio para que eu possa corrigir. Para ler esse tutorial voc precisa saber como programar em C++. Esse no um tutorial de C++. Se voc precisa de um tutorial C++, pea-me que eu poderei escrever um, no futuro, mas, neste momento, estou interessado apenas nos programadores avanados. E voc ir precisar de um bom compilador C++. A opo bvia, para usurios de Linux o gcc. Eu ainda no aconselho o uso do Clang [1]. Eu preciso testar todos os patterns no Clang antes de promover esse compilador. Os design patterns que pretendo apresentar, nessa srie de dez curtos artigos so: 1. Singleton Pattern 2. Inversion of Control (Dependency injection) Pattern 3. Observer Pattern 4. Facade Pattern 5. Callback Handler Pattern 6. Trap Pattern 7. Proxy Pattern 8. Monad Pattern 9. Multiple-Read, Single-Write Pattern 10. Mapping Functor Pattern

Depois desse discurso todo, vamos a eles! Crditos: [1] O Clang o compilador otimizado do projeto LLVM (Low Level Virtual Machine), e pode ser encontrado neste link.

Pattern Singleton Se h alguma coisa que eu realmente odeio, essa coisa uma varivel global. Variveis globais transformam programas em uma massa enrolada e desconexa como um espaguete sem a necessidade de um nico comando goto. O padro Singleton nos ajuda a evitar variveis globais e, ainda, inibe alguns erros de linkagem que variveis globais podem provocar quando compilamos nossos programas. Entre uma varivel global e um Singleton, eu sempre prefiro o Singleton, a no ser que seja impossvel us-lo. Um singleton apenas uma classe que pode ter um, e apenas um objeto como instncia. Toda vez que tentamos acessar uma instncia de um Singleton, acessamos sempre a mesma instncia.

Uma implementao bastante interessante de Singleton pode ser vista abaixo: class Singleton { public: static Singleton *access(); virtual int getValue(); virtual void setValue(const int &val); protected: Singleton() { this->value = 0; } ~Singleton() {} protected: int value; }; Singleton *Singleton::access() { static Singleton *single = NULL; if(single == NULL) { single = new Singleton(); } return single; } int Singleton::getValue() { return this->value; } void Singleton::setValue(const int &val) { this->value = val; } Muito simples o padro Singleton, no? E muito, muito til. Qualquer varivel global pode ser colocada dentro da classe Singleton, para evitar dados desorganizados espalhados pelo programa todo. Use o padro Singleton para trazer forte organizao ao seu cdigo, a no ser que voc goste de lidar com um monte de variveis globais, uma vez que elas tornam a programao em um jogo de caa ao tesouro - mas, se voc perder esse jogo, estar realmente derrotado (perdendo o seu prprio emprego, por exemplo). Uma das caractersticas mais interessantes do Singleton que tanto o seu Constructor quanto o seu Destructor so variveis protegidas, dentro da classe. Elas no podem ser acessada fora da prpria classe Singleton. Uma vez que um Singleton instanciado, no faz sentido fornecer um Destructor para apag-lo da memria. E o Singleton no causar vazamentos de memria, uma vez que ele no se multiplicar dentro do programa (ele uma instncia nica, de toda forma), e Singletons, geralmente, so utilizados durante todo o perodo de execuo do sistema. Eles sero desalocados automaticamente pelo sistema operacional quando o programa terminar. Singletons, geralmente, so muito teis em contextos de multi-threading. Nesse caso, as funes de acesso aos dados da classe precisam de algum tipo de sincronizao, como

pode ser visto no exemplo abaixo: class SynchroSingleton { public: static SynchroSingleton *access(); virtual int getValue(); virtual void setValue(const int &val); protected: SynchroSingleton() { this->value = 0; } ~SynchroSingleton() {} void lock(); void unlock(); protected: int value; /** * Variables needed to lock * and unlock functions: */ // ... }; SynchroSingleton *SynchroSingleton::access() { static SynchroSingleton *single = NULL; if(single == NULL) { single = new SynchroSingleton(); } return single; } int SynchroSingleton::getValue() { return this->;value; } void SynchroSingleton::setValue (const int &val) { this->value = val; } void SynchroSingleton::lock() { /** * Specific synchronization code, * using Critical Section * objects. This is platform * specific and outside the scope

* of this tutorial. In Linux or * BSD you can use POSIX threads. * */ } void SynchroSingleton::unlock() { /** * Specific synchronization code, * using Critical Section * objects. This is platform * specific and outside the scope * of this tutorial. In Linux or * BSD you can use POSIX threads. * */ } No exemplo acima, a atualizao da varivel value no precisa ser sincronizada porque, na maioria dos sistemas, uma atribuio simples a um inteiro uma instruo atmica CPU, mas, como o cdigo acima apenas um exemplo, ele mostra como usar a sincronizao em uma instncia de um Singleton. O pattern de Multiple-Read, Single Write, que ser apresentando futuramente, tambm pode ser usado em conjuno com o Singleton. O cdigo a seguir mostra como chamar uma instncia do Singleton: Singleton.cpp: #include <iostream> using namespace std; class Singleton { public: static Singleton *access(); virtual int getValue(); virtual void setValue (const int &val); protected: Singleton() { this->value = 0; } ~Singleton() {} protected: int value; }; Singleton *Singleton::access() { static Singleton *single = NULL; if(single == NULL) { single = new Singleton();

} return single; } int Singleton::getValue() { return this->value; } void Singleton::setValue(const int &val) { this->value = val; } int main() { cout << "Hello world!" << endl; Singleton *s = Singleton::access(); cout << "Singleton address: " << (int) s << endl; cout << "Let's load \"another\" " << singleton instance..." << endl; Singleton *another_s = Singleton::access(); cout << "New instance singleton " << "address: " << (int) another_s << endl; cout << "Then: what's up, Doc?" << endl; return 0; } Ento, apenas compile o programa e rode: $ g++ Singleton.cpp -o sing $ ./sing Hello world! Singleton address: 1048912 Let's load "another" singleton instance... New instance singleton address: 1048912 Then: what's up, Doc? Como voc pode ver, acima, ambas as chamadas a Singleton::access() retornam o mesmo endereo de memria, portanto o mesmo ponteiro para o mesmo objeto, uma vez que ele no pode ser destrudo fora da prpria classe Singleton.

No prximo artigo discutiremos a respeito do pattern Inversion of Control.

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


Autor: Paulo Silva Filho <psfilho.developer at gmail.com> Data: 25/10/2010 Introduo Na primeira parte dessa srie ns apresentamos o design pattern (padro de design) Singleton. Agora, nosso assunto o infame padro Inverso de Controle, tambm conhecido como Injeo de Dependncia. Injeo de Dependncia e Inverso de Controle so to fortemente relacionados que, algumas vezes, esses dois padres so classificados como apenas um - e eu vou seguir essa tendncia, uma vez que acho que uma Inverso de Controle no existe, de fato, sem algum tipo de Injeo de Dependncia. Assim que descrevermos esses padres, voc entender a minha posio, mas eu espero que voc desenvolva a sua prpria opinio a respeito desse assunto. Para comear a discusso a respeito de padres aqui apresentados, revisitaremos o padro Singleton, que ser refinado e, ento, ser usado para hospedar uma dependncia externa. Por fim, generalizaremos a Injeo de Dependncia atravs da aplicao de uma Inverso de Controle. Para os amantes de Linux e de Open-source, usaremos um software da Microsoft como um antigo exemplo de inverso de controle. Mas vocs precisam perdoar a minha heresia - os produtos da Microsoft tambm so software e eles usam alguns padres de design C++ bastante interessantes. Esse assunto to extenso que eu peo a voc que no espere que o esgotemos nessa parte do tutorial. Lidaremos, agora, somente como o iniciozinho do assunto. Os artigos, a partir de agora, se tornaro menos amigveis, e eles exigiro algum nvel de conhecimento de Metaprogramao com Templates. Algum conhecimento sobre como o compilador gera o cdigo C++ intermedirio a partir dos templates tambm necessrio, mas no se preocupe, explicarei as partes mais obscuras a voc. Vamos comear com uma introduo Metaprogramao com Templates. Crditos: [1] Esse artigo foi adaptado de uma srie de posts do meu website,

em: http://psfdeveloper.blogspot.com

Qual essa dos Templates, no final das contas? Na minha opinio, templates so um dos mais importantes, superestimados, subvalorizados e depreciados aspectos da linguagem de programao C++. A Comunidade Java parece odiar os Templates do C++, como pode ser visto na documentao a respeito de Generics do Java. No link apresentado em [1], o autor chama Templates C++ de "um Extravagante Processador de Macros". A prestigiosa revista Dr.Dobbs [2] possui uma opinio diferente. Precisamos entender que os engenheiros e o quadro de funcionrios da Sun (e, agora, da Oracle), no geral, tentam vender Java como uma panacia; ento eles precisam inflar as ferramentas do Java e depreciar qualquer tipo de concorrncia. A opinio deles muito enviesada, mas, para mim, inaceitvel dizer que uma das melhores e mais bem engenheiradas ferramentas de Metaprogramao (os Templates de C++) so apenas um "Processador de Macros Extravagante". Eu concordo com a opinio de que os Templates de C++ so exatamente como um processador de macros que, sendo um Processador de Macros, daqueles muitssimo poderosos. Qualquer pessoa que sabe programar com a linguagem M4 entende do que estou falando. Por outro lado, podemos dizer que Generics do Java so uma ferramenta de metaprogramao que utiliza uma "Extravagante Forma de Remoo da Tipos" para garantir genericidade e capacidade de conteinerizao ao Java, dependendo fortemente de reflexo para alcanar padres complexos. E eu odeio reflexo em linguagens de programao. Mesmo quando elas so completamente disponveis (como em JavaScript, Python ou Perl), eu raramente uso, e quando uso, o fao com muito cuidado, uma vez que acho que o uso de reflexo pode tornar um programa to badernado quando o uso de variveis globais. Como o Java uma linguagem com capacidades dinmicas muito maiores do que C++, Generics podem ser eficientes porque a linguagem pode contar com ferramentas de tempo de execuo para garantir o rigor de uso dos tipos - o que quase impossvel com C++, por conta de sua anmica RTTI (Run-Time Type Information, algo como "informao de tipagem em tempo de execuo"). Mas eu sou um programador Java tambm, e no quero participar da rivalidade que existe entre essas linguagens. No vou compar-las, mais, mas voc precisa entender que alguns dos padres descritos aqui so praticamente impossveis de serem reproduzidos usando Generics do Java. Voltando ao C++, Templates so uma forma de programao que roda em tempo de compilao. Eles so, apenas, uma pequena linguagem de programao inclusas dentro do C++, feita para trabalhar com o cdigo C++ como seus dados. Essa linguagem permite a criao de pequenos subprocedimentos (as prprias classes ou funes "templatezadas") cuja implementao o seu prprio texto "templatezado" e seus parmetros de entrada so constantes de tipos inteiros, Classes, Estruturas ou qualquer outra definio de tipo. Templates

C++ so completamente conscientes do Sistema de Tipagem de C++, o que uma capacidade muito grande e crtica. Templates suportam recursividade, sendo possvel programar loops com eles. Outro aspecto presente nos templates a existncia de Guards (guardies). Um guard apenas um conjunto de definies de uma mesma expresso (no nosso caso, um Template), com diferentes constantes como valores de entrada, promovendo, para cada valor de entrada especfico, um comportamento diferente; ou seja, um guard um condicional. Com essas caractersticas, os Templates C++ se tornam Turing Completos. Veja as referncias presentes no Artigo da Wikipdia presente em [4] e vocs vo verificar o que eu estou falando. Depois dessa explicao a respeito de o que um Template C++, precisamos entender como eles trabalham, quando compilamos um programa. Quando definimos um Template, eles so como uma definio de classe: estticos e inativos. Mas, cada vez que chamamos um template com um dado parmetro (os valores que ficam circundados por <..>), que pode ser uma constante ou um tipo, ns instanciamos o template. Antes de chamarmos o compilador propriamente dito, o compilador de templates do C++ ir criar uma cpia do cdigo do template, substituindo os parmetros pelos valores dados quela instncia em particular. Para cada instanciao, uma nova cpia do cdigo ser gerada pelo compilador. Ento o cdigo seguir para o compilador C++ propriamente dito e gerar o objeto binrio, com diferentes identificadores e smbolos para cada instncia do template. Quando revisitarmos o padro Singleton, isso ficar mais claro. Os Templates C++ so to poderosos, que a mais influente e abrangente Biblioteca de C++ (na minha opinio), o Boost [3], depende fortemente do uso de Templates, e mesmo pesquisa de ponta em metaprogramao feita usando essa biblioteca. Para todo programador C++, o Boost conhecimento obrigatrio. Depois desse contedo profundamente terico, vamos para os padres de design. Vamos revisitar o Singleton. Crditos: [1] Artigo Oficial de Java Generics da Sun (agora Oracle): http://www.oracle.com/technetwork/articles/javase/generics-136597.html [2] Revista Dr.Dobbs: http://www.drdobbs.com/184401818 [3] O Boost pode ser encontrado em: http://www.boost.org [4] A wikipdia possui um arquivo poderoso a respeito de metaprogramao com Templates, que pode ser visto em:http://en.wikipedia.org/wiki/Template_metaprogramming

Uma reimplementao muito mais sofisticada do Singleton

Agora que ns conhecemos o suficiente a respeito de Templates C++, podemos reescrever a classe Singleton de forma a habilit-la a receber qualquer tipo de valor. Ns iremos transformar o nosso Singleton em um continer. Minha primeira proposta de reimplementao est apresentada na figura 1:

Figura 1 O cdigo acima uma figura porque, a partir de agora, os programas de exemplo se tornaro mais e mais complexos e precisarei de indentao, highlighting e numerao de linha adequados. No fim dos posts, eu apresentarei verses em texto dos exemplos, adequados para copiar e colar. A implementao de singleton, acima, quase igual apresentada na parte anterior desse artigo; mas, agora, ela pode ser parametrizada com um tipo, de forma que no precisamos mais de herana, nem de reimplementao da funo access(), para usarmos esse singleton em trabalhos reais. No entanto, essa implementao tem um erro muito, muito sutil. Eu dei a essa classe o nome de Typleton devido a esse erro, e ns vamos ver por qu. Essa classe, no final das contas, no realmente um Singleton. Antes de falarmos a respeito do erro no fragmento de cdigo acima, leia a definio dessa classe cuidadosamente. Algumas pessoas acham a sintaxe de Templates do C++ apavorante. E ela pode se tornar ainda pior, mas eu evitarei cdigos ilegveis. O erro acima um erro lgico, localizado na definio da funo access, na linha 4 da figura acima. Estude esse cdigo um pouco mais. Na prxima pgina ns trabalharemos em cima do erro apresentado nesse programa.

Erros na Implementao de Typleton? Apresentei a vocs uma implementao de Singleton que eu chamei de Typleton. Eu tambm lhes falei que Typleton possui um erro muito sutil que impede que essa classe seja um verdadeiro Singleton. Uma vez que precisamos de um verdadeiro Singleton para implementar a Injeo de Dependncia, precisamos entender tal erro e resolv-lo. Programadores C++ experientes podem perceber o erro em Typleton logo primeira vista (veja o cdigo no post anterior). Mas nem todo mundo consegue. Ento precisamos ver o erro em tempo de execuo. Veja o cdigo abaixo:

Figura 2 Vamos usar o cdigo apresentado na figura 2 para examinar melhor Typleton. Como voc pode ver, na primeira linha, inclumos o arquivo de cabealho iostream. Sem ele, no podemos apresentar uma sada de dados para o programa. E um programa de testes sem sada de dados como um carro sem rodas: intil. Na segunda linha ns inclumos o Typleton.h, que ns definimos na pgina anterior desse artigo. Na quarta linha, importamos o namespace padro do Standard C++, std, para evitar nomes qualificados completos de variveis, ento, comeamos a funo principal. A funo principal to simples quanto um jogo-da-velha. Simplesmente instanciamos o template Typleton vrias vezes chamando pela sua funo access e mostrando o endereo de memria do ponteiro retornado. Quando compilamos e rodamos o programa podemos ver uma sada similar mostrada na Figura 3, abaixo:

Figura 3 Na figura 3, acima, eu fui cuidadoso o suficiente para mascarar algumas informaes pessoais que aparecem no prompt do shell do meu computador. De outra forma, algumas pessoas mal intencionadas poderiam me encontrar e me matar - para dizer que estou apenas tentando no ser paranico. Como pode ser visto na Figura 3, as primeiras linhas, antes da chamada a $ ./Typleton, mostram informaes de compilao do programa. Typleton pertence a um projeto maior, que eu compilo usando CMake. Meu objetivo explicar esse projeto inteiro nestes tutoriais de padres de programao C++ - ento voc pode esperar por um monte de artigos, ainda. Depois da chamada ao programa Typleton, ns vemos a sua sada de dados. Ento, finalmente, podemos ver o bug: Quando instanciamos o template Typleton com um tipo especfico, no caso acima int, ele se comporta como um Singleton, obtemos dele apenas um ponteiro para o objeto armazenado no endereo 1048944. Mas se instanciamos Typleton novamente, com outro tipo (double, como mostrado na Figura 2), ns obtemos outra instncia de Typleton, o objeto armazenado no endereo 1048912! Se voc se lembra do que discutimos sobre templates C++ no post anterior, ento sabemos que cada instncia de um template uma classe completamente nova para o compilador. Ento, no caso de Typleton, essa classe template , de fato, um Singleton para cada tipo que ela instanciada. Mas sendo um template ou no, um Singleton precisa ser instanciado uma vez, e apenas uma nica vez, no programa inteiro. Portanto, Typleton no um Singleton de verdade. Ele se comporta como um Singleton, para cada tipo com o qual o instanciamos, mas a classe, como um todo, no um Singleton, no final das contas. Uma vez que eu achei o comportamente dessa classe bastante interessante e, de certa forma, til, mantive-a na minha biblioteca, mas me lembrando de que ela no um verdadeiro Singleton.

Como transformar Typleton em um verdadeiro Singleton? A resposta uma nova classe que eu chamei de Templeton. Mas isso assunto apenas para a prxima pgina.

De Typleton para Templeton O primeiro assunto de que precisamos nos lembrar que nossa implementao de Singleton, chamada Typleton, no um Singleton de verdade. Isso acontece porque Typleton uma classe template e, como todo Template do C++, cada instncia sua, para cada tipo usado como parmetro, uma nova classe para o compilador. Ento, Typleton um Singleton para cada tipo que ele instancia. Podemos verificar todas essas coisas lendo o post anterior dessa srie. Agora, precisamos resolver esse problema, transformando Typleton em um verdadeiro Singleton, fazendo-o, como Template, instancivel apenas uma nica vez. The solution for Typleton problem is simple, but surpreendently hard. We just must to know if the template class is already instatiated, unregarding its type parameter, and forbid a new instantiation. I've found a bold and neat solution for this problem. And the next section of this post will show it. A soluo para o problema de Typleton simples, mas surpreendentemente difcil. Ns apenas precisamos saber se a classe template foi instanciada, independentemente do tipo que foi usado como seu parmetro, e proibir uma nova instanciao. Eu encontrei uma soluo inteligente e sucinta para esse problema. Antes de explicar a soluo para o problema de Typleton, precisamos fazer uma racionalizao. C++ uma linguagem na qual a maioria das decises de design dos programas feita utilizando-se construes aplicadas em tempo de compilao. Isso est em absurdo contraste com linguagens mais dinmicas como Java, Python ou Lisp. Templates do C++ so inteligncia executada em tempo de compilao, inseridas dentro de um programa feito em C++. Ento, para manter consistncia de design, todas as solues para problemas com templates devem ser feitas usando construes e cdigos executados em tempo de compilao? A resposta para a questo acima imediata: No. Ponto. Templates C++ so ferramentas muito complexas dessa linguagem e, apesar de serem Turing Completas, a linguagem dos Templates C++ uma linguagem funcional. Ela no possui variveis globais, nem estado, nem atribuies. completamente impossvel definir algum comportamento em uma classe template que interfira no comportamento de outra classe template. Talvez alguma soluo extica em tempo de compilao exista para tal problema, mas ela seria complicada, estranha, esotrica e, muito provavelmente, muito difcil de entender e de ler o seu cdigo. Ento eu procurei por uma soluo de tempo de execuo para o problema de Typleton. A

soluo de tempo de execuo colocar a funo que verifica a instanciao do Singleton fora de qualquer template, ento essa funo no seria de nenhuma forma instancivel. Ento eu enclausurei essa funo dentro de uma classe, cujo contedo s pode ser lido pelas suas classes filhas, para evitar o uso de funes globais quando elas no so necessrias. Finalmente, eu criei uma nova classe chamada Templeton, exibindo o mesmo comportamento de Typleton, mas cujas instncias so verificadas pela funo fornecida pela sua classe parental. Complexo? De forma alguma. Difcil de entender? Talvez. Ento precisamos ver o cdigo, na prxima pgina.

A implementao de Templeton Typleton completamente auto-suficiente. Essa classe autocontida e no precisa de nenhuma outra classe. Por outro lado, Templeton precisa de algumas classes de apoio. Por causa disso, e para organizar o cdigo e a proliferao de classes, eu criei um namespace chamado featherns que ir conter todas as classes apresentadas nesse tutorial, de todos os Design Patterns que eu lhes mostrar. Featherns o nome do projeto que eu criei para hospedar o cdigo deste tutorial. Trata-se de uma combinao das palavras Feather (pena) e Patterns, resultando em Featherns. O nome significa que este projeto, como uma biblioteca, leve como uma pena e lida com Design Patterns. Featherns ser uma biblioteca composta somente por arquivos de cabealho, assim como algumas bibliotecas do Boost. E eu pretendo que ela seja completamente porttil, sem dependncias externas. Essas dependncias externas, por ironia, podem ser usada em Featherns atravs de Injeo de Dependncia (como veremos mais tarde nesse mesmo post). Acompanhando a classe parental de Templeton, cujo nome SingletonController, precisamos definir uma exceo que ser lanada se Templeton for instanciada mais de uma vez. Chamei essa classe de MultiSingletonException e ela uma classe derivada da classe std::exception fornecida pela Biblioteca Padro C++. Mas isso no suficiente, precisamos vincular Templeton corretamente a SingletonController, e essa vinculao correta atravs de herana privativa, no atravs da herana pblica, que muito mais comum. Vamos ao cdigo fonte:

Figura 4 Como pode ser visto, depois de todas as explicaes dadas acima, a codificao de Templeton bastante simples. Templeton usa uma funco de controle externa, chamada SingletonController::initialize, como pode ser visto na linha 30 da Figura 4; no lugar de uma funo local, como ocorre em Typleton. Uma vez que SingletonController no um template, ele no instancivel para diversos tipos, e a sua funo de controle sempre a mesma em todo o programa, no importando que classe foi usada para instanciar Templeton. Templeton realmente um Singleton de verdade. E um daqueles bastante restritivos. Ele s pode ser instanciado uma nica vez em todo o programa, incluindo as suas bibliotecas compartilhadas e em qualquer biblioteca de componentes que o programa utilize. uma fora, mas, tambm, uma fraqueza. Templeton to restritiva que ns precisaremos relax-lo um pouquinho para deix-lo efetivamente til para o uso em Inverso de Controle. Mas esse post a respeito de Singletons, no de Inverses de Controle. Portanto deixaremos para resolver esse problema mais tarde, no momento adequado. Agora que tempos uma codificao real de um Singleton, chegado o momento de mostrar alguns exemplos. Mas no vou faz-lo. Irei, de forma sdica, ainda mais fundo dentro de Typleton e de Templeton. Essas duas classes so contineres de ponteiros. Isso bom. Gerenciamento de ponteiros, em C++, nos d completo poder sobre o polimorfismo existente nas funes virtuais.

Atribuio de ponteiros completamente segura em multithreading porque esses comandos so traduzidos pelo C++ (e tambm pelo C) em instrues de processamento atmicas. Mas o uso de Templeton e de Typleton seguro em multithreading apenas se as suas classes parmetro tambm forem seguras. Isso significa que todo controle de acesso a threads precisa ser feito pelos usurios dessas classes. Isso ruim, mas em Featherns no poderia ser diferente. Controle de acesso a threads cdigo dependente de plataforma e Featherns tem como objetivo ser autocontida e independente de plataforma. Mas eu posso dar uma pequena ajuda, oferecendo na biblioteca classes que definam um padro de implementao de segurana em multithreading nos Singletons. Featherns no oferecer controle de acesso a threads, mas ir oferecer como e onde colocar o controle de acesso. Agora chegamos em um ponto de inflexo. Explicar em mnimos detalhes como implementar segurana em multithreading em Typleton e Templeton seria cansativo, e no adicionaria nenhum valor informacional a esse tutorial. Ento ofereo a vocs o link para a primeira verso liberada do arquivo de cabealho que contm Typleton e Templeton. Obtenha tal arquivo neste link. D uma parada para descansar. Leia o arquivo Singleton.h com cuidado. Agora o momento de partirmos para as Injees de Dependncia.

Injees de Dependncia em Singletons Se voc leu o arquivo de cdigo fonte Singleton.h, deve ter percebido cinco novas classes: LockerException, Locker, DummyLocker, SynchroTypleton e SynchroTempleton. E a adio da funo membro esttica synchroInitialize na classe SingletonController. Por que eu fiz dessa forma? Por que copiar e modificar levemente classes existentes se eu posso usar herana para isso? A resposta simples: esse um daqueles casos nos quais muito mais fcil copiar e colar do que aplicar o paradigma ou a receita de programao presente na ordem do dia. Tambm era preciso garantir que as contrapartidas sincronizadas de Templeton e Typleton esto isoladas dessas classes no sincronizadas. Nascemos para ser espertos, e livres mesmo para usar padres ruins - mas de forma inteligente. Ns no nascemos para sermos os mais elegantes e rigorosos designers do mundo. No 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 exceo de bloqueio (lock) de processamento. Programar em ambientes multitarefa lidar com esse tipo de exceo durante todo o tempo. Ento Featherns oferece uma classe padro para essas excees.

As prximas classes a serem compreendidas so Locker e DummyLocker. Os Singletons sincronizados (SynchroTypleton e SynchroTempleton), para serem sincronizados, precisam receber uma classe de sincronizao que implemente a interface definida por Locker. Como voc pode ver, Locker uma classe com duas funes membro virtuais puras e elas definem o modo que os Singletons sincronizados restringem o acesso ao seu contedo. Se Locker uma interface, DummyLocker a sua implementao padro. E ela burra. DummyLocker no retringe acesso de nada. Ela apenas uma implementao cuja funo ser ignorada. Criar um Singleton sincronizado sem definir um Locker personalizado criar um Singleton no sincronizado. Antes de explicar os Singletons sincronizados, eu lhes darei uma justificativa para a adio de uma nova funo em SingletonController. No era impossvel, para ns, usar a mesma funo initialize, presente em SingletonController, em SynchroTempleton. Mas, se fizssemos isso, Templeton e SynchroTempleton seriam o mesmo Singleton, isto , se algum instanciasse um Templeton, SynchroTempleton no poderia ser instanciado, e vice-versa. Ento criei uma nova funo para ser usada apenas por SynchroTempleton, isolando-o completamente de Templeton. Agora podemos seguir para os Singletons Sincronizados. Se olharmos para o cdigo dos Singletons Sincronizados, SynchroTypleton (localizado na linha 114 de Singleton.h) e SychroTempleton (localizado na linha 230 de Singleton.h), a maior diferena notada entre eles e suas contrapartes no sincronizadas no a infra-estrutura de sincronizao que eles fornecem, mas a sua natureza como contineres. SynchroTypleton e SynchroTempleton no mais contm ponteiros. Eles contm, agora, cpias completas de objetos. Esse completo distanciamento do seu contedo em relao sua antiga natureza como ponteiros tornou invivel a aplicao de qualquer tipo de herana entre os Singletons Sincronizados e os seus respectivos irmos no sincronizados. Mas essa mudana era necessria, uma vez que a sincronizao s efetiva se estamos lidando com instncias reais de objetos, no com seus ponteiros. Os Singletons Sincronizados apenas liberam cpias dos seus objetos contedo para o usurio, e, tambm, apenas permitem substituio sincronizada desses objetos pelos usurios. Usar objetos no lugar de ponteiros como contedo nos Singletons Sincronizados nos d as mais completas capacidades de sincronizao e uma grande vantagem: ns no precisamos mais lidar com indireo de ponteiros. Isso torna o cdigo fonte mais fcil de ler e muito mais prazeroso de ser escrito. Se voc analisar os exemplos que eu vou lhes fornecer, mais tarde, vero um cdigo mais amigvel quando usamos os Singletons Sincronizados. Mas, por outro lado, ns temos muito mais prejuzos do que benefcios: menor eficincia, uma vez que lidar com cpias de objetos precisa de muito mais processamento do que lidar com ponteiros, e restries na definio de que objetos podem ser armazenados no continer. Se um continer armazena ponteiros, ento qualquer tipo pode ser armazenado pelo continer.

Mas se o continer armazena objetos, esse objeto precisa ser de um tipo primitivo (como int, char, or um ponteiro) ou ele precisa ser uma instncia de uma classe bem comportada, que implemente um constructor padro, sem parmetros, que implemente um constructor de cpia ou, algumas vezes, que sobreponha o operador de atribuio. Mas esse o custo de uma infra-estrutura de sincronizao, e o programador precisa pensar duas vezes antes de usar um Singleton Sincronizado no lugar de um no sincronizado. Agora ns alcanamos o ponto realmente importante. Os Singletons Sincronizados no fornecem nenhum tipo de sincronizao. Eles fornecem uma infra-estrutura. Quem fornece o cdigo de sincronizao o usurio. Esse cdigo de sincronizao pode ser implementado usando o Boost, POSIX Threads ou CriticalSection objects da API Win32. Featherns no poderia fornecer nenhum tipo de sincronizao porque esse tipo de cdigo dependente de plataforma. Ento ele conta com alguma implementao da classe Locker fornecida pelo usurio. 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 so apenas exemplos, e no fornecem nenhuma sincronizao, apenas apresentam como elas devem ser chamadas por SynchroTypleton e SynchroTempleton. Esse tipo de dependncia em recursos externos por um programa, biblioteca ou funo o que ns chamados de Injeo de Dependncia. O usurio, para usar os Singletons Sincronizados corretamente, precisa implementar um Locker e injet-lo na classe de Singleton da sua escolha. Essa injeo feita em tempo de compilao atravs de uma instanciao de template. Da o nome do pattern: Injeo de Dependncia. Mas Injeo de Dependncia muito mais do que isso, e, no prximo artigo, iremos discuti-la melhor. Ento chegaremos at a Inverso de Controle. Depois de todas essas elucidaes, vamos para os exemplos. Voc pode pegar os cdigos de exemplo clicando nesse link. Depois disso, tenha a certeza de que voc possui a ltima verso do CMake (CMake 2.8.2) rodando no seu sistema. CMake pode vir em algum pacote j incluso na sua Distribuio de Linux e usurios de Mac OS X podem usar o MacPorts para consegui-lo. Usurios de FreeBSD podem usar o Ports do sistema. Mas, para sistemas com verses mais antigas de CMake nos seus pacotes (como Debian Lenny) ou para usurios de Windows, vocs podem obter o CMake no seu site [1]. Eu no darei instrues de compilao 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 diretrio feathernscpatt. Voc encontrar o diretrio de incluso de arquivos de cabealho de Featherns. Apenas adicione esse diretrio 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 prxima!

You might also like