Faz sentido algum refator terminar com um LOC mais alto? [fechadas]

25

Existem casos em que códigos mais detalhados (como em declarações mais lógicas) são mais limpos que códigos mais concisos?

nome de usuário errado
fonte
18
Claro que sim. Mesmo ao eliminar a duplicação, o código para definir a nova função extraída ocupa espaço próprio. Escrever uma nova função pode levar quatro linhas e salvar apenas duas, e ainda assim vale a pena.
Kilian Foth
5
"código mais conciso"? Eu realmente odeio a crença equivocada de que uma contagem menor de linhas significa código "melhor". Não faz. De fato, geralmente é o oposto. Chega um ponto - e é alcançado muito rapidamente - onde colocar cada vez mais significado em cada vez menos espaço torna o código mais difícil de entender. De fato, há uma competição inteira - o Concurso Internacional de Código Ofuscado C - em que muitos vencedores confiam nesses limites da compreensão humana para escrever código impenetrável.
Andrew Henle 12/10
1
O título da sua pergunta e sua própria pergunta estão fazendo perguntas diferentes. Por exemplo, uma instrução if pode ser alterada para uma expressão ternária que é a mesma lógica, mas apenas 1 linha.
Captain Man
1
-1 *** código mais detalhado (como nas declarações mais lógicas) *** a verbosidade e o número de declarações lógicas são duas coisas não relacionadas. É uma má forma alterar as definições.
21818 Pieter B #:

Respostas:

70

Para responder a isso, vamos dar um exemplo do mundo real que aconteceu comigo. Em C # uma biblioteca que eu mantenho, eu tinha o seguinte código:

TResult IConsFuncMatcher<T, TResult>.Result() =>
    TryCons(_enumerator) is var simpleMatchData && !simpleMatchData.head.HasValue
        ? _emptyValue.supplied
            ? _emptyValue.value
            : throw new NoMatchException("No empty clause supplied");
        : _recursiveConsTests.Any() 
            ? CalculateRecursiveResult() 
            : CalculateSimpleResult(simpleMatchData);

Discutindo isso com colegas, o veredicto unânime foi que as expressões ternárias aninhadas, juntamente com o uso "inteligente" de is varresultaram em código conciso, mas difícil de ler.

Então eu refatorei para:

TResult IConsFuncMatcher<T, TResult>.Result()
{
    var simpleMatchData = TryCons(_enumerator);

    if (!simpleMatchData.head.HasValue)
    {
        return _emptyValue.supplied
            ? _emptyValue.value
            : throw new NoMatchException("No empty clause supplied");
    }

    return _recursiveConsTests.Any() 
        ? CalculateRecursiveResult() 
        : CalculateSimpleResult(simpleMatchData);
}

A versão original continha apenas uma expressão composta com um implícito return. A nova versão agora contém uma declaração explícita de variável, uma ifdeclaração e duas explícitas returns. Ele contém mais instruções e mais linhas de código. No entanto, todos que eu consultei consideraram mais fácil ler e raciocinar, que são aspectos-chave do "código limpo".

Portanto, a resposta para sua pergunta é um enfático "sim", mais detalhado pode ser mais limpo que o código conciso e, portanto, é uma refatoração válida.

David Arno
fonte
34
Atualmente, o cérebro do desenvolvedor é um recurso mais escasso do que o disco, CPU, RAM ou largura de banda da rede. Essas outras coisas são importantes e, em certos aplicativos, podem ser seu fator limitador, mas na maioria dos casos, você deseja otimizar a capacidade de seus desenvolvedores de entender o código primeiro e depois essas outras coisas.
Anaximander 12/10
2
@anaximander, concordo absolutamente. Escreva o código para que outras pessoas leiam primeiro e depois o compilador. É por isso que acho útil que outras pessoas revisem meu código, mesmo que eu seja o único a desenvolvê-lo.
David Arno
4
Se eu estivesse analisando isso, sugeriria reverter a ordem das declarações de retorno e remover !a condição. Eu também sugeriria colocar o segundo retorno em um else. Mas mesmo assim, é uma grande melhoria.
Martin Bonner apoia Monica
2
@ Davididno Eu vejo essa lógica, e se if (!foo.HasValue)é um idioma no seu código, ainda mais fortemente. No entanto, ifnão é realmente uma saída cedo - é um "faça isso ou aquilo dependendo".
Martin Bonner apoia Monica
2
@fabric A comparação de valores booleanos é perigosa. Eu evito o máximo que posso.
Martin Bonner apoia Monica
30

1. Falta de correlação entre LOC e a qualidade do código.

O objetivo da refatoração é melhorar a qualidade de um pedaço de código.

LOC é uma métrica muito básica que, às vezes, se correlaciona com a qualidade de um pedaço de código: por exemplo, é provável que um método com alguns milhares de LOC tenha problemas de qualidade. Deve-se notar, no entanto, que LOC não é a única métrica e, em muitos casos, carece de correlação com a qualidade. Por exemplo, um método 4 LOC não é necessariamente mais legível ou mais sustentável que um método 6 LOC.

2. Algumas técnicas de refatoração consistem na adição de LOCs.

Se você fizer uma lista de técnicas de refatoração , poderá identificar facilmente as que consistem na adição intencional de LOCs. Exemplos:

Ambas são técnicas de refatoração muito úteis, e seu efeito no LOC é completamente irrelevante quando se pensa em usá-las ou não.

Evite usar LOC.

LOC é uma métrica perigosa. É muito fácil de medir e muito difícil de interpretar corretamente.

Até você se familiarizar com as técnicas de medição da qualidade do código, considere evitar medir o LOC em primeiro lugar. Na maioria das vezes, você não obtém nada de relevante e, em alguns casos, isso o leva a diminuir a qualidade do seu código.

Arseni Mourzenko
fonte
Você reformulado sua resposta e melhorou a qualidade adicionando mais LOT (linhas de texto): p
grinch
12

Se você deseja ver o resultado final de apenas minimizar a contagem de bytes ou LoC do seu código-fonte, consulte os envios para o site Stack Exchange Code Golf .

Se seu código-fonte for reduzido dessa maneira, em breve você terá uma bagunça insustentável. Mesmo que você seja a pessoa que escreveu esse código e o entenda completamente na época, qual será sua eficiência quando voltar a usá-lo daqui a seis meses? Também não há evidências de que esse código mínimo seja executado mais rapidamente.

O código deve ser escrito de tal maneira que qualquer membro da sua equipe possa analisá-lo e entender o que está fazendo imediatamente.

Peregrino
fonte
Talvez redundante, mas apenas para explicar; se você refatorar golfed código para facilitar a leitura você sempre acabar com mais LoC
jollyjoker
1

Sim, a refatoração pode definitivamente resultar em mais linhas de código.

O caso mais comum da IMO é quando você pega um código que não é genérico e o torna mais genérico / flexível . Gerar código facilmente faz com que as linhas de código aumentem significativamente (às vezes por um fator de dois ou mais).

Se você espera que o código recém-genérico seja usado por outras pessoas (em vez de apenas como um componente interno de software) como uma biblioteca, normalmente você acaba adicionando códigos mais unidos e marcação de documentação no código que aumentará as linhas de código novamente.

Por exemplo, aqui está um cenário muito comum que acontece para todo desenvolvedor de software:

  • seu produto precisa de um novo recurso urgente de alta prioridade ou correção de bugs ou aprimoramento em duas semanas (ou qualquer período de tempo considerado urgente para o tamanho do seu projeto / empresa / etc)
  • você trabalha duro e entrega o XYZ no prazo e ele funciona. Parabéns! Bom trabalho!
  • Enquanto você desenvolvia o XYZ, o design / implementação de código existente não era realmente compatível com o XYZ, mas você conseguiu colocar o XYZ na base de código
  • o problema é que o calço é feio e tem um cheiro horrível de código porque você fez algumas coisas complicadas / inteligentes / feias / de más práticas, mas meio que funcionam
  • quando você encontrar tempo mais tarde, refatorar o código, o que pode alterar muitas classes ou adicionar uma nova camada de classes, e sua nova solução será "executada corretamente" e não terá mais o mau cheiro do código ... "caminho certo" agora ocupa muito mais linhas de código.

Alguns exemplos concretos que me vêm à cabeça:

  • para uma interface de linha de comandos você pode ter 5000 linhas de código if / else-if ou usar retornos de chamada ... cada retorno de chamada seria muito menor e mais fácil de ler / testar / verificar / depurar / etc, mas se você contar linhas de codifique as feias 5000 linhas do código if / else-if provavelmente seriam menores
  • para um trecho de código de processamento que suporta N métodos de processamento , você pode novamente usar instruções if / else com a aparência mais feia ...
    • ou você pode mudar para retornos de chamada que seriam mais agradáveis ​​/ melhores, mas retornam mais linhas de código (embora ainda compile o tempo)
    • ou você pode abstrair ainda mais e criar plugins que podem ser alterados em tempo de execução. plug-ins são bons porque você não precisa recompilar o produto principal para cada novo plug-in ou modificação em um plug-in existente. e você pode publicar a API para que outras pessoas possam estender o produto. Mas, novamente, uma abordagem de plug-in usa mais linhas de código.
  • para uma GUI, você cria um ótimo novo widget
    • você ou um colega de trabalho observa que o novo widget seria ótimo para XYZ e ABC, mas agora o widget está totalmente integrado apenas para trabalhar com XYZ
    • você refatora o widget para trabalhar para ambos, mas agora o total de linhas de código aumenta
Trevor Boyd Smith
fonte