DRY (Dont Repeat Yourself) é sobre duplicação de conhecimento!

Cada pedaço de conhecimento deve ter uma representação única, inequívoca e autoritária dentro de um sistema - Andrew Hunt and David Thomas.

DRY (Dont Repeat Yourself) é sobre duplicação de conhecimento!

Leia a frase a seguir do livro The Pragmatic Programmer:

"Every piece of knowledge must have a single, unambiguous, authoritative representation within a system. - by Andrew Hunt and David Thomas.

Tradução: Cada pedaço de conhecimento deve ter uma representação única, inequívoca e autoritária dentro de um sistema."

Estas palavras se referem ao DRY (Dont repeat yourself). Parece bastante simples. Mas DRY não é apenas sobre não repetir código que pareça igual, mas sim sobre conhecimento dentro do sistema.

Isso levanta uma questão importante: o que é um pedaço de conhecimento? Podemos definir como sendo:

  • Uma funcionalidade única na regra de negócios (no domínio) do seu software.
  • Um algoritmo: Apenas lembrando que algoritmo é um conjunto finito de passos claros que são aplicados sistematicamente (de maneira ordenada) até que a solução seja atingida. De forma simples, podemos dizer que um algoritmo define o caminho que deve ser percorrido para chegar até a solução de um determinado problema.

Dito isso vamos iniciar pelo que é chamado de pedaço de conhecimento do sistema. Vamos imaginar que um desenvolvedor precisa criar uma funcionalidade que valide se o nome do usuário está entre 10 à 200 caracteres. O desenvolvedor acaba criando a validação, mas por descuido não cria uma classe que contém apenas a responsabilidade de validar o nome do usuário. O que pode acontecer? Outro desenvolvedor que venha precisar utilizar essa validação em outro componente do software, acaba replicando aquela mesma lógica, que podemos chamar de conhecimento, em outra classe, que precisa validar um nome de usuário também. Agora duas classes tem conhecimentos duplicados e sabem como validar um nome de usuário. Isso é uma clara violação do princípio DRY. O sistema vai continuar a ter duplicações de lógicas desnecessárias até que alguém crie uma representação única daquele conhecimento dentro do sistema, que no caso é sobre o Nome. Veja abaixo um exemplo de como seria uma forma correta de centralizar esse conhecimento:

import { left, right, Either } from '@/shared'
import { InvalidNameError } from '@/entities/errors'

export class Name {
  public readonly value: string

  private constructor (name: string) {
    this.value = name
  }

  public static create (name: string): Either<InvalidNameError, Name> {
    if (!Name.validate(name)) {
      return left(new InvalidNameError(name))
    }

    return right(new Name(name))
  }

  public static validate (name: string): boolean {
    if (!name) {
      return false
    }

    if (name.trim().length < 10 || name.trim().length > 200) {
      return false
    }

    return true
  }
}

Nem sempre estamos violando o DRY

Veja mais um exemplo. Imagine que temos este código:

interface Product {
  color: string
  size: string
  type: string
}

class CsvValidation
{
     public validateProduct(product: Product)
    {
        if (!product.color) {
            throw new Error('Import fail: the product attribute color is missing');
        }

        if (!product.size) {
            throw new Error('Import fail: the product attribute size is missing');
        }

        if (!product.type) {
            throw new Error('Import fail: the product attribute type is missing');
        }
    }
}

O método valida algumas saídas CSV em apenas um lugar validateProduct(). Este é o conhecimento e nunca se repete.

As condições ifse repetindo, não são uma violação do princípio, pois a lógica de negócio não vai se repetir em nenhum outro lugar do sistema. Podemos chamar isso duplicação de código desnecessária, de sintaxe que não duplica conhecimento ou conceitos de sistema. Ao tentar identificar a duplicação, podemos nos perguntar:

"Estamos olhando para a duplicação de sintaxe ou duplicação de conhecimento?" - Antonio Sciamanna.

No caso temos repetição da estrutura condicional if. E de lançamento de Error também. MAS O CONHECIMENTO NÃO ESTÁ DUPLICADO.

Podemos melhorar muito nosso código, a estrutura dele, com uma refatoração:

class CsvValidation
{
    private  productAttributes = [
        'color',
        'size',
        'type',
    ];

    public validateProduct(product: Product)
    {
        for (const attribute of this.productAttributes) {
            if (!product) {
                throw Error(`Import fail: the product attribute is missing ${attribute}`);
            }
        }
    }
}

Não há mais duplicação de código! E o que podemos aprender? Primeiro, vimos que a duplicação de conhecimento viola claramente o princípio Dont Repeat Yourself. Segunda lição, ter código duplicado não viola necessariamente o princípio DRY.

Inequívoca

Na frase que se inicia este artigo, temos a palavra Inequívoca. Vamos tentar nos aprofundar mais nela. A palavra Inequívoca significa segundo dicionários, algo que não permite dúvida, engano, erro. Então também temos uma alerta. Precisamos tomar cuidado em não deixar que nada que faça parte do conhecimento dentro do sistema acabe se transformando em dúvida para outros desenvolvedores e até mesmo leve outros a caminhos errados. Nós não devemos apenas nos preocupar em evitar duplicação de conhecimento, mas também é nossa obrigação nos preocupar com a clareza que nosso código está escrito. Veja um exemplo:

// Primeiro exemplo
class DtaRcrd102 {
 private Date genymdhms;
 private Date modymdhms;
}

// Segundo exemplo
if (student.classes.length < 7) {
   // Do something
}

Note que nos códigos acima temos alguns problemas, mesmo que eles sejam o mais simples possível. O primeiro exemplo é impossível entender SOBRE O QUE DtaRcrd102 se trata e genymdhms, modymdhms? No segundo exemplo o que representa o número 7 nessa condicional? Se eu trocar esse número para 8, 9. Isso vai afetar de alguma maneira a regra de negócio?

Mas você deve estar pensando: Mas isso é sobre clean code, não sobre DRY!

Sim estes conceitos foram abordados no livro de Robert Martin, mas estão ligados ao Dont Repeat Yourself! Do que adianta você estar obcecado em ter o menos possível de duplicação de conhecimento ou código, quando o maior problema está em um desenvolvedor olhar para um componente reutilizável que você formou as pressas e não conseguir entender a lógica, até mesmo os nomes das variáveis dentro da classe?

Por isso esse pedaço de conhecimento, não permite dúvida! Deve ser claro sobre o que se trata aquele conhecimento em seu componente.

// Exemplos refatorados
if (student.classes.length < MAX_CLASSES_PER_STUDENT) {
    // Do something
}

class Customer {
 private Date generationTimestamp;
 private Date modificationTimestamp;
}

Vale sempre relembrar as palavras de Martin Fowler no livro Refatoração: Aperfeiçoando o design de códigos existentes:

" Uma das partes mais importantes de um código claro são nomes bons, portanto devemos pensar bem ao nomear funções, módulos, variáveis e classes, de modo que eles comuniquem claramente o que fazem e como são usados "

Alertas sobre Refatoração prematura

Você não deve aplicar o princípio DRY se sua lógica de negócios ainda não tiver nenhuma duplicação! Obviamente depende muito do contexto mas, como regra geral, tentar aplicar DRY a algo que é usado apenas em um lugar pode levar a uma generalização prematura. Veja o que pode acontecer se cada implementação você já implementa o princípio:

  • Você possivelmente introduzirá complexidade e acoplamento em seu código o que trará nenhum benefício!

  • Gastará tempo para criar abstrações (classes abstratas) que podem ser usadas apenas em um lugar, para sempre. As necessidades de negócios podem mudar drasticamente: hoje, todo mundo vai te dizer que você precisa de alguma generalização “para o futuro”, e amanhã, todo mundo vai esquecê-la. Lembre-se que mudanças ocorrem a todo momento.

A reutilização de código e a duplicação de código são duas coisas diferentes. DRY afirma que você não deve duplicar o conhecimento, não que você deve usar abstrações para reutilizar tudo e em todos os lugares.

Duas features, mesmo que sejam muito semelhantes enquanto o software está em fase de construção, podem se tornar muito diferentes no futuro. Se você não tem certeza de como o sistema estará daqui dois meses é melhor copiar seu código e deixá-lo seguir um caminho diferente.

Uma duplicação de código sai mais barato do que criar abstrações enganosas.

As violações do princípio DRY devem ser tratadas quando o conhecimento já existir e estiver obviamente duplicado.

Conclusão

Poderia colocar muitos outros exemplo em código e contar situações que já presenciei no dia a dia como Desenvolvedor de Software. Mas o objetivo é deixar claro que DRY não é apenas sobre evitar duplicação de código. E sobre isso podemos concluir este artigo com a seguinte frase dos autores do livro The Pragmatic Programmer na edição de aniversário de 20 anos do livro:

"Muitas pessoas tomaram [o princípio DRY] para se referir apenas ao código: eles pensaram que DRY significa “não copie e cole linhas de fonte”. […] DRY é sobre a duplicação de conhecimento, de intenção. Trata-se de expressar a mesma coisa em dois lugares diferentes, possivelmente de duas maneiras totalmente diferentes."

É importante lembrar que o conhecimento dentro de um software deve ser único!

Fico por aqui neste artigo. Qualquer dúvida ou critica construtiva por favor entrem em contato. Até o próximo!