Tópicos
A refatoração é o ato de melhorar a estrutura de um programa sem alterar o seu comportamento. A refatoração é feita em
poucos pequenos passos, cada um valendo o que faz. Entre cada passo, executamos os testes de unidade relevantes para
certificar que as mudanças que fizemos não prejudicaram nada. O ciclo de edição, compilação e testes dura normalmente
de 30 segundos a cinco minutos.
A finalidade da refatoração é melhorar o design e a legibilidade do código. Existem várias metas específicas:
-
O código deve passar em todos os seus testes.
-
Ele deve ser o mais expressivo possível que você possa fazer.
-
Ele deve ser o mais simples possível que você possa fazer.
-
Ele não deve ter nenhuma redundância.
A refatoração não é algo que possamos agendar. Não existe nenhuma entrada no cronograma para ela. Não existe nenhum
momento especial para fazê-la. A refatoração é feita o tempo todo. Quando você e seu parceiro estiverem trabalhando em
uma tarefa, tal como escrever testes e código, vocês notarão que o código e os testes não estão tão limpos e simples
quanto poderiam estar. Esta é a hora de parar e refatorar o código.
A regra é: Não deixe que o sol se ponha com código ruim.
Considere os dois testes de unidade e a classe Formatter abaixo. A classe Formatter funciona, mas não é tão expressiva como eu gostaria que fosse. Então eu vou
refatorá-la em etapas.
public void testCenterLine() {
Formatter f = new Formatter();
f.setLineWidth(10);
assertEquals(" word ", f.center("word"));
}
public void testOddCenterLine() throws Exception {
Formatter f = new Formatter();
f.setLineWidth(10);
assertEquals(" hello ", f.center("hello"));
}
|
import java.util.Arrays;
public class Formatter {
private int width;
private char spaces[];
public void setLineWidth(int width) {
this.width = width;
spaces = new char[width];
Arrays.fill(spaces, ' ');
}
public String center(String line) {
int remainder = 0;
StringBuffer b = new StringBuffer();
int padding = (width - line.length()) / 2;
remainder = line.length() % 2;
b.append(spaces, 0, padding);
b.append(line);
b.append(spaces, 0, padding + remainder);
return b.toString();
}
}
|
A função setLineWidth está um pouco misteriosa. O que é este array spaces e porque é preenchido com espaços em branco? Olhando adiante, a função center, vemos que o array spaces é apenas uma conveniência para
nos permitir mover uma série de espaços em branco para um StringBuffer. Questiono se
precisamos realmente deste array de conveniência.
Por enquanto, eu irei inicializar o array com sua própria função denominada buildArrayOfSpaces. Dessa forma, tudo está em um só lugar, e posso pensar sobre isso com um
pouco mais de clareza.
public void setLineWidth(int width) {
this.width = width;
buildArrayOfSpaces(width);
}
private void buildArrayOfSpaces(int width) {
spaces = new char[width];
Arrays.fill(spaces, ' ');
}
Executo os testes: os testes passam
|
Eu não gosto da forma como a função center foi construída. Existe matemática espalhada
por toda parte. Creio que podemos reorganizar a matemática para tornar as coisas mais claras.
public String center(String line) {
int remainder = line.length() % 2;
int numberOfBlanksInFront = (width - line.length()) /
2;
int numberOfBlanksAtEnd = (width - line.length()) / 2 +
remainder;
StringBuffer b = new StringBuffer();
b.append(spaces, 0, numberOfBlanksInFront);
b.append(line);
b.append(spaces, 0, numberOfBlanksAtEnd);
return b.toString();
}
Executo os testes: os testes passam
|
Agora parece melhor, mas podemos reduzir a desordem, transformando algumas variáveis em funções.
public String center(String line) {
StringBuffer b = new StringBuffer();
b.append(spaces, 0, numberOfBlanksInFront(line));
b.append(line);
b.append(spaces, 0, numberOfBlanksBehind(line));
return b.toString();
}
private int numberOfBlanksBehind(String line) {
int extraBlankIfOdd = line.length() % 2;
return (width - line.length()) / 2 +
extraBlankIfOdd;
}
private int numberOfBlanksInFront(String line) {
return (width - line.length()) / 2;
}
Executo os testes: os testes passam
|
Isto torna a leitura da função center um pouco melhor. Entretanto, o uso da função StringBuffer.append está um pouco confuso. Podemos melhorá-la um pouco, criando uma função
mais explícita.
public String center(String line) {
StringBuffer b = new StringBuffer();
appendBlanks(b, numberOfBlanksInFront(line));
b.append(line);
appendBlanks(b, numberOfBlanksBehind(line));
return b.toString();
}
private void appendBlanks(StringBuffer b, int numberOfBlanks)
{
b.append(spaces, 0, numberOfBlanks);
}
Executo os testes: os testes passam
|
Agora, podemos rescrever appendBlanks para evitar usar o array spaces.
import java.util.Arrays;
public class Formatter {
private int width;
public void setLineWidth(int width) {
this.width = width;
}
public String center(String line) {
StringBuffer b = new StringBuffer();
appendBlanks(b, numberOfBlanksInFront(line));
b.append(line);
appendBlanks(b, numberOfBlanksBehind(line));
return b.toString();
}
private void appendBlanks(StringBuffer b, int numberOfBlanks) {
while(numberOfBlanks-- > 0)
b.append(' ');
}
private int numberOfBlanksBehind(String line) {
int extraBlankIfOdd = line.length() % 2;
return (width - line.length()) / 2 + extraBlankIfOdd;
}
private int numberOfBlanksInFront(String line) {
return (width - line.length()) / 2; }
}
}
Executo os testes: os testes passam
|
Os nomes de funções tais como numberOfBlanksBehind implicam que o leitor saiba que elas
serão chamadas a partir da função center. Devemos eliminar essa implicação renomeando
essas funções.
import java.util.Arrays;
public class Formatter {
private int width;
public void setLineWidth(int width) {
this.width = width;
}
public String center(String line) {
StringBuffer b = new StringBuffer();
appendBlanks(b, numberOfBlanksToLeftOfCenter(line));
b.append(line);
appendBlanks(b,
numberOfBlanksToRightOfCenter(line));
return b.toString();
}
private void appendBlanks(StringBuffer b, int numberOfBlanks) {
while(numberOfBlanks-- > 0)
b.append(' ');
}
private int numberOfBlanksToRightOfCenter(String line) {
int extraBlankIfOdd = line.length() % 2;
return (width - line.length()) / 2 + extraBlankIfOdd;
}
private int numberOfBlanksToLeftOfCenter(String line) {
return (width - line.length()) / 2;
}
}
Executo os testes: os testes passam
|
E acho que acabamos. Você poderá fazer outros tipos de refatorações, ou talvez não concorde com todas as refatorações
que eu fiz. Isso é esperado. A questão é, contudo, que eu fiz um grande esforço para melhorar a legibilidade e a
simplicidade desta classe. Este esforço irá ajudar as outras pessoas a compreender esta classe e facilitará a mudança
quando chegar a hora.
|