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.
Parte 4: Erros na Implementação de Typleton?
Apresentei a vocês uma implementação de Singleton que eu chamei de Typleton. Eu também 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 Injeção de Dependência, precisamos entender tal erro e resolvê-lo.
Programadores C++ experientes podem perceber o erro em Typleton logo à primeira vista (veja o código no post anterior). Mas nem todo mundo consegue. Então precisamos ver o erro em tempo de execução. Veja o código abaixo: Vamos usar o código apresentado na figura 2 para examinar melhor Typleton. Como você pode ver, na primeira linha, incluímos o arquivo de cabeçalho iostream. Sem ele, não podemos apresentar uma saída de dados para o programa. E um programa de testes sem saída de dados é como um carro sem rodas: inútil. Na segunda linha nós incluímos o Typleton.h, que nós definimos na página anterior desse artigo. Na quarta linha, importamos o namespace padrão do Standard C++, std, para evitar nomes qualificados completos de variáveis, então, começamos a função principal. A função principal é tão simples quanto um jogo-da-velha. Simplesmente instanciamos o template Typleton várias vezes chamando pela sua função access e mostrando o endereço de memória do ponteiro retornado.
Quando compilamos e rodamos o programa podemos ver uma saída similar à mostrada na Figura 3, abaixo: Na figura 3, acima, eu fui cuidadoso o suficiente para mascarar algumas informações 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 não ser paranóico.
Como pode ser visto na Figura 3, as primeiras linhas, antes da chamada a $ ./Typleton, mostram informações de compilação do programa. Typleton pertence a um projeto maior, que eu compilo usando CMake. Meu objetivo é explicar esse projeto inteiro nestes tutoriais de padrões de programação C++ - então você pode esperar por um monte de artigos, ainda.
Depois da chamada ao programa Typleton, nós vemos a sua saída de dados. Então, finalmente, podemos ver o bug: Quando instanciamos o template Typleton com um tipo específico, no caso acima int, ele se comporta como um Singleton, obtemos dele apenas um ponteiro para o objeto armazenado no endereço 1048944.
Mas se instanciamos Typleton novamente, com outro tipo (double, como mostrado na Figura 2), nós obtemos outra instância de Typleton, o objeto armazenado no endereço 1048912! Se você se lembra do que discutimos sobre templates C++ no post anterior, então sabemos que cada instância de um template é uma classe completamente nova para o compilador. Então, no caso de Typleton, essa classe template é, de fato, um Singleton para cada tipo que ela é instanciada.
Mas sendo um template ou não, um Singleton precisa ser instanciado uma vez, e apenas uma única vez, no programa inteiro. Portanto, Typleton não é um Singleton de verdade. Ele se comporta como um Singleton, para cada tipo com o qual o instanciamos, mas a classe, como um todo, não é 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 não é 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 próxima página.
Programadores C++ experientes podem perceber o erro em Typleton logo à primeira vista (veja o código no post anterior). Mas nem todo mundo consegue. Então precisamos ver o erro em tempo de execução. Veja o código abaixo: Vamos usar o código apresentado na figura 2 para examinar melhor Typleton. Como você pode ver, na primeira linha, incluímos o arquivo de cabeçalho iostream. Sem ele, não podemos apresentar uma saída de dados para o programa. E um programa de testes sem saída de dados é como um carro sem rodas: inútil. Na segunda linha nós incluímos o Typleton.h, que nós definimos na página anterior desse artigo. Na quarta linha, importamos o namespace padrão do Standard C++, std, para evitar nomes qualificados completos de variáveis, então, começamos a função principal. A função principal é tão simples quanto um jogo-da-velha. Simplesmente instanciamos o template Typleton várias vezes chamando pela sua função access e mostrando o endereço de memória do ponteiro retornado.
Quando compilamos e rodamos o programa podemos ver uma saída similar à mostrada na Figura 3, abaixo: Na figura 3, acima, eu fui cuidadoso o suficiente para mascarar algumas informações 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 não ser paranóico.
Como pode ser visto na Figura 3, as primeiras linhas, antes da chamada a $ ./Typleton, mostram informações de compilação do programa. Typleton pertence a um projeto maior, que eu compilo usando CMake. Meu objetivo é explicar esse projeto inteiro nestes tutoriais de padrões de programação C++ - então você pode esperar por um monte de artigos, ainda.
Depois da chamada ao programa Typleton, nós vemos a sua saída de dados. Então, finalmente, podemos ver o bug: Quando instanciamos o template Typleton com um tipo específico, no caso acima int, ele se comporta como um Singleton, obtemos dele apenas um ponteiro para o objeto armazenado no endereço 1048944.
Mas se instanciamos Typleton novamente, com outro tipo (double, como mostrado na Figura 2), nós obtemos outra instância de Typleton, o objeto armazenado no endereço 1048912! Se você se lembra do que discutimos sobre templates C++ no post anterior, então sabemos que cada instância de um template é uma classe completamente nova para o compilador. Então, no caso de Typleton, essa classe template é, de fato, um Singleton para cada tipo que ela é instanciada.
Mas sendo um template ou não, um Singleton precisa ser instanciado uma vez, e apenas uma única vez, no programa inteiro. Portanto, Typleton não é um Singleton de verdade. Ele se comporta como um Singleton, para cada tipo com o qual o instanciamos, mas a classe, como um todo, não é 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 não é 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 próxima página.
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. :-)