Expressão Declarativa
Outro princípio por trás dos padrões de implementação é expressar declarativamente o máximo possível da intenção. Uma programação imperativa é poderosa e flexível, mas tê-la exige que se siga o fluxo de execução.
Deve-se construir mentalmente um modelo do estado do programa e dos fluxos de controle de dados. Para partes de um programa que mais parecem fato simples, sem sequência ou condicionais, é mais fácil ler um código simplesmente declarativo.
Por exemplo, em versões mais antigas de JUnit, classes podiam ter um método estático suite() que retornava um conjunto de testes para execução.
public static junit.framework.Test suite(){
Test result = new TestSuite();
…códigos…
return result;
}
Agora vem a questão mais comum – que testes serão executados? Na maioria dos casos, o método suite() apenas agrega os testes em diversas classes. Entretanto, como o método suite() é genérico, para se ter certeza, é preciso ler e entender o método.
O JUnit 4, por sua vez, usa o princípio de expressão declarativa para resolver o mesmo problema. Em vez de um método rodar um suite de testes, há um executor especial de testes que os roda em um conjunto de classes (o caso comum):
@RunWith(Suite.class)
@TestClasses({
SimpleTest.class,
ComplicatedTest.class
})
Class AllTest{}
Sabe-se que testes estão sendo agregados usando esse método; basta olhar a anotação TestClasses para descobrir quais testes serão executados. Como a expressão da suíte é declarativa, não é preciso suspeitar que haja exceções traiçoeiras.
Essa solução propicia a força e a generalidade do método original suite(), mas o estilo declarativo torna o código mais fácil de ler. (A anotação RunWith fornece ainda mais flexibilidade para testes que o método suite().
Ritmo de Mudança
Um último princípio é unir lógica e dados que mudam em um mesmo ritmo e separar lógica e dados que mudam em ritmos diferentes – esses ritmos são uma espécie de simetria temporal.
Às vezes o princípio ritmo de mudança aplica-se às mudanças que um programador faz. Por exemplo, ao escrever um software de impostos, separara-se o código que faz cálculos gerais de taxas do código que é específico de um ano.
O código muda em ritmos diferentes. Quando são feitas mudanças para os anos seguintes, é interessante ter a certeza de que o código de anos anteriores ainda funciona. Separá-los dá mais segurança quanto às consequências locais de mudanças.
O ritmo de mudança aplica-se a dados. Todos os campos em um objeto deveriam mudar mais ou menos no mesmo ritmo. Por exemplo, campos que são modificados apenas durante a ativação de um único método devem ser variáveis locais.
Dois campos que mudam juntos, mas fora de sincronia com seus campos vizinhos, provavelmente pertencem a um objeto auxiliar. Se, em um instrumento financeiro, valor e moeda podem ser alterados juntos, então esses dois campos provavelmente estariam melhor expressos com um objeto auxiliar Money:
setAmount(int value, String currency){
this.value = value;
this.currency = currency;
}
que se torna:
setAmount(int value, String currency){
this.value = new Money(value, currency);
}
e depois:
setAmount(Money value){
this.value – value;
}
O princípio de ritmo de mudança é uma aplicação de simetria, mas simetria temporal. No exemplo acima, os campos originais value e currency são simétricos, pois mudam ao mesmo tempo.
Contudo, são não simétricos aos outros campos do objeto. Expressar simetria colocando-os em seu próprio objeto demostra a relação entre eles para os leitores, e provavelmente, gera novas oportunidade de, mais tarde, reduzir a duplicação e, ainda, localizar as consequências.
Fonte: Padrões de Implementação: Um Catálogo de Padrões Indispensável para o Dia a Dia do Programador (Português)
Deixe um comentário