Meu código deve estar SECO ou legível se não puder ser ambos?

14

Estou escrevendo o código Ruby para um exercício de criptografia simples e frequentemente encontro esse dilema (o exercício é uma cifra de paciência, se você precisar saber). É uma questão de saber se eu devo preencher minha lógica com variáveis ​​descritivas e instruções de etapa única que tornam a função legível em vez de instruções concisas e até densas que eliminam a repetição e / ou minimizam as oportunidades de erros.

Meu exemplo mais recente: Meu programa recebe entrada e, devido às rígidas diretrizes de formato, pode determinar facilmente se a entrada deve ser criptografada ou descriptografada. Para simplificar, uma vez que a chave e a mensagem de criptografia são convertidas / geradas para serem compatíveis, é uma questão de subtrair a chave da mensagem criptografada ou adicionar a chave a uma mensagem não criptografada, para obter a saída desejada (pense na chave como criptografia, mensagem + criptografia = código; código - criptografia = mensagem). A posição DRY diz para mim que devo converter minha mensagem criptografada de forma diferente da mensagem não criptografada para que a função que recebe a chave de criptografia e a aplica à mensagem nunca precise distinguir. Descobri que isso significa que preciso de algumas instruções if aninhadas na função, mas a lógica parece sólida. Este código, no entanto, não é facilmente legível.

Por outro lado, eu poderia escrever duas funções diferentes chamadas com base em um sinalizador definido quando o aplicativo determina a criptografia ou descriptografia. Isso seria mais simples de ler, mas duplicaria a função de alto nível de aplicar a chave de criptografia a uma mensagem (fazendo com que ela fosse criptografada ou descriptografada).

Devo me inclinar para código legível ou código conciso? Ou eu perdi outra maneira de obter essa funcionalidade e satisfazer os dois princípios? É uma posição em uma escala em que é preciso considerar o objetivo do projeto e tomar as melhores decisões para servir a esse objetivo?

Até agora, costumo enfatizar o código DRY conciso sobre o código legível.

lutze
fonte
2
Primeiro utilizável, segundo seco, terceiro legível. O código altamente utilizável resulta em um código de consumo altamente legível que atende mais facilmente a DRY e legível. É por isso que você quer pegar complexidades desagradáveis ​​e colocá-las em algum lugar com uma API agradável, se elas não puderem ser melhoradas; pelo menos o código que interage com eles será poupado de ser ruim também com essa abordagem.
Jimmy Hoffa
4
alguns exemplos de código reais podem ajudar, desconfio que esteja faltando alguma terceira maneira, como funções de ordem superior, para ter código seco e legível
jk.
2
Não é uma duplicata. Conciso v. Legível e DRY v. Legível são duas comparações muito diferentes. Não ser seco é muito mais perigoso do que não ser conciso.
precisa saber é o seguinte
Apenas não caia na armadilha de que assumir código fortemente duplicado seja mais legível porque se enquadra em um padrão previsível. Suspeito que possa ser fácil pensar dessa maneira, até que você tenha mantido um sistema que se duplica tanto que encontra bugs sutis em um ramo que não estão no outro, ramos quase idênticos.
KChaloux
Não entendo o que você entende por "função de alto nível da aplicação da chave de criptografia à mensagem". Se você quer dizer que há sobreposição entre criptografar e descriptografar, considere usar o refatoramento do método extract para fatorar as partes comuns.
Aaron Kurtzhals

Respostas:

20

DRY é uma diretriz, não uma religião. Aqueles que o levam ao ponto de DRY acima de tudo, o levaram longe demais.

Em primeiro lugar, o código utilizável é fundamental. Se o código não é útil e utilizável, não é ... e não faz sentido escrevê-lo em primeiro lugar.

Segundo, algum dia alguém precisará manter seu código. Se o seu código não puder ser mantido, eles quebrarão seu design "bonito" denso, conciso e SECO enquanto amaldiçoam seu nome. Não faça isso. Eu sou essa pessoa e toda vez que vejo um determinado nome na anotação do código, estremeço.

Criar um código denso que não possua "variáveis ​​descritivas" e colocar tudo em expressões ternárias aninhadas com lambdas sem nenhuma documentação é inteligente. É bom saber que você pode fazê-lo - mas não o faz. Código inteligente é muito difícil de depurar. Evite escrever código inteligente .

A maior parte do tempo gasto em software é gasta em manutenção do software - não sendo gravada na primeira vez. Escreva o código para que você (ou qualquer outra pessoa) possa rápida e facilmente corrigir bugs e adicionar recursos conforme necessário, com o mínimo de alterações ideais no design.

Comunidade
fonte
Intuitivamente ou não, você encontrou um problema que eu estava ignorando. Escrevi o código 'inteligente' o máximo que pude para este exercício, apenas para saber que poderia fazê-lo, o que eu concordo que é bom saber, mas eu concordo com você aqui que é uma má prática. Agora, tenho muita refatoração para fazer.
Lutze
1
@ lutze Eu sou um codificador perl e às vezes ruby ​​e certamente pode entender a tentação de escrever um código inteligente lá. Uma versão pré-post desta resposta incluía uma citação de Larry Wall sobre arrogância - acredito que essa é uma virtude muito importante ao trabalhar com código que algum dia será mantido. Devido à possível discrepância das linguagens, às vezes é difícil obter a vantagem de escrever mais código em vez de tentar um código denso inteligente ... até que você precise mantê-lo.
17

Não tenho certeza com base na pergunta que você entende DRY. Código DRY não é o mesmo que conciso. Muitas vezes é o contrário.

Aqui, não tenho certeza de qual é o grande problema desse exemplo. Crie uma função para criptografar, uma função para descriptografar, ajudantes para funcionalidades comuns (misturar bytes) e um front end simples para receber entradas e determinar a criptografia / descriptografia ... Não se repita.

Em geral, porém, existem DRY e legibilidade para ajudar a manter e estender o código. Nem todos os cenários são iguais, uma grande legibilidade para remover um pouco de repetição não é boa, e nem um monte de duplicação para adicionar um pouco de legibilidade.

Se pressionado, eu preferiria a legibilidade. O código duplicado ainda pode ser testado - o código ilegível leva você a fazer (e testar) a coisa errada.

Telastyn
fonte
Ótimos pontos, eu realmente combinara um pouco o princípio DRY com o objetivo do código conciso.
Lutze
12

Não estou familiarizado com o seu caso específico, mas posso fornecer algumas diretrizes gerais.

O objetivo da legibilidade e do DRY é a manutenção .

A manutenção é importante na maioria das situações, você gastará mais tempo mantendo o código do que escrevendo. Isso é especialmente verdadeiro se você considerar que escrever código é um tipo especial de manutenção.

Infelizmente, DRY é muitas vezes incompreendido. Isso ocorre em parte porque parece muito simples, mas, como muitas coisas simples, pode ser, bem ... complicado.

A intenção do DRY é que cada unidade de funcionalidade exista em apenas um local. Se o princípio for seguido, o mantenedor do código encarregado de alterar ou verificar essa funcionalidade pode lidar com o código de uma só vez. Se o DRY não for seguido, há um risco muito real de que algumas cópias da funcionalidade não sejam mantidas adequadamente.

A violação mais flagrante do DRY é a codificação de copiar e colar, na qual blocos inteiros de código são repetidos verbatum na base de código. Isso é surpreendentemente comum na minha experiência, e de nenhuma maneira isso contribui para a legibilidade. Portanto, refatorar o código para introduzir um método comum invariavelmente aumenta a conformidade e a legibilidade do DRY.

A segunda violação mais flagrante é "copiar e colar e alterá-lo um pouco". Novamente, refatorar para introduzir um método comum com parâmetros ou dividir uma função em etapas e abstrair os pontos comuns quase sempre aumentam a legibilidade.

Existem as violações mais sutis, nas quais a funcionalidade é duplicada, mas o código é diferente. Isso nem sempre é fácil de detectar, mas quando você o vê e refatora para extrair o código comum em um único método / classe / função, o código fica usualmente mais legível do que era antes.

Finalmente, há os casos de repetição de design. Por exemplo, você pode ter usado o padrão de estado várias vezes em sua base de código e está pensando em refatorar para eliminar essa repetição. Nesse caso, continue com cuidado. Você pode estar reduzindo a legibilidade introduzindo mais níveis de abstração. Ao mesmo tempo, você não está realmente lidando com a duplicação da funcionalidade, mas com a duplicação da abstração. Às vezes vale a pena ... mas muitas vezes não é. Seu princípio orientador será perguntar "qual destes é mais sustentável".

Sempre que faço essas chamadas de julgamento, tento considerar a quantidade de tempo que as pessoas gastam mantendo o código. Se o código é a funcionalidade principal do negócio, provavelmente precisará de mais manutenção. Nesses casos, pretendo tornar o código sustentável para pessoas familiarizadas com a base de código e as abstrações envolvidas. Eu ficaria mais feliz em apresentar um pouco mais de abstração para reduzir a repetição. Por outro lado, um script raramente usado e mantido com pouca frequência pode ser menos fácil de entender para os mantenedores se envolver muita abstração. Nesse caso, eu iria errar do lado da repetição.

Também considero o nível de experiência de outros membros da minha equipe. Evito abstrações "sofisticadas" com desenvolvedores inexperientes, mas aproveito os padrões de design reconhecidos pela indústria com grupos mais maduros.

Em conjunto, a resposta para sua pergunta é fazer o que torna seu código mais sustentável. O que isso significa no seu cenário depende de você decidir.

Kramii
fonte
Acho que você acertou exatamente um dos meus problemas. O exemplo que dei da funcionalidade que estava tentando não duplicar é mais provável que seja a duplicação da abstração.
Lutze
+1. Atualmente, estou lidando com um sistema que abusou copiar e colar em um nível ridículo. Factoring um único método de copiar / colar código removido mais de 400 linhas de uma das minhas classes hoje. Infelizmente, eu encontrei-o em um método 900 de linha ...
KChaloux
1
Se duas construções de código atualmente fazem a mesma coisa, mas geralmente uma alteração em uma não implica uma alteração na outra, copiar / colar pode ser melhor do que a funcionalidade comum fatorada. Se o código usar dois métodos idênticos de caractere por caractere, fizer a mesma coisa para propósitos diferentes e basear consistentemente a escolha do método no objetivo requerido, se as coisas posteriores precisarem ser feitas ligeiramente para um desses propósitos, alterando apenas o apropriado O método afetará apenas os comportamentos que devem ser afetados. Usar um único método comum poderia ter dificultado muito a mudança .
Supercat 26/03
7

Se suas funções se tornarem mais complicadas e mais longas (portanto, menos legíveis) quando você tentar torná-las SECAS, estará fazendo errado. Você está tentando colocar muita funcionalidade em uma função porque acha que essa é a única maneira de evitar repetir os mesmos trechos de código em uma segunda função.

Tornar o código DRY quase sempre significa refatorar a funcionalidade comum para funções menores. Cada uma dessas funções deve ser mais simples (e, portanto, mais legível) do que a original. Para manter tudo na função original no mesmo nível de abstração, isso também pode significar refatorar peças adicionais que não são usadas em outros lugares. Sua função original então se torna uma "função de alto nível", chamando as menores, e fica menor e mais simples também.

Fazer isso consequentemente leva principalmente a diferentes níveis de abstração no seu código - e algumas pessoas acreditam que esse tipo de código é menos legível. Para minha experiência, isso é uma falácia. O ponto principal aqui é dar bons nomes às funções menores, introduzir tipos de dados e abstrações bem nomeados, que podem ser facilmente compreendidos por uma segunda pessoa. Dessa forma, "SECO" e "legibilidade" só devem muito, muito raramente, entrar em conflito.

Doc Brown
fonte
2

Embora não saiba exatamente o que está causando o problema aqui, minha experiência é que, quando você acha DRY e a legibilidade conflitantes, é hora de refatorar algo.

Loren Pechtel
fonte
0

Eu escolheria legível (= mantenível) em vez de SECO em qualquer dia da semana. Na realidade, os dois geralmente se alinham, alguém que entende o conceito de DRY geralmente produzia (principalmente) código legível.

Zdravko Danev
fonte
2
sem uma explicação, essa resposta pode se tornar inútil se outra pessoa postar uma opinião oposta. Por exemplo, se alguém postar uma declaração como "Eu escolheria SECO (= mantenível) em vez de qualquer dia da semana", como essa resposta ajudaria o leitor a escolher duas opiniões opostas? Considere editar ing-lo em uma melhor forma
mosquito
0

Muito, muito, muito, muito poucas vezes na minha carreira, encontrei um caso legítimo que lesse a legibilidade contra a DRY. Torne-o legível primeiro. Nomeie bem as coisas. Copie e cole, se necessário.

Agora, dê um passo atrás e observe a totalidade que você criou, como um artista dando um passo atrás para ver a pintura deles. Agora você pode identificar corretamente a repetição.

O código repetido deve ser refatorado em seu próprio método ou extraído em sua própria classe.

A repetição de código deve ser o último recurso. Legível E SECO primeiro, na falta disso, se você limitar o escopo do código repetido.

Greg Burghardt
fonte