domingo, 28 de abril de 2013

Dynamic Method Binding

Já não é novidade que a partir da versão 4 do .NET Framework e da linguagem C# é possível declarar objetos com tipagem dinâmica. Veja este link para mais detalhes.

Em C#, quando um objeto é declarado como dynamico compilador nos permite invocar qualquer método a partir desse objeto, sem checar em tempo de compilação se este método existe ou não. Esse recurso é conhecido como Dynamic Method Binding e apenas em tempo de execução o Framework irá informar, através de uma exceção, que o método invocado não existe.

Exemplo: 
dynamic objetoDesconhecido = ObtemObjetoDesconhecidoDeAlgumLugar();
objetoDesconhecido.MetodoQueNaoExiste(); // Compila mas não executa
Não quero discutir aqui o propósito dessa inclusão à linguagem, quero apenas apresentar uma construção bastante interessante permitida com o uso desse recurso.

Imagine escrever um conjunto de métodos privados sobrecarregados, cada um tratando um tipo específico de entrada e delegar a um único método público a função de decidir qual dos métodos privados deve ser executado.

O exemplo abaixo faz isso:
public class Taxonomy
{
    public string GenusFor(IAnimal animal) {
        if (animal is Lion)
            return GenusFor((Lion)animal);
        if (animal is Zebra)
            return GenusFor((Zebra)animal);
        if (animal is Elephant)
            return GenusFor((Elephant)animal);
        return null;
    }
    private string GenusFor(Lion lion) {
        return "Lion";
    }
    private string GenusFor(Zebra zebra) {
        return "Zebra";
    }
    private string GenusFor(Elephant elephant) {
        return "Elephant";
    }
}
O código acima pode parecer desnecessário se considerarmos que o genero do animal poderia ser uma simples propriedade da interface IAnimal, no entanto serve bem para ilustrar a ideia.

Veja como esse mesmo código ficaria se utilizássemos tipagem dinâmica para deixar que o .NET Framework decida em tempo de execução qual a versão do método GenusFor deve ser executada:
public class Taxonomy
{
    public string GenusFor(IAnimal animal) {
        return ((dynamic)this).GenusFor((dynamic)animal);
    }
    private string GenusFor(Lion lion) {
        return "Lion";
    }
    private string GenusFor(Zebra zebra) {
        return "Zebra";
    }
    private string GenusFor(Elephant elephant) {
        return "Elephant";
    }
}

Usando ((dynamic)this), informamos ao compilador que queremos que o this seja tratado como um tipo dinâmico, assim a existência de um método GenusFor com os parâmetros informados não será verificada em tempo de compilação.

Em tempo de execução, o CLR irá procurar por um método chamado GenusFor no objeto this e encontrará 4 sobrecargas, 1 pública e 3 privadas. Nesses casos, o comportamento do CLR é utilizar o método cujos tipos dos parâmetros esperados sejam o mais próximos possíveis dos parâmetros passados. Isso faz com que o método correto seja invocado, conforme o tipo de IAnimal recebido como parâmetro.

Interessante, não? No entanto lembre-se que recursos poderosos como esse devem ser usados com muita cautela. Brinque um pouco com o código disponível no GitHub e veja o impacto positivo e negativo que esse tipo de abordagem pode trazer.

Confira também o uso desse recurso em um dos exemplos citados no livro Implementing Domain-Driven Design, de Vaugh Vernon.

Abraços.

sexta-feira, 26 de abril de 2013

Não posso resolver o seu problema se você só me informa a sua solução.

Imagino que todo desenvolvedor um pouco mais experiente já tenha passado por esse tipo de situação. Seu 'chefe' lhe apresenta um 'problema' e diz que você precisa resolvê-lo urgente. "Precisamos disso para a próxima versão." Algo do tipo:

"Preciso que crie essa tabela no banco, armazene tais valores e altere tal e tal tela para o usuário fazer o cadastro dos dados."
Nesses cenários o 'chefe' sabe bem o que está fazendo, domina a arquitetura do sistema, sabe que basta inserir a tabela no banco e alterar as telas de cadastro, certo?

Normalmente isso não costuma ser verdade. Questões técnicas quase sempre passam despercebidas no momento da elaboração da solução, em maior ou menor grau, e muitas vezes o desenvolvedor não se sente a vontade, ou se quer tem a opção de criticar a solução apresentada pelo 'chefe'. Ou pior, ele implementa tal solução sem perceber que haverá um impacto indesejado, que causará problemas futuros.

O grande problema nisso é o fato de o 'chefe' ter comunicado uma solução para um problema que ele se quer expôs ao desenvolvedor. Duas cabeças pensam melhor que uma. Quanto melhor a equipe conhecer o verdadeiro problema a ser resolvido, maior será a chance de alguém visualizar com antecedência os impactos negativos de se implantar uma ou outra solução.

Não é a toa que metodologias como o Scrum determinam que decisões técnicas ("soluções") sejam tomadas pelos desenvolvedores, cabendo ao Product Owner apenas a tarefa de especificar e priorizar os requisitos ("problemas").

Infelizmente essa é uma realidade que nem sempre podemos mudar. Mas em todo caso, na próxima vez que você receber uma tarefa ("solução") para implementar, pense se você tem total entendimento de qual é o problema que aquela solução se destina a resolver. Se não tiver esse entendimento, tente esclarecer os objetivos com os demais membros do time, com seu 'chefe', e quem mais puder ajudar. Não se acanhe. Quanto melhor você entender o problema, melhor capacitado estará para implementar uma solução, ainda que não seja você quem a defina.

Seja pragmático.

Referência: http://www.acceptancetesting.info/the-book/

segunda-feira, 15 de abril de 2013

O papel do testador no futuro do desenvolvimento de software.

O papel do testador no futuro do desenvolvimento de software é um tema que já havia planejado tratar aqui. No entanto, Igor Abade já o fez com maestria e portanto não vejo nada mais adequado que referenciar o seu texto aqui, ao invés de reescrever a roda.

O testador de software deve ser visto como mais um membro da equipe de desenvolvedores, tão importante quanto os demais, sejam eles programadores, analistas de negócio ou gerentes. Seu trabalho deve ser integrado ao processo e jamais menosprezado.

Aproveito para indicar o livro Specification By Example, do Gojko Adzic, que apresenta entre outros aspectos um ponto de vista semelhante a respeito do papel do testador de software.

Confiram o texto do Igor Abade.

sexta-feira, 5 de abril de 2013

O que constitui um programador pragmático?

Todo desenvolvedor é único, com pontos fortes e fracos, preferências e aversões. Com o tempo, cada um irá criar seu próprio ambiente. Esse ambiente irá refletir a individualidade do programador, assim como seus hobbies, roupas, corte de cabelo. No entanto, se você for um programador pragmático, você irá compartilhar muitas das seguintes características:

  • Adotar cedo e adaptar-se rápido: Você tem um instinto por tecnologias e técnicas, e ama testar coisas novas. Quando diante de uma novidade, você será capaz de compreende-la rápido e integrá-la com o restante de seu conhecimento. Sua confiança nasce de sua experiência.
  • Ser curioso: Você tende a fazer perguntas. Isso é bacana, como você fez? Você teve problemas com essa biblioteca? O que é esse BeOS do qual ouvi falar? Como links simbólicos são implementados? Você é um acumulador de detalhes, cada um podendo afetar alguma decisão anos a frente.
  • Ser um pensador crítico: Você raramente aceita as coisas como dadas sem primeiro entender os fatos. Quando um colega diz "porque é assim que funciona," ou um fornecedor promete a solução para todos os seus problemas, você sente o cheiro de um desafio.
  • Ser realista: Você tenta entender a real natureza de cada problema que enfrenta. Esse realismo lhe dá uma boa ideia de quão diferente as coisas são, e quanto tempo tomarão. Entender pro si próprio que um processo deve ser difícil ou vai tomar um tempo para ser concluído lhe dá estímulo para continuar.
  • Atirar para todos os lados: Você se esforça para se familiarizar com todo tipo de tecnologia e ambiente, e trabalha para se manter atualizado. Embora seu trabalho atual exija que você seja um especialista, você sempre será capaz de seguir para novas áreas e novos desafios.
Deixamos a mais básica característica para o final. Todo programador pragmático a possui:
  • Cuidar da sua criação.
Nós entendemos que não há sentido em desenvolver software a menos que você se preocupe em fazer isso bem feito.
  • Pense! Sobre seu trabalho.
De modo a ser um programador pragmático, nós o desafiamos a pensar sobre o que faz enquanto faz. Isso não é uma análise única de suas práticas atuais, mas sim uma avaliação contínua de toda decisão tomada, a cada dia, em cada desenvolvimento. Nunca entre no piloto automático. Pense constantemente, criticando seu trabalho em tempo real. O velho lema da IBM, PENSE!, é o mantra do programador pragmático.

Se isso soa difícil pra você, então você está exibindo a característica de ser realista. Isso irá tomar boa parte do seu valioso tempo, tempo que provavelmente já está sob tremenda pressão. A recompensa é um envolvimento mais ativo com o trabalho que você ama, um sentimento de domínio sobre um crescente conjunto de assuntos, e prazer em sentir um crescimento contínuo. Ao longo do tempo, seu investimento será pago à medida em que você e sua equipe se tornam mais eficientes, escrevem códigos mais fáceis de manter, e gastam menos tempo em reuniões.

Andrew Hunt e David Thomas, The Pragmatic Programmer: From journeyman to master, Addison Wesley 1999. Pág. xviii

terça-feira, 2 de abril de 2013

Engenheiro de Software ou Arquiteto de Software?

Há alguns dias me perguntaram a diferença entre um Arquiteto de Software e um Engenheiro de Software.

Após uma definição do papel de cada um, para quem é do ramo, pode até não ser difícil entender, mas para quem não é, pode parecer que são apenas duas formas de descrever a mesma função.

À época pedi um tempo para pensar numa forma simples de explicar, e minha resposta veio através de uma analogia com um jogo bem conhecido, um quebra-cabeças, daqueles de encaixar peças para formar uma imagem.



Nessa analogia dizemos que o Engenheiro de Software é aquele que desenha a imagem a ser montada, o que será visto quando o jogo for concluído, enquanto o Arquiteto de Software é aquele que divide a imagem em peças pequenas e encaixáveis, que juntas formam a imagem desejada.

Um Engenheiro de Software tem como objetivo transformar um conjunto de processos de negócio em software; modelar o domínio do negócio de modo que um software possa ser produzido.

Já o Arquiteto de Software tem como objetivo organizar os artefatos modelados pelo Engenheiro de Software de modo a conseguir o melhor encaixe possível entre eles, dados os objetivos do negócio.

E ai? Será que consegui explicar? Ou complicar ainda mais? rs