Expressividade no Código
Um post no GUJ mais uma vez rende uma resposta maior do que se supunha. A thread em si é bastante útil mas existe muito ruído então vou tentar sumarizar:
Vocês pegam códigos que te faz passar horas e horas tentando entender o que está sendo feito? Valores que você nem imagina de onde estão vindo?
Eu sou muito ruim ou isso é normal?
E logo surge alguém sugerindo que deveria haver documentação. Existem casos onde documentação, seja JavaDoc, especificação funcional ou etc. é fundamental mas neste caso é diferente. JavaDoc, especificação textual e etc. devem ser uma fonte importante quando que está interessado nessa informação não vai lero código, é como reutilizar uma biblioteca ou um framework. Ninguém quer abrir o Spring para entender como utilizá-lo, precisamos de documentação para isso.
Um cenário completamente diferente é quando você recebe de presente um código já existente para dar manutenção. Neste caso o código tem que fazer sentido, tem que dizer algo. Tem que ser expressivo, mostrar suas intenções claramente.
NOTA: O trecho abaixo foi escrito direto no editor de texto, sem ajuda de compilador ou IDE. Por favor me corrijam.
Qualquer programador meia-boca sabe sua linguagem. Sabe o domínio dela. Veja o trecho abaixo:
abstract class A{
public abstract int d(){
}
}
class B extends A{
int z= 0;
int x=-1;
public B(int z, int x){
this.z=z;
this.x=x;
}
public int d(){
if(z==15) return x + x* 0.15;
esle return x;
}
}
Você entendeu muito bem, tenho certeza, que A é uma classe abstrata implementada por B. Que B tem um construtor que recebe dois inteiros, os armazena e usa numa computação simples com uma ramificação abaixo. Ótimo.
Imagine que você recebeu um email do seu usuário dizendo: “Precisamos fazer com que clientes do sexo feminino que comprem mais de R$1000,00 ganhem 10% de desconto.”. Tente implementar isso no código acima enquanto eu vou almoçar.
E aí, terminou? Sim, sim, claro que é impossível sem saber d que se trata. Então depois de procurar bastante você encontra no diretório onde fica a documentação do projeto um arquivo que pode te ajudar a entender. Após umas cinquenta páginas de diagramas de alto nível inúteis explicando tudo que acontece no container web você chega a uma descrição de algo como:
A classe A tem a lógica abstrata de uma venda. As vendas são sempre feitas de acordo com critérios específicos por isso existem classes que implementam vendas específicas. No momento (01/10/2003) só existe uma implementação, na classe B, que é a venda para uma pessoa física.
A primeira coisa que você pensa é: se desde 2003 ninguém precisou de outra implementação para que essa maldita classe abstrata? Mas ants de mexer no código precisamos entendê-lo, então vamos em frente.
O méodo e questão recebe três argumentos: a quantdade em reais vendida, o sexo da pessoa (segundo código vindo do mainframe na tabela ETXS32) e se a compra é casada ou não (um booleano).
De acordo com o caso de uso UC171 o sexo do comprador é utilizado para aplicar descontos.
TABELA ETXS32
15 -> masculino
22 -> femininoSe a compra for casada o processamento é feito delegando para outra classe, mantendo o padrão Strategy, Composite e Adapter do Decorator. Note que o Chain of Responsibility do Bridge é usado com Visitors para passar as instâncias de Flyweight pelos Interpreters[…]
Após a sequência de buzzwords de padrões é bom parar. Acho que a informação necessária já está aí em cima e olha que não se passaram nem 4 horas de procura! Vamos lá: recebemos o valor, o sexo segundo um código numérico sem lóica que vêm de outro sistema. Também é passado um boolean.. cadê o boolean?
Procurando no histórico deste arquivo no CVS (poxa vida, eles ainda usam CVS!) você vê que no meio de 2005 alguém tirou o boolean de lá com um comentário ‘Removido compra casada. Ninguém usa isso e ninguém entende isso. Ninuém vai sentir falta’. Acho que a pessoa estava correta mas ela esqueceu que existem uns 300 documentos que precisam ser revistos porque todos fazem referência a esta tal venda casada, do caso de uso, diagrama de domínio até diagrama de deployment. Cada mudança simples implica em editar 300 documentos… provavelmente mais tempo atualizando a tal documentação que o código… dá pra culpar o desenvolvedor?
Bom, agora você entende o que o código faz ao menos. Ele está aplicando um desconto de 15% para homens, você não conseguiu achar isso no caso de uso mas se ninguém está reclamando em produção é porque deve ser assim mesmo. Amanhã (afinal, você perdeu o dia inteiro hoje na ‘documentação’ do sistema) você faz a mudança.
Novo dia e você está pronto para alterar este código. Hmm… alterar pode ser muito rápido e sujo ou devagar e bem feito. Como disse o Uncle Bob recentemente sujo nunca é rápido então você opta pelo caminho com mais qualidade (e mais ético).
Como desenvolvedores profissionais escrevem testes (e gerar você deve começar por aí. Você sabe muito pouco sobre este sistema e o teste vai te dar alguma garantia que a menos esta pequena parta que está mexendo vai continuar funcionando após suas modificações. Vamos lá:
class TestVenda extends TestCase{
public void testDeveAplicarDescontoSeSexoDoCompradorForMasculino(){
B vendaParaUmHomem = new B(15, 100);
assertEquals("Valor final sofreu 15% de desconto", 85, vndaParaUmHomem.d());
}
public void testDeveNaoAplicarQualquerDescontoSeVendaNaoCaiEmNenhumaPromocao(){
B vendaParaUmHomem = new B(9999, 1);
assertEquals("Valor final intacto", 1, vndaParaUmHomem.d());
}
}
Os testes executam. Agora vamos alterar um pouco esta classe, pensando no pobre coitado que for mexer nela após você. Vamos começar agregando nomes mais expressivos:
abstract class VendaAbstrata{
public abstract int vender(){
}
}
class Venda extends VendaAbstrata{
static final int NAO_INFORMADO = -1;
static final int MASCULINO = 15;
static final int FEMININO = 22;
int sexoDoComprador= NAO_INFORMADO;
int valorDaCompra=-1;
public B(int sexoDoComprador, int valorDaCompra){
this.sexoDoComprador=sexoDoComprador;
this.valorDaCompra=valorDaCompra;
}
public int vender(){
if(sexoDoComprador==MASCULINO) return valorDaCompra - valorDaCompra* 0.15;
esle return valorDaCompra;
}
}
Já está bem melhor, não? Compare com a primeira versão do código Os testes passam? Então é hora de commitar (eu acho esse neologismo horrível mas alguém sugere algo melhor?).
Vamos para o segundo round: pequeno refactoring. Já é possível fazer a alteração neste código mas anda temos tempo para deixá-lo um pouquinho mais legível, mais claro, mais expressivo. Vamos alterar:
abstract class VendaAbstrata{
public abstract int vender(){
}
}
class Venda extends VendaAbstrata{
static final int NAO_INFORMADO = -1;
static final int MASCULINO = 15;
static final int FEMININO = 22;
int sexoDoComprador= NAO_INFORMADO;
int valorDaCompra=-1;
public B(int sexoDoComprador, int valorDaCompra){
this.sexoDoComprador=sexoDoComprador;
this.valorDaCompra=valorDaCompra;
}
public int vender(){
int valorFinalDaCompra = aplicarDescontosSobreValorDaCompra();
return valorFinalDaCompra;
public int aplicarDescontosSobreValorDaCompra(){
int valorComDesconto= valorDaCompra;
if(sexoDoComprador==MASCULINO)
valorComDesconto = descontarPorcentagem(15, valorDaCompra);
return valorComDesconto;
}
public int descontarPorcentagem(int porcentagem, int valorOriginal){
return valorOriginal * (porcentagem / 100.0);
}
}
Agora que tal esta versão do código + teste unitário contra a versão antiga + trezentos documentos e especificações? A implementação da regra nova ficou fácil? Acho que sim, tanto que deixo como exercício ao leitor, bem como algumas dezenas de refactorings que vão deixar o código acima decente.
A resposta curta para a thread do GUJ é: geralmente o problema não é seu mas de quem escreveu o código.
Any fool can write code that a computer can understand. Good programmers write code that humans can understand.
-Martin Fowler, “Refactoring: Improving the Design of Existing Code “
December 28th, 2007 at 8:52 pm
Excelente!
Espero que as pessoas ententam isso um dia.
December 28th, 2007 at 9:19 pm
Muito esclarecedor, como sempre.
É impressionando como o livro do Martin Fowler continua atual, e pelo jeito durará por toda a eternidade!
Só uma pequena correção:
Ponto e vírgula no método abstrado, ao invés de chares
-> public abstract int vender();
December 28th, 2007 at 10:49 pm
Muito bacana o seu post Philip. Poucas pessoas no mercado tem conhecimento sobre o assunto - REFACTORING! Participei da discussão lá no GUJ e comentei sobre a utilização da refatoração. Para mim, refatorar é uma arte. Requer mto “cuidado”, ou seja, deixar o código funcional como antes, e para isso, os testes, como mencionado por você, estão ai para nos auxiliar (e ainda existem pessoas que sequer utilizam testes automatizados e falam q refatoram - Espero que o desenvolvedor tenha uma fé mto forte e mtos santos em sua mesa olhando para ele…rs )..
Abraços
December 29th, 2007 at 1:34 am
[…] o artigo do Phillip Calçado, “Expressividade no código“. Nele, Phillip fala o que todo programador já está cansado de saber mas que a maioria […]
December 29th, 2007 at 8:43 am
“Post” antológico! Parabéns!
December 30th, 2007 at 12:18 pm
E eis o meu ponto da discussão do GUJ: se o código já foi escrito de maneira clara o suficiente, para que raios gastar mais tempo escrevendo javadoc? Faz sentido? É util?
valeuz…
January 1st, 2008 at 6:56 pm
Excelente artigo!
Eu sempre acreditei que documentação, incluindo comentários no código fonte, só deve ser criada se ela de fato agregar algum valor ao projeto.
Como você bem disse, uma vez criada a documentação, ela terá que ser atualizada em várias ocasiões, e a menos que ela tenha uma utilidade bem concreta, esta atualização será um enorme desperdício de tempo e recursos.
Talvez eu seja meio extremo nesse sentido, principalmente em relação aos comentários no código fonte, mas ao ler, por exemplo, o seu código, eu reforço a minha opinião, já que ele fala por si só, não só dispensando qualquer documentação, como também se tornando, de certa forma, uma espécie de documentação(mais ou menos como prega o BDD, se não me engano).
P.S.: Só pra evitar interpretações errôneas, eu não sou contra comentários, apenas sou contra comentários desnecessários. Um exemplo disso seria comentar simples getters/setters ou váriaveis com nomes significativos, como uma variável de instância chamada dataDeNascimento em uma eventual classe Cliente.
Além disso, também sou, na maioria dos casos, contra comentários que esclarecem aspectos da sintaxe do código, ao invés de focar na sua semântica, que é o que realmente importa.
January 2nd, 2008 at 10:24 pm
Odeio códigos asquerosos! rsrsrs
Que o código fale por si…
January 3rd, 2008 at 12:23 am
Em alguns casos acho melhor não ter documentação nenhuma do que ter uma documentação desatualizada há séculos… Acho que nomes de métodos, classes e atributos auto explicativos são essenciais para uma boa manutenção. E é claro: testar sempre!
January 4th, 2008 at 10:35 am
Mto bom.
Fui eu quem abriu a discussão no Guj. Foi um dia de revolta.
Pegando no exemplo da sua classe, imagine que temos uma tela de vendas super complexa, onde temos as variaveis:
xPre, xPreDolar, xPreco, xPrecoCalculado e por ai vai…
duro no ínicio p´saber o q cada uma representa…
A ideia de Refactoring é mto boa, mas inútil em alguns casos.
January 5th, 2008 at 12:10 pm
Se você possui estes valores na tela provavelmente eles são parte do negócio, não? Se forem parte do negocio então deveriam estar no modelo de objetos, logo a mesma situação se aplica.