domingo, 22 de janeiro de 2012

RSpec, factory_girl e transações com o banco de dados

Ontem estava quebrando a cabeça para entender como meus testes estavam rodando, pois para mim, a cada execução destes o meu banco de dados estava limpo. Isso é o que acontence quando se utiliza o Test::Unit, porém utilizando o RSpec é diferente.

Nada como uma boa noite de sono. Hoje pela manhã, com a cabeça fresca, fiz as buscas que realmente interessavam e a documentação está toda lá, no site do RSpec: https://www.relishapp.com/rspec/rspec-rails/docs/transactions

Além disso, quando se utiliza o FactoryGirl o comportando é diferente de quando se utiliza fixtures. Com fixtures, o banco é populado com o conteúdo declarado. Já com FactoryGirl o objeto é criado e salvo quando se faz a chamada Factory(:meu_modelo). Isso acabou me confundindo, pois no começo eu pensava que o FactoryGirl iria me retornar o que estava no banco, pois com uma sintaxe parecido é isso o que o fixtures faz.

O RSpec cria transações no banco para cada bloco it "..." do, sendo assim, caso algum objeto seja salvo fora deste escopo ele é mantido no banco e deve ser limpo manualmente.

segunda-feira, 9 de janeiro de 2012

O mundo é plano, porém ainda possui muitas ondulações

Já faz uns 5 meses que comprei um Kindle. Acho que deste que conheci o PC, há 20 anos atrás, não sentia tanta satisfação por possuir um aparelho eletrônico. Ele é um aparelho fantástico, agradável para ler, leve, possui um dicionário integrado, toca MP3, fácil para fazer anotações e marcar páginas. Possui todas as qualidades para substituir livros de papel (revistas e jornais também).

Apesar de tudo isso, acho que a penetração de tais aparelhos na sociedade ainda é muito pequena. Quantas pessoas que você conhece possuem um Kindle?

A Amazon fez um trabalho realmente incrível na produção deste aparelho e para incentivar os consumidores a adotar esta nova tecnologia, vendem abaixo do custo, por quê? Seu interesse é a venda de livros eletrônicos. Com a venda de poucos títulos é possível cobrir o custo de fabricação do aparelho e ter lucro.

Apesar disso, o governo brasileiro ainda não tomou nenhuma atitude para difundir este equipamento por aqui. Ao contrário, deu subsídios a Positivo na fabricação do Positivo Alfa, que apesar de possuir um dicionário em português, não apresenta nenhuma outra vantagem em relação ao Kindle. É mais lento, mais caro, a bateria dura menos, etc. Resumindo, não é um competidor.

Eu adoro as publicações da The Pragmatic Bookshelf e gosto muito também da qualidade do serviço deles. Quando você compra um livro para o Kindle, ele vai automaticamente para o seu aparelho e toda vez que o seu livro é atualizado com correções ou passa de uma publicação beta para a publicação oficial você recebe a atualização. Eles também mandam seus livros paraa sua conta do Dropbox. Definitivamente, um serviço excelente e muito cômodo!

Nos últimos tempos tenho lido rumores de que as editoras estão negociando para ter seus títulos no Kindle, porém não saberemos quando isso se tornará realidade nem como, mas já é uma luz no fim do túnel. Com isenção de impostos e bons acordos para oferta de conteúdo a experiência com a tinta eletrônica pode ser ainda mais incrível!

sábado, 31 de dezembro de 2011

Bibliotecas para Programação de Jogos

No meu último post escrevi a respeito do desenvolvimento de uma pequena parte do que estou fazendo. Mesmo sendo uma tarefa simples, a complexidade para atingi-la de acordo com os meus objetivos, de forma portável, me dá a dimensão do trabalho que é.

Isto me fez buscar bibliotecas que dê suporte para programação de jogos abstraindo detalhes do ambiente, porém não me fornecendo abstrações em cima da OpenGL, pois quero aprender tal tecnologia, sendo assim nada melhor do que eu mesmo implementar tais abstrações.

Achei algumas bibliotecas interessantes: SDL, Allegro, PLIBClanLib e Orx. Para decidir qual utilizar adotei alguns parâmetros de comparação entre elas:
  • Jogos implementados
  • Exemplos
  • Documentação
  • Plataformas suportadas
  • Bindings/Scripts suportados
Logos após ler um pouco mais sobre cada uma das bibliotecas, dispensei a PLIB, pois apesar de ser bastante modular e possui módulos bastante interessantes, já está bastante defasada e sem atualizações. Umas das maiores diversões de participar de projetos de software livre é sua comunidade vibrante, sendo assim, projeto descartado. Vamos aos demais!

As plataformas suportadas pelas restantes são:




Windows Linux MacOS X iOS Android
Orx X X X X X
SDL X X X - -
Allegro X X X X -
ClanLib X X X - -
PixelLight X X - - X

Após verificar as funcionalidade de cada uma resolvi fazer um test-drive. Nada melhor do que guiar um pouco o carro para sentir suas vibrações.

Comecei experimentando a Orx, por ser a que roda nas mais diversas plataformas. Logo me decepcionei, não encontrei nenhuma maneira rápida e fácil de executar as coisas pela linha de comando. Depois que achei esse tutorial no site deles desisti de vez. Odeio ficar dependente de ambientes de programação.

Achei bastante interessante as funcionalidades apresentadas pela ClanLib, porém achei a documentação fraca, grande parte dos exemplos não rodam, pois a biblioteca suporta OpenGL 3, sendo que as implementações do Linux em sua maioria suportam a versão 2.1. Além disso, achei a comunidade fechada demais, com poucos canais de comunicação.

Num primeiro momento achei a PixelLigth simples, porém olhando ela com mais calma, percebi que ela também estava bastante próxima das engines Crystal Space 3D e Ogre.

Por último testei a Allegro, que me agradou muito, bastante unix-like. Documentação de fácil acesso, muitos exemplos interessantes, possui o segundo melhor suporte multi-plataforma, compatível com SDL, diversos canais de comunicação e uma comunidade muito grande, com diversos jogos do-it-yourself. Pura diversão! Não deixe de conferir o demo "speed" nos seus fontes. Nem vou dizer que é minha escolha :)

quinta-feira, 3 de novembro de 2011

Inicializando os Gráficos: Janela e Contexto 3D

Comecei escrever o código de uma engine. Minhas expectativas são:
  • Utilizarei C++ para o core;
  • Utilizarei TDD;
  • Multi-plataforma;
  • Altamente modular;
Quero aprender TDD, então nada melhor do que aplicar o conceito na prática. Além disso, quero utilizá-lo da forma mais xiita, ou seja, escrever sempre um teste que justifique a mudança do código. No início esta condição parece bastante extrema, mas é uma forma muito útil de você pensar em design antes de codificar qualquer coisa. Realmente sinto que demoro mais para fazer algo desta forma, mas o que é feito se torna mais consistente.

Não pretendo escrever código multi-plataforma de cara, mas pretendo escrevê-lo de uma forma que este trabalho seja facilitado no futuro. Inicialmente irei focar em OpenGL e utilizarei o SO Linux.

Finalmente, quando digo que o código será altamente modular, significa que quero diminuir ao máximo o acoplamento entre as classes / módulos da engine.

Comecei a escrever um pouquinho de código e já tenho encontrado dificuldades. Inicialmente quero realizar 3 tarefas:
  1. Criar uma janela no X11;
  2. Criar um contexto OpenGL;
  3. Ter uma interface para adicionar este contexto a uma janela;
Para realizar esta tarefa necessito de uma conexão com o X11. Realizar tal tarefa é trivial, porém este recurso será necessário em diversos pontos da aplicação, o que me leva a pensar no padrão Singleton.

Esta solução me deixa com uma pulga atrás da orelha, pois na criação de um objeto que represente uma janela, precisarei ter acesso a conexão com o servidor gráfico, que me leva a necessidade de criar uma interface para acessar esta propriedade. Tal organização expõe um conceito  do ambiente de execução na interface entre duas classes, o que não é legal para uma solução multi-plataforma.

Uma solução seria tornar o Singleton que representa a conexão com o servidor gráfico em uma fábrica de janelas e contextos, porém uma janela também não poderia ter uma interface para adicionar um contexto a ela, pois isto também iria expor detalhes da implementação de OpenGL, que no caso do X11 utiliza a extensão GLX. Espero que o ponto já esteja claro, ou seja, escrever uma biblioteca que não deixe "vazar" informações de seu ambiente é difícil, ainda mais quando os recursos são complexos.

Estas dificuldades me levaram ao livro Cross-Platform Development in C++. Através dele pude perceber que a tarefa é realmente difícil e exige bastante conhecimento. Desta forma começo a duvidar se estou seguindo pelo caminho certo, pois se continuar neste ritmo começarei a escrever algo do jogo só em 2020 :).

Irei me aprofundar no assunto, mas ao mesmo tempo irei analisar algumas bibliotecas que forneçam um ambiente multi-plataforma básico para o desenvolvimento de um jogo e que com certeza será assunto para um próximo post.

segunda-feira, 19 de setembro de 2011

Programando com Exceções

Escrevendo uma classe em C++ fiquei com uma dúvida. Como tratar um erro que acontece em um construtor? Imediatamente tive duas ideias, retornar NULL ou configurar uma flag na classe. A primeira logo descobri que não era possível, pois os construtores não permitem retornos. A segunda, apesar de possível, achei deselegante e muito suscetível a erros.

Foi a partir deste momento que me perguntei se exceções seria a forma correta de tratar o problema e após ler alguns livros esta é a forma unânime para se notificar um erro que acontece em um construtor, porém na busca desta resposta encontrei muito mais informações deste mecanismo que acabaram me convencendo que exceções são uma ótima forma de tratar erros de tempo de execução em uma aplicação.

É importante lembrar que alguns erros são de lógica e não deveriam acontecer durante a execução do software. Estes normalmente são falhas do programador, sendo assim, a forma correta de tratá-los é através de chamadas a macro assert, as quais podem ser desabilitadas facilmente em produção definindo a variável NDEBUG durante a compilação.

Através das exceções é possível separar o fluxo de código "normal" daquele que é responsável pelo tratamento do erro, tornando a execução de um determinado fluxo mais limpa e fácil de ler, ou seja, sem distrações. Além disso, um erro, muitas vezes, acontece em um determinado componente do sistema que não possui informações suficientes para saber como este deve ser tratado, pois seu contexto é muito limitado para identificar as condições que o provocaram, impossibilitando qualquer medida para corrigi-lo, neste caso, faz mais sentido lançar uma exceção e deixar uma parte da aplicação que tenha um contexto mais adequado tratar o erro.

Outra questão que tive quando comecei a pensar sobre exceções foi a respeito dos recursos que já haviam sido alocados durante a chamada do construtor. O que iriam acontecer com eles?

Caso uma exceção ocorra durante a execução de um construtor, o destrutor da classe não é chamado, sendo assim, seus recursos não são liberados. A solução para este problema é chamada por "resource allocation is initialization". Ao invés de fazer uma chamada a uma biblioteca específica para se obter um recurso, cria-se uma classe que irá representar este recurso que é inicializado através do construtor. A Wikipédia traz uma ótima descrição a respeito deste assunto: http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization

Outro benefício interessante do uso de exceções é a chamada dos destrutores das classes que estão na pilha, pois quando uma exceção é lançada, grosseiramente falando, é como se o seu código fosse executando vários "return" até encontrar uma parte do código que a trate. Isto evita que seus objetos fiquem perdido, causando memory leaks.

Também procurei informações sobre exceções para descobrir alguns conselhos de como utilizá-las, pois também tinha dúvidas se seria um bom recurso para utilizar na programação de jogos, devido ao overhead que adicionam. Pelo que li é totalmente plausível utilizá-las (além disso a engine Ogre3D as utilizam), porém é necessário entendê-las muito bem para conseguir visualizar o impacto que terão durante sua chamada.

Os conselhos apresentados por Bjarne Stroustrup no livro C++ Programming Language são muito bons:
  • Use exceções para tratamento de erros;
  • Não use exceções onde estruturas de controle locais são suficientes;
  • Use a técnica "resource allocation is initialization" para gerenciar recursos;
  • Nem todo programa precisa tratar exceções;
  • Use "resource allocation is initialization" e tratamento de exceções para manter invariantes;
  • Minimize o uso de try-blocks. Use "resource allocation is initialization" ao invés de código de tratamento explícito;
  • Nem toda função precisa tratar todos os erros possíveis;
  • Lance uma exceção para indicar uma falha em um construtor;
  • Evite lançar exceções em destrutores;
Além desses existem muitos outros conselhos, porém acredito que isto já é o bastante para atiçar o apetite de qualquer um que tenha lido até aqui. Os livros que achei mais interessantes sobre o assunto foram:
Ótimas referências!

quinta-feira, 5 de maio de 2011

Model-View-Presenter (MVP) no .NET Compact Framework

Ao contrário de padrões de projetos, padrões arquiteturais dão linhas diretrizes muito vagas e nenhuma sugestão concreta de implementação. Ao tentar aplicar o padrão MVP segui o básico do framework apresentado no paper MVP: Model-View-Presenter. The Taligent Programming Model for C++ and Java, no entanto achei o modelo ineficiente para ser utilizado em uma aplicação Windows Forms, já que neste ambiente temos a disposição componentes que podem ser manipulados visualmente durante a programação da solução.

Utilizando o framework supra-citado, não era possível faz uso do ambiente gráfico para gerar automaticamente o código de criação da tela, desta forma passei a me perguntar se um framework que levasse em consideração estas questões já não existia, pois definir visualmente as telas da aplicação é um recurso muito útil para ser desperdiçado pela adoção do MVP.

Encontrei o framework MVC# que apesar de ser antigo, oferece suporte ao MVP, inclusive no .NET Compact Framework. Além do MVP, ele adota uma idéia de tarefas, onde uma tarefa pode englobar vários pares de views/presenters, sendo que a tarefa se torna um lugar para armazenamento de informações necessárias por view/presenter distintos.

O site criado pelo programador é muito bom e contém uma boa descrição de como ele pensou, projetou e implementou a solução. Além disso, são descritos vários exemplos que vão bem além de uma simples tela.

Apesar de estar satisfeito com este framework, ele ainda pode melhorar, porém para as minhas necessidades atuais me atendeu muito bem, já que minha aplicação era simples. Em uma aplicação mais complexa seria necessária ter um controle melhor em relação as visões e das tarefas (criação/destruição). Uma coisa boa é que o código-fonte do framework é aberto, permitindo modificações e melhorias, sendo assim, caso precise delas tenho chances de implementá-las. Viva a influência do software livre!


segunda-feira, 18 de abril de 2011

.Net Compact Framework e TDD

Estou trabalhando no desenvolvimento de um aplicativo para um dispositivo móvel e resolvi utilizar TDD convencido de todas os benefícios que a abordagem traz para o desenvolvimento de aplicações.

Estou utilizando o Visual Studio 2008 que já traz o suporte necessário para criação de testes unitários, porém o ambiente me deixou um tanto decepcionado na aplicação da técnica, já que que tal metodologia exige que os testes possam ser executados rapidamente, porém após uma sorte descobri que os testes podem ser executados rapidamente (pule para o último parágrafo caso não queira ler a viagem).

Eu já havia criado meu projeto e implementado algumas coisas, sim eu sei que o TDD não recomenda isso, mas eu também não concordo com todas as práticas de TDD, ainda mais quando se está desenvolvendo código que não se sabe exatamente como será a estrutura, como é o meu caso. Após implementar algumas coisas e acreditar que a interface dos meus componentes não sofreriam mudanças significativas criei um projeto de testes.

Ao começar a pensar em como implementaria os testes me deparei com um problema: como iria simular os componentes que interagem com o hardware e seu retorno? A primeira coisa que me veio a cabeça foi mocks, porém para minha surpresa não existe nenhuma biblioteca que dê suporte a mocks no Compact Framework. Foi então que encontrei o artigo do Martin Fowler "Mocks Aren't Stubs". Problema resolvido. Criei stubs para os componentes que se comunicam com hardware e para as telas gráficas, pois a parte gráfica também é um pouco difícil de testar, já que se deve instanciar componentes gráficos que necessitam de interação com usuário. Como estou utilizando MVP para organizar as camadas da minha aplicação a visão é bastante simples e não faz muito sentido o esforço para testá-la como se fosse em um ambiente real, sendo assim, criei stubs para as telas também.

Após implementar alguns testes percebi que a execução era extremamente lenta, pois a IDE iniciava o emulador do móvel e executava os teste dentro deste ambiente. Em média este processo levava em torno de 5 minutos. Se considerarmos um cenário onde executamos 100 testes por dia, o que não é nenhum exagero, cada execução demorando 5 minutos, ficaríamos mais de 8 horas só executando testes.

Após esta experiência decepcionante abandonei a idéia de utilizar testes para mais tarde descobrir que se o projeto de testes for removido e depois adicionado novamente a solução, os testes não são executados no simulador, mas sim na própria máquina, o que é muito mais rápido e torna viável a aplicação de TDD. Não encontrei nenhuma explicação para este comportamento. Pretendo aplicar testes novamente aos meus projetos, mas infelizmente não será neste, pelo menos não por enquanto, pois os prazos se tornaram curtos para implementá-los agora :(