Em que casos menos código não é melhor? [fechadas]

54

Eu refatorei alguns códigos no trabalho ultimamente e achei que fiz um bom trabalho. Eu derrubei 980 linhas de código para 450 e diminuí o número de classes.

Ao mostrar isso aos meus colegas, alguns não concordaram que isso foi uma melhoria.

Eles disseram - "menos linhas de código não são necessariamente melhores"

Eu posso ver que pode haver casos extremos em que as pessoas escrevem linhas realmente longas e / ou colocam tudo em um único método para salvar algumas linhas, mas não foi isso que eu fiz. O código é, na minha opinião, bem estruturado e mais simples de compreender / manter devido a ser metade do tamanho.

Estou lutando para ver por que alguém iria querer trabalhar com o dobro do código necessário para realizar um trabalho, e estou pensando se alguém sente o mesmo que meus colegas e pode fazer alguns bons casos para ter mais código por menos?

    
por PiersyP 31.08.2017 / 01:51
fonte

10 respostas

123

Uma pessoa magra não é necessariamente mais saudável do que uma pessoa com excesso de peso.

Uma história infantil de 980 linhas é mais fácil de ler do que uma tese de física de 450 linhas.

Existem muitos atributos que determinam a qualidade do seu código. Alguns são simplesmente computados, como Complexidade Ciclomática , e Complexidade do Halstead . Outros são mais vagamente definidos, como coesão , legibilidade, compreensibilidade, extensibilidade, robustez, correção, auto-documentação , limpeza, testabilidade e muito mais.

Poderia ser, por exemplo, que, embora você reduzisse o tamanho geral do código, você introduziu uma complexidade desnecessária adicional e tornou o código mais enigmático.

A divisão de um longo código em pequenos métodos pode ser tão prejudicial quanto benéfico .

Peça aos seus colegas que forneçam feedback específico sobre o motivo pelo qual eles acham que seus esforços de refatoração produziram um resultado indesejável.

    
por 31.08.2017 / 02:32
fonte
35

Curiosamente, um colega e eu estamos atualmente no meio de um refatorador que aumentará o número de classes e funções em um pouco menos que o dobro, embora as linhas de código permaneçam em torno do mesmo . Então, por acaso tenho um bom exemplo.

No nosso caso, tivemos uma camada de abstração que realmente deveria ser dois. Tudo estava espremido na camada de interface do usuário. Ao dividi-lo em duas camadas, tudo se torna mais coeso, e testar e manter as peças individuais torna-se muito mais simples.

Não é o tamanho do código que está incomodando seus colegas, é outra coisa. Se eles não conseguirem articulá-lo, tente ver o código como se você nunca tivesse visto a implementação antiga e avaliá-la por seus próprios méritos, em vez de apenas compará-la. Às vezes, quando faço um longo refatorador, perco a visão do objetivo original e levo as coisas longe demais. Tome um olhar crítico e coloque-o de volta nos trilhos, talvez com a ajuda de um programador cujos conselhos você valoriza.

    
por 31.08.2017 / 05:22
fonte
18

Uma citação, muitas vezes atribuída a Albert Einstein, vem à mente:

Make everything as simple as possible, but not simpler.

Quando você exagera no recorte das coisas, isso pode tornar o código mais difícil de ler. Como "fácil / difícil de ler" pode ser um termo muito subjetivo, explicarei exatamente o que quero dizer com isso: uma medida do grau de dificuldade que um desenvolvedor habilidoso terá para determinar "o que esse código faz?" apenas olhando para a fonte, sem a ajuda de ferramentas especializadas.

Linguagens como Java e Pascal são famosas por sua verbosidade. As pessoas muitas vezes apontam para certos elementos sintáticos e afirmam com desprezo que "eles estão lá apenas para facilitar o trabalho do compilador". Isso é mais ou menos verdade, exceto pela parte "justa". Quanto mais informação explícita houver, mais fácil será o código ler e entender, não apenas por um compilador, mas também por um ser humano.

Se eu disser var x = 2 + 2; , é óbvio que x é um número inteiro. Mas se eu disser var foo = value.Response; , é muito menos claro o que foo representa ou quais são suas propriedades e capacidades. Mesmo que o compilador possa facilmente inferi-lo, ele coloca muito mais esforço cognitivo em uma pessoa.

Lembre-se de que os programas devem ser escritos para as pessoas lerem, e apenas incidentalmente, para que as máquinas executem. (Ironicamente, essa citação vem de um livro dedicado a uma linguagem famosa por ser extremamente difícil de ler!). É uma boa idéia remover coisas que são redundantes, mas não retire o código que torna mais fácil para seus companheiros seres humanos descobrir o que está acontecendo, mesmo que não seja estritamente necessário para o programa ser escrito.

    
por 31.08.2017 / 06:08
fonte
15

O código mais longo pode ser mais fácil de ler. Geralmente é o oposto, mas há muitas exceções - algumas delas descritas em outras respostas.

Mas vamos olhar de um ângulo diferente. Assumimos que o novo código será visto como superior pela maioria dos programadores habilidosos que vêem os dois códigos sem ter conhecimento adicional da cultura, base de código ou roteiro da empresa. Mesmo assim, há vários motivos para contestar o novo código. Por uma questão de brevidade, vou chamar "Pessoas criticando o novo código" Pecritenc :

  • Estabilidade. Se o código antigo era conhecido por ser estável, a estabilidade do novo código é desconhecida. Antes que o novo código possa ser usado, ele ainda precisa ser testado. Se por algum motivo o teste adequado não estiver disponível, a mudança é um problema bastante grande. Mesmo que o teste esteja disponível, Pecritenc pode achar que o esforço não vale a melhoria (menor) do código.
  • Desempenho / dimensionamento. O código antigo pode ter melhorado, e Pecritenc assume que o desempenho se tornará um problema no futuro, já que clientes e recursos logo se acumulam.
  • Extensibilidade. O código antigo pode ter permitido a fácil introdução de alguns recursos que Pecritenc supõe serem adicionados em breve *.
  • Familiaridade. O código antigo pode ter padrões reutilizados usados em outros cinco locais da base de código da empresa. Ao mesmo tempo, o novo código usa um padrão sofisticado que apenas metade da empresa já ouviu falar neste momento.
  • Batom em um porco. Pecritenc pode achar que tanto o antigo quanto o novo código são lixo, ou irrelevantes, fazendo com que qualquer comparação entre eles seja inútil.
  • Orgulho. Pecritenc pode ter sido o autor original do código e não gosta de pessoas fazendo mudanças maciças em seu código. Ele pode até ver melhorias como um insulto leve, porque elas implicam que ele deveria ter feito melhor.
por 31.08.2017 / 11:54
fonte
1

Que tipo de código é melhor pode depender da experiência dos programadores e também das ferramentas que eles usam. Por exemplo, eis por que o que normalmente seria considerado código mal escrito pode ser mais eficaz em algumas situações do que o código orientado a objetos bem escrito que faz uso total da herança:

(1) Alguns programadores simplesmente não têm uma compreensão intuitiva da programação orientada a objetos. Se a sua metáfora para um projeto de software for um circuito elétrico, você esperará muita duplicação de código. Você vai gostar de ver mais ou menos os mesmos métodos em muitas classes. Eles vão fazer você se sentir em casa. E um projeto em que você precisa procurar métodos em classes principais ou mesmo em aulas de avós para ver o que está acontecendo pode parecer hostil. Você não quer entender como a classe pai funciona e, em seguida, entender como a classe atual é diferente. Você quer entender diretamente como a classe atual funciona, e você descobre que as informações estão espalhadas por vários arquivos confusos.

Além disso, quando você quiser apenas corrigir um problema específico em uma classe específica, talvez não goste de ter que pensar em corrigir o problema diretamente na classe base ou sobrescrever o método na classe de interesse atual. (Sem herança você não teria que tomar uma decisão consciente. O padrão é simplesmente ignorar problemas similares em classes similares até que eles sejam reportados como bugs.) Este último aspecto não é realmente um argumento válido, embora possa explicar alguns dos problemas. oposição.

(2) Alguns programadores usam muito o depurador. Embora, em geral, eu esteja firmemente do lado da herança de código e evitando a duplicação, compartilho algumas das frustrações que descrevi em (1) ao depurar código orientado a objetos. Quando você segue a execução do código, às vezes ele continua pulando entre as classes (ancestrais), embora permaneça no mesmo objeto. Além disso, ao definir um ponto de interrupção em um código bem escrito, é mais provável que ele seja acionado quando não é útil, portanto você pode ter que gastar esforço para torná-lo condicional (onde for prático) ou até mesmo para continuar manualmente várias vezes antes do acionador relevante.

    
por 31.08.2017 / 11:41
fonte
1

Depende totalmente. Eu tenho trabalhado em um projeto que não permite variáveis booleanas como parâmetros de função, mas requer um enum dedicado para cada opção.

Então,

enum OPTION1 { OPTION1_OFF, OPTION1_ON };
enum OPTION2 { OPTION2_OFF, OPTION2_ON };

void doSomething(OPTION1, OPTION2);

é muito mais detalhado do que

void doSomething(bool, bool);

No entanto,

doSomething(OPTION1_ON, OPTION2_OFF);

é muito mais legível que

doSomething(true, false);

O compilador deve gerar o mesmo código para ambos, então não há nada a ser ganho usando o formato mais curto.

    
por 01.09.2017 / 15:15
fonte
0

Eu diria que a coesão pode ser um problema.

Por exemplo, em um aplicativo da web, digamos que você tenha uma página Admin na qual você indexa todos os produtos, que é essencialmente o mesmo código (índice) usado em uma situação na página inicial. produtos.

Se você decidir dividir tudo para que possa ficar seco e elegante, precisará adicionar muitas condições em relação à navegação do usuário ou não, e sobrecarregar o código com coisas desnecessárias, o que o tornará altamente ilegível. digamos de um designer!

Então, em uma situação como essa, mesmo que o código seja praticamente o mesmo, só porque poderia ser dimensionado para outra coisa e os casos de uso poderiam mudar um pouco, seria ruim ir atrás de cada um deles adicionando condições e ifs . Portanto, uma boa estratégia seria abandonar o conceito DRY e dividir o código em partes sustentáveis.

    
por 31.08.2017 / 15:49
fonte
0
  • Quando menos código não faz o mesmo trabalho que mais código. A refatoração pela simplicidade é boa, mas você deve tomar cuidado para não simplificar demais o espaço do problema que essa solução atende. 980 linhas de código podem lidar com mais casos de canto do que 450.
  • Quando menos código não falha tão bem quanto mais código. Eu vi um par de trabalhos "ref *** toring" feitos no código para remover o "try-catch" desnecessário e outras manipulações de caso de erro. O resultado inevitável foi em vez de mostrar uma caixa de diálogo com uma boa mensagem sobre o erro e o que o usuário poderia fazer, o aplicativo travou ou ficou com YSOD.
  • Quando menos código é menos sustentável / extensível do que mais código. A refatoração para a concisão do código geralmente remove construções de código "desnecessárias" no interesse do LoC. O problema é que essas construções de código, como declarações de interface paralela, métodos / subclasses extraídos, etc, são necessárias, caso esse código precise fazer mais do que o faz atualmente, ou fazer isso de forma diferente. No extremo, certas soluções customizadas para o problema específico podem não funcionar se a definição do problema mudar um pouco.

    Um exemplo; você tem uma lista de inteiros. Cada um desses números inteiros possui um valor duplicado na lista, com exceção de um. Seu algoritmo deve encontrar esse valor não pareado. A solução de caso geral é comparar todos os números contra todos os outros números até encontrar um número que não tenha dupe na lista, o que é uma operação N ^ 2 vezes. Você também pode construir um histograma usando uma hashtable, mas isso é muito ineficiente em termos de espaço. No entanto, você pode torná-lo tempo linear e espaço constante usando uma operação XOR bit a bit; XOR a cada inteiro contra um "total" em execução (começando com zero) e, no final, a soma corrente será o valor do inteiro não-pareado. Muito elegante. Até que os requisitos mudem, e mais de um número na lista possa ser desemparelhado, ou os números inteiros incluam zero. Agora seu programa ou retorna lixo ou resultados ambíguos (se ele retorna zero, isso significa que todos os elementos estão emparelhados ou que o elemento desemparelhado é zero?). Esse é o problema das implementações "inteligentes" na programação do mundo real.

  • Quando menos código é menos auto-documentado do que mais código. Ser capaz de ler o código em si e determinar o que está fazendo é fundamental para o desenvolvimento da equipe. Dar um algoritmo para o cérebro que você escreveu que funciona perfeitamente para um desenvolvedor júnior e pedir que ele o ajuste para modificar um pouco a saída não vai te levar muito longe. Muitos desenvolvedores seniores também teriam problemas com essa situação. Ser capaz de entender a qualquer momento o que o código está fazendo, e o que pode dar errado com ele, é a chave para um ambiente de desenvolvimento de equipe (e até mesmo solo; eu garanto que o flash de genialidade que você teve quando escreveu O método de linha para curar o câncer vai acabar quando você voltar a essa função, procurando fazê-lo curar o mal de Parkinson também.
por 01.09.2017 / 15:56
fonte
0

O código do computador precisa fazer várias coisas. Um código "minimalista" que não faz essas coisas não é um bom código.

Por exemplo, um programa de computador deve cobrir todos os possíveis (ou, no mínimo, todos os casos prováveis). Se um pedaço de código cobre apenas o "caso base" e ignora outros, não é um bom código, mesmo que seja breve.

O código do computador deve ser "escalável". Um código criptografado pode funcionar apenas para um aplicativo especializado, enquanto um programa mais longo, porém mais aberto, pode facilitar a inclusão de novos aplicativos.

O código do computador deve ficar claro. Como outro entrevistado demonstrou, é possível que um codificador hard-core produza uma função de tipo "algorítmica" de uma linha que faz o trabalho. Mas o one-liner teve que ser dividido em cinco "sentenças" diferentes antes que fosse claro para o programador médio.

    
por 01.09.2017 / 18:36
fonte
-2

Desempenho computacional. Ao otimizar o alinhamento ou executar partes do código em paralelo, pode ser benéfico, por exemplo, não dar um loop de 1 a 400, mas de 1 a 50 e colocar 8 instâncias de código semelhante em cada loop. Eu não estou assumindo que este era o caso em sua situação, mas é um exemplo onde mais linhas são melhores (desempenho-sábio).

    
por 31.08.2017 / 14:15
fonte