Atualmente, estou lendo e trabalhando em "Código Limpo: Um Manual de Artesanato em Software Ágil", de Robert C. Martin. O autor fala sobre como uma função deve fazer apenas uma coisa e, portanto, é relativamente curta. Martin escreve especificamente:
Isso implica que os blocos dentro de instruções if, else, while e etc. devem ter uma linha. Provavelmente essa linha deve ser uma chamada de função. Isso não apenas mantém a função anexa pequena, mas também agrega valor documental, porque a função chamada dentro do bloco pode ter um nome bem descritivo.
Isso também implica que as funções não devem ser grandes o suficiente para conter estruturas aninhadas. Portanto, o nível de recuo de uma função não deve ser maior que um ou dois. Isso, é claro, facilita as funções de ler e entender
Isso faz sentido, mas parece entrar em conflito com exemplos do que eu vejo como código limpo. Pegue o seguinte método, por exemplo:
public static boolean millerRabinPrimeTest(final int n) {
final int nMinus1 = n - 1;
final int s = Integer.numberOfTrailingZeros(nMinus1);
final int r = nMinus1 >> s;
//r must be odd, it is not checked here
int t = 1;
if (n >= 2047) {
t = 2;
}
if (n >= 1373653) {
t = 3;
}
if (n >= 25326001) {
t = 4;
} // works up to 3.2 billion, int range stops at 2.7 so we are safe :-)
BigInteger br = BigInteger.valueOf(r);
BigInteger bn = BigInteger.valueOf(n);
for (int i = 0; i < t; i++) {
BigInteger a = BigInteger.valueOf(SmallPrimes.PRIMES[i]);
BigInteger bPow = a.modPow(br, bn);
int y = bPow.intValue();
if ((1 != y) && (y != nMinus1)) {
int j = 1;
while ((j <= s - 1) && (nMinus1 != y)) {
long square = ((long) y) * y;
y = (int) (square % n);
if (1 == y) {
return false;
} // definitely composite
j++;
}
if (nMinus1 != y) {
return false;
} // definitely composite
}
}
return true; // definitely prime
}
}
Este código foi retirado do repositório de código-fonte do Apache Commons em: https://github.com/apache/commons-math/blob/master/src/main/java/org/apache/commons/math4/primes/SmallPrimes.java
O método parece muito legível para mim. Para implementações de algoritmos como este (implementação do Teste Probabilístico de Miller-Rabin), é adequado manter o código como está e ainda o considerar 'limpo', conforme definido no livro? Ou mesmo algo já tão legível como esse se beneficiaria da extração de métodos para tornar o algoritmo essencialmente uma série que chama funções que "fazem apenas uma coisa"? Um exemplo rápido de uma extração de método pode ser mover as três primeiras instruções if para uma função como:
private static int getTValue(int n)
{
int t = 1;
if (n >= 2047) {
t = 2;
}
if (n >= 1373653) {
t = 3;
}
if (n >= 25326001) {
t = 4;
}
return t;
}
Nota: Essa pergunta é diferente da duplicata possível (embora também seja útil para mim), porque estou tentando determinar se estou entendendo a intenção do autor do Código Limpo e estou fornecendo um exemplo específico para tornar as coisas mais fáceis. concreto.
fonte
Respostas:
"Código limpo" não é um fim em si, é um meio para um fim. O principal objetivo de refatorar funções maiores para funções menores e limpar o código de outras maneiras é manter o código evolutivo e sustentável.
Ao escolher um algoritmo matemático tão específico como o teste principal "Miller-Rabin" de um livro, a maioria dos programadores não deseja evoluí-lo. Seu objetivo padrão é transferi-lo do pseudo-código do livro de texto corretamente para a linguagem de programação de seu ambiente. Para esse propósito, eu recomendaria seguir o livro o mais próximo possível, o que normalmente significa não refatorar.
No entanto, para alguém que trabalha como matemático nesse campo que está tentando trabalhar nesse algoritmo e alterá-lo ou aprimorá-lo, o IMHO dividindo essa função em menores e bem nomeados, ou substituindo o grupo de "números mágicos" por constantes nomeadas, pode ajuda a facilitar as alterações no código, como em qualquer outro tipo de código.
fonte