Copie e cole o cabeçalho de for-loop (por exemplo: for(let i = 0; isomething.length; i ++)) violando o princípio DRY?

3

Por exemplo, no meu projeto, muitas vezes encontrei algumas cabeças de loop for exibido muitas vezes, por exemplo:

for(let i=0;i<SharedData.students.length;i++){
    SharedData.students[i].something=.....
}

if(isReset){
    for(let i=0;i<SharedData.students.length;i++){
        SharedData.students[i].reset();
    }
}
.
.
.

qual a tarefa que dentro e fora de loop for totalmente diferente, mas geralmente precisa

for(let i=0;i<SharedData.students.length;i++)

Então, minha pergunta é: copiar e colar

for(let i=0;i<SharedData.students.length;i++)

violando o princípio DRY?

    
por mmmaaa 14.12.2018 / 03:51
fonte

5 respostas

20

O princípio Don't Repeat Yourself (DRY) é fácil de aplicar.

Tenha em mente que o verdadeiro pecado não está usando copiar e colar. Está espalhando uma decisão de uma forma que torna difícil mudar essa decisão. Se o que você realmente tem é duas decisões que parecem ser as mesmas no momento, então está tudo bem. Você estaria causando dano se você forçasse as duas decisões a serem expressas no mesmo lugar.

Ao deixá-los separados, como você tem agora, você está permitindo que os dois loops variem independentemente. Se você reescreveu eles como Robert Harvey sugere:

for(let i=0;i<SharedData.students.length;i++){
    SharedData.students[i].something=.....
    if(isReset){
        SharedData.students[i].reset();
    }
}

então você perderia a capacidade de fazê-los variar independentemente (por exemplo, ter um pulando o primeiro elemento, por qualquer motivo).

Essa ideia pode ser difícil de entender, então deixe-me dizer de outra forma:

int x = 100;
int y = 100;    

Aqui está uma "violação" de DRY que a maioria das pessoas não pensaria duas vezes. Por quê? Porque sabemos que, embora y seja uma cópia redundante de x, pode nem sempre ser. Tem seu próprio significado. Nós não queremos perder esse significado só porque ele tem o mesmo valor de x agora.

Então, por favor, quando você pensar em DRY, pense menos sobre copiar e colar e mais sobre o que está facilitando a mudança.

    
por 14.12.2018 / 05:03
fonte
7

Is this violating the DRY principle?

Até certo ponto, sim - e acho um pouco surpreendente que alguns comentadores aqui pareçam ignorá-lo ou negá-lo. De fato, as conseqüências são geralmente aceitáveis em muitos casos do mundo real, mas acho que vale a pena dar uma olhada mais de perto no exemplo.

Então, vamos supor por um momento essa afirmação

for(let i=0;i<SharedData.students.length;i++){
    SharedData.students[i].something=.....

aparece 100 vezes em um programa. Depois, há algumas decisões de design que já se tornaram mais difíceis de mudar:

  1. a decisão de ter SharedData um atributo chamado students

  2. a decisão de que students é uma matriz indexável

  3. a decisão de que os alunos têm um campo mutável chamado something

(Claro, você não escreveu para repetir a parte interna do loop, mas deixe-me colocar isso neste exemplo para fins de demonstração)

Então, como você pode atenuar esses problemas? O primeiro pode ser mitigado, evitando repetir a expressão explícita SharedData.students com mais frequência do que o necessário. Muitas vezes, uma variável local adicional simples pode ajudar:

 let studentArray = SharedData.students;
 for(let i=0;i<studentArray.length;i++){
        studentArray.something=.....

Observe que essa simples alteração divide o número de repetições de SharedData.students por dois. Em uma escala maior, você pode considerar ter várias funções implementadas em termos de um parâmetro studentArray em vez de um parâmetro SharedData .

O problema 2 pode ser mitigado, por exemplo, usando uma instrução foreach , se sua linguagem de programação tiver algo assim:

  foreach(student in studentArray){
       student.something = ...

Agora, é necessário ter apenas students um contêiner iterável, que é uma suposição mais fraca do que ser uma matriz indexável.

O problema 3 pode ser atacado pelo encapsulamento da parte interna do loop for dentro de uma função:

foreach(student in studentArray)
      DoSomething(student);

Agora, a lógica de manipular ou usar student de uma maneira específica está em um lugar, não em 100 mais.

Talvez valha a pena dar uma olhada no porquê de um tal cabeçalho repete tantas vezes dentro de um programa. Pode ser um sinal de que a seção de código geral contendo o loop for pode ser generalizada, talvez introduzindo a operação como um parâmetro em si (prefiro a sintaxe C #, suponho que você tenha a idéia):

 void DoSomethingForAllStudents(Action<Student> DoSomething)
 {
     foreach(student in studentArray)
         DoSomething(student);
 }

Mas cuidado, isso já pode ser overengineered, e se o custo de tornar as coisas menos DRY é overengineering, muitas vezes você deve deixar essas coisas como são.

Como escrevi no início, em muitos casos do mundo real, os problemas nomeados são decisões de design que você não vai mudar mais tarde durante toda a vida do seu programa, ou onde o número real de repetições não é tão alto . Portanto, mesmo que isso literalmente viole DRY, não pense demais nisso.

    
por 14.12.2018 / 07:55
fonte
1

Sim, isso está violando o princípio DRY. O princípio DRY não é absoluto e deve ser usado com um padrão pessoal. Nós não vivemos em um mundo perfeito.

A frequência de uso é um grande problema no entanto. Se você estiver copiando e colando o mesmo código com frequência, eu diria que você deve SECAR.

    
por 14.12.2018 / 23:46
fonte
0

Primeiro, um outro exemplo mais reconhecível de repetidos for-loops:

for (let i = 0; i < columnCount; ++i) {
    write getColumnTitle(i);
}
writeln();
for (let rowI = 0; rowI < rowCount; ++rowI) {
    row = rows.get(i);
    for (let i = 0; i < columnCount; ++i) {
        write row.getColumnData(i);
    }
    writeln();
}

É fácil concordar que o loop é um pouco detalhado (isto é, o i ), um iterador ou melhor um fluxo (passando uma expressão de função lambda sendo repetidamente chamada) pode ser um estilo melhor.

Mas, neste caso, o princípio DRY não é válido.

No entanto, se você vir o seguinte padrão:

for i A
B
for i C
D
for i E
F

Em seguida, o problema não é DRY, mas sim que o código está funcionando geralmente trabalhando em todo i-tuples, registros, mas o loop lida com uma segunda dimensão.

rec.a()
B
rec.c()
D
rec.e()
F

Este código parece um processamento sequencial e é quase um loop aninhado for j for i X Y .

O erro mais provável aqui é uma Separação de dúvidas violada.

Por exemplo, a importação de um arquivo do Excel para um banco de dados também tem essa abordagem bidimensional: ler o Excel, gravar registros do banco de dados.

A melhor prática é:

  • crie um leitor de tabelas do Excel, que pode ser passado:
    • uma definição de tabela
    • uma função de importação de linha
  • cria um gravador de banco de dados de linha
  • faça o importador de integração

Sinopse

  1. A abordagem rec.a () de eliminar os loops for é um passo à frente, mas pode causar muita complexidade de muitos métodos muito específicos e altamente vinculados.

  2. A separação de interesses pode simplificar as coisas: também o leitor do Excel mencionado acima pode ser desenvolvido de forma orientada a testes em um teste de unidade.

por 14.12.2018 / 15:47
fonte
0

Depende.

O objetivo do principal DRY não é tanto eliminar linhas de código de "aparência idêntica" quanto "facilitar a consistência" e "reduzir o ônus da mudança".

Tomando seu exemplo,

for(let i=0;i<SharedData.students.length;i++)

Em que circunstâncias essa linha mudaria? Essas circunstâncias são "prováveis"? E, no caso dessa mudança, as linhas idênticas ou semelhantes também mudariam de forma idêntica?

Use seu julgamento. Calibre suas próprias sensibilidades ao longo do tempo.

    
por 15.12.2018 / 02:25
fonte