Convenções de codificação

quarta-feira, 23 de dezembro de 2009

Estudos revelam que apenas 20% a 40% do custo de um software está relacionado com a construção. 60% a 80% com manutenção (Livro "Facts and fallacies of software engineering", Robert L. Glass).
Isso não significa dizer que manutenção é o mais importante no ciclo de desenvolvimento de software, mas certamente requer uma atenção especial.
Levando em consideração que também é bastante comum fazer manutenção em código de outra pessoa é importante que a outra pessoa nos deixe um código limpo, claro, fácil de entender e sem armadilhas escondidas.

Assim é muito importante que bons padrões de codificação sejam seguidos. Bons padrões melhoram a legibilidade, deixam claras as intenções do autor do código e expõem erros, facilitando não só para quem faz a manutenção mas para quem escreve o código.

Infelizmente muita gente não dá a devida importância ao tópico porque é bastante comum que outra pessoa dê manutenção no código no final das contas. O grande problema é que alguns de vocês leitores precisam escrever um código bom para que outros de vocês sejam capazes de entender, corrigir e mudar. Além disso, você se pergunta em que condições você está entregando o produto do seu trabalho? Você se pergunta o qual o valor do que você está entregando para o seu empregador?

Agora estou escrevendo este post no Blog. E simplesmente não dá para diferenciar os que estão dando manutenção no código de outra pessoa dos que estão escrevendo código para outra manter. E isso se dá pelo simples fato de que todos nós trocamos os chapéus vez ou outra. Então melhor do que torcer para que o seu próximo código a ser mantido tenha sido bem escrito é escrever o seu código atual bem. Se o seu amigo estiver fazendo o mesmo, você terá um bom código para manter e ele também.

Eu já escrevi anteriormente sobre qualidade de código mas desta vez me refiro a padrões. Padrões como http://java.sun.com/docs/codeconv/ e outros podem ser encontrados pelo Google. O padrão definido na linguagem Java tem sido largamente aceito inclusive por comunidades de outras linguagens de programação por ser bastante geral, simples e manter o código limpo.

Para quem acha que estou exagerando na importância do tópico vale lembrar que o padrão ajuda outros desenvolvedores a entenderem o código por já estarem familiarizados com o padrão. Um padrão ajuda em tarefas de automação como contagem de linhas de código (não estou defendendo esta medida, apenas citando) e revisões por pares frequentemente envolvem leitura de código.


Veja o trecho de código abaixo com uma identação adequada e sem uma identação adequada:

    //RUIM
    if ((condition1 && condition2)
        || (condition3 && condition4)
        ||!(condition5 && condition6)) { //QUEBRA RUIM
        doSomethingAboutIt();            //FÁCIL DE PERDER ESTA LINHA
    }

    //BEM MELHOR
    if ((condition1 && condition2)
            || (condition3 && condition4)
            ||!(condition5 && condition6)) {
        doSomethingAboutIt();
    }

    //TAMBÉM SERVE
    if ((condition1 && condition2) || (condition3 && condition4)
            ||!(condition5 && condition6)) {
        doSomethingAboutIt();
    }


Exemplo retirado do padrão Java, onde se lê "Line wrapping for if statements should generally use the 8-space rule, since conventional (4 space) indentation makes seeing the body difficult."

E o principal é fazer com que o que esteja errado pareça estar errado. Às vezes encontramos um bug num trecho inocente:

    if (condicao)
        fazerAlgo();

E descobrimos que o autor esqueceu de fazerAlgoAntes();. Assim, vamos corrigir:

    if (condicao)
        fazerAlgoAntes();
        fazerAlgo();


E pronto, lá está o fazerAlgo(); fora do if simplesmente porque o desenvolvedor não abriu um bloco no if:

    if (condicao) {
        fazerAlgo();
    }


E não é importante? Quem quer criar um bug novo ao consertar outro? Ou pior, quando você achou que era só "fazerAlgoAntes();" e não percebeu que quebrou o "fazerAlgo();" vai ficar se perguntando porque não funcionou, se não era só isso, como uma coisa fez a outra quebrar, etc...

Viu a diferença que um bloco no if pode fazer?

Não fique parado! Aprenda o padrão da sua linguagem mais utilizada/preferida! :-)

E se você tem algum caso de bug causado por código mal formatado, comente!

Bookmark and Share

Interfaces e comportamento aumentando a coesão e reduzindo o acoplamento

segunda-feira, 7 de dezembro de 2009

Você já encontrou uma pessoa famosa na rua executando tarefas cotidianas? Eu já. Encontrei, há muitos anos, o Francisco Cuoco alugando um filme numa Blockbuster em Ipanema. Também numa ida ao Tanaka San da Lagoa encontrei, jantando, Helena Ranaldi, Malu Mader e outros.

Como todas as outras pessoas, estas também implementam interfaces. E estas implementam interfaces públicas! ;-)

Elas comem como todo mundo, assistem filmes, fazem tudo o que nós fazemos. E atuam. Quando estão atuando, estão no ambiente de trabalho. Lá, diferente da vida pessoal, eles são obrigados a obedecer certas regras, assim como o resto de nós.

Ninguém vai trabalhar de pijamas, apesar de ser compreensível que, no mesmo horário, no conforto do seu lar, alguém pudesse estar de pijamas.

Isto porque no trabalho precisamos apresentar uma outra interface, com outro comportamento. Este comportamento é definido por um conjunto coeso de tarefas que podemos executar. Estas tarefas são executadas de uma determinada forma, seguindo determinados passos que compõem nosso método de executá-las.

Assim é com as classes. Ao definir métodos públicos para uma classe, estamos definindo uma interface para ela. Esta interface exibe um comportamento que poderá ser exigido dela através da execução destes métodos.

Se a sua classe implementa mais de uma interface, ela exibe mais de um comportamento. O mecanismo de interfaces presente em muitas linguagens é geralmente usado para simular uma herança múltipla, muitas vezes ausente, pois as interfaces podem ser usados como tipos quando o comportamento que será exigido de um objeto está inteiramente contido nela (e é o mais recomendável pois deixa o código mais flexível e reutilizável).

No entanto, o significado de implementar uma interface é dizer que um determinado comportamento é atendido e criar uma interface não só mostra isso com mais clareza mas também agrupa os métodos de forma mais coesa e dá uma flexibilidade maior ao sistema permitindo que classes em outra hierarquia também possam implementar o mesmo comportamento.

Este é o ponto de vista interno, do desenho da classe.

Do ponto de vista das classes clientes, ou seja, das classes que irão exigir o comportamento chamando os métodos, utilizar através de uma interface significa não estar preso a uma implementação específica, que pode inclusive ser trocada em tempo de compilação ou de execução, reduzindo drasticamente o acoplamento.

Em tempo de compilação esta flexibilidade permite que os métodos possam ser chamados de classes diferentes e cada classe poderá realizar diferentes ações. Esta flexibilidade é usada em muitos frameworks para isolar a implementação real da funcionalidade sendo disponibilizada, permitindo que os desenvolvedores possam evoluir o framework sem impactar na API externa.

Em tempo de execução esta flexibilidade permite que um método seja chamado de instâncias de diferentes classes, por exemplo, para notificar o acontecimento de um evento. A partir daí, cada instância fará o que foi programada para fazer no acontecimento daquele evento. Assim, uma coleção de instâncias de diferentes classes observadoras pode ser notificada quando um evento observável ocorrer em uma instância de outra.
Este é o padrão de projetos Observer.

Ainda não sei se vou explicar padrões de projeto aqui. Tem muita informação disponível a respeito já, basta googlar. O que acham?


Bom, vão as dicas:


- Depois de identificadas as entidades do sistema, verifique comportamentos em comum que serão exigidos pelas funcionalidades já requisitadas e os defina em termos de interfaces. Isto permitirá a reutilização de funcionalidades já construídas para diferentes hierarquias, reduzindo tempo e custo de desenvolvimento e manutenção.

- Procure identificar funcionalidades que tratam classes de hierarquias diferentes de forma semelhante no seu sistema e verifique se uma interface não poderia eliminar a redundância.

Você cria interfaces no seu sistema?

Bookmark and Share

 
addthis_config = { data_ga_tracker: pageTracker }