O princípio DRY (Don’t Repeat Yourself) é um princípio de desenvolvimento de software que promove a eliminação de duplicação de código e enfatiza a importância de evitar a repetição desnecessária de informações ou lógica em um sistema, pois a duplicação de código pode levar a problemas de manutenção, aumento da complexidade e inconsistências.
O princípio DRY pode ser resumido pela seguinte frase: “Toda parte do conhecimento deve ter uma representação única, não ambígua e autoritativa no sistema”.
Este princípio foi popularizado pelo livro “The Pragmatic Programmer” de Andrew Hunt e David Thomas.
Aqui estão algumas informações sobre como o princípio DRY atua, como funciona, como pode ser usado e onde pode ser aplicado:
- Evitar duplicação: O princípio DRY visa eliminar a duplicação de código, seja em forma de repetição literal de trechos de código, repetição de lógica semelhante ou redundância de informações. Ao evitar a duplicação, o código se torna mais conciso, fácil de manter e menos propenso a erros.
- Centralização do conhecimento: O DRY incentiva a centralização do conhecimento em um único lugar no sistema. Em vez de ter a mesma lógica espalhada em vários lugares, é preferível ter um único local onde a lógica é definida e referenciada sempre que necessário. Isso facilita a manutenção e evita inconsistências.
- Reutilização de código: O DRY promove a reutilização de código existente. Em vez de duplicar código, é preferível criar abstrações ou componentes reutilizáveis que possam ser compartilhados em diferentes partes do sistema. Dessa forma, uma única alteração nesses componentes afeta todas as suas instâncias.
- Simplificação e legibilidade: Ao eliminar a duplicação, o código se torna mais simples, conciso e legível. A lógica é expressa em um único lugar, o que facilita a compreensão e a depuração. Além disso, alterações futuras são mais fáceis de serem feitas, pois só precisam ser aplicadas em um único local.
O princípio DRY pode ser aplicado em diversos níveis no desenvolvimento de software, desde a granularidade do código até a arquitetura do sistema. Alguns exemplos de onde o DRY pode ser usado incluem:
- Evitar a repetição de blocos de código semelhantes em várias partes do sistema.
- Utilizar funções, métodos ou classes reutilizáveis para evitar duplicação de lógica.
- Centralizar informações comuns em constantes, enums ou configurações.
- Usar herança, interfaces ou composição para compartilhar funcionalidades entre diferentes classes.
- Refatorar trechos de código duplicados para criar abstrações e componentes reutilizáveis.
A seguir temos alguns exemplos básicos mostrando o uso deste princípio:
1: Evitar duplicação de código
| // Exemplo não DRY public void CalcularAreaCirculo(double raio) { double area = Math.PI * raio * raio; Console.WriteLine(“A área do círculo é: “ + area); }public void CalcularPerimetroCirculo(double raio) { double perimetro = 2 * Math.PI * raio; Console.WriteLine(“O perímetro do círculo é: “ + perimetro); } |
DRY :
| // Exemplo DRY public void CalcularCirculo(double raio) { double area = Math.PI * raio * raio; double perimetro = 2 * Math.PI * raio; Console.WriteLine(“A área do círculo é: “ + area); Console.WriteLine(“O perímetro do círculo é: “ + perimetro); } |
No exemplo acima, inicialmente temos duas funções separadas para calcular a área e o perímetro de um círculo. Isso resulta em duplicação de código.
No exemplo DRY, a duplicação é eliminada, criando uma única função CalcularCirculo que calcula tanto a área quanto o perímetro. Isso reduz a repetição de código e facilita a manutenção, já que qualquer alteração na fórmula do círculo precisa ser feita em um único local.
2: Centralizar informações comuns
| // Exemplo não DRY public void ExibirErro(string mensagem) { Console.WriteLine(“Erro: “ + mensagem); }public void ExibirAviso(string mensagem) { Console.WriteLine(“Aviso: “ + mensagem); }public void ExibirInformacao(string mensagem) { Console.WriteLine(“Informação: “ + mensagem); } |
Usando DRY:
| // Exemplo DRY public void ExibirMensagem(string tipo, string mensagem) { Console.WriteLine(tipo + “: “ + mensagem); } |
Neste exemplo, inicialmente temos três funções separadas para exibir diferentes tipos de mensagens (erro, aviso e informação). Isso resulta em duplicação de código.
No exemplo DRY, a duplicação é evitada, criando uma única função ExibirMensagem que recebe um parâmetro adicional indicando o tipo de mensagem a ser exibida. Isso centraliza a lógica comum e reduz a repetição de código.
3: Reutilização de código com herança
| // Exemplo não DRY public class Retangulo { public double Largura { get; set; } public double Altura { get; set; } public virtual double CalcularArea() { return Largura * Altura; } }public class Quadrado : Retangulo { public override double CalcularArea() { return Largura * Largura; } } |
Usando DRY:
| public class Figura { public double Largura { get; set; } public double Altura { get; set; } public virtual double CalcularArea() { return Largura * Altura; } }public class Quadrado : Figura { public override double CalcularArea() { return Largura * Largura; } } |
Neste exemplo, inicialmente temos duas classes, Retangulo e Quadrado, onde Quadrado herda de Retangulo. No entanto, essa hierarquia não segue o princípio DRY, pois há duplicação de código na implementação de CalcularArea.
No exemplo DRY, a duplicação é evitada, criando uma classe base Figura que contém as propriedades Largura e Altura, bem como a implementação comum de CalcularArea. A classe Quadrado herda de Figura e substitui apenas o método necessário, mantendo o código mais limpo e reutilizando a lógica da classe base.
4- Utilizar constantes em vez de valores literais repetidos
| // Exemplo não DRY public double CalcularCircunferencia(double raio) { return 2 * Math.PI * raio; }public double CalcularAreaCirculo(double raio) { return Math.PI * raio * raio; } |
usando DRY:
| private const double Pi = Math.PI;public double CalcularCircunferencia(double raio) { return 2 * Pi * raio; }public double CalcularAreaCirculo(double raio) { return Pi * raio * raio; } |
Neste exemplo, inicialmente temos duas funções que calculam a circunferência e a área de um círculo. Ambas as funções repetem o valor de Math.PI. No exemplo DRY, o valor de Math.PI é armazenado em uma constante Pi, evitando a repetição desnecessária.
5- Utilizar herança para compartilhar comportamento comum
| // Exemplo não DRY public class Animal { public virtual void EmitirSom() { Console.WriteLine(“O animal emite um som.”); } }public class Gato : Animal { public override void EmitirSom() { Console.WriteLine(“O gato mia.”); } }public class Cachorro : Animal { public override void EmitirSom() { Console.WriteLine(“O cachorro late.”); } } |
usando DRY:
| public abstract class Animal { public abstract void EmitirSom(); }public class Gato : Animal { public override void EmitirSom() { Console.WriteLine(“O gato mia.”); } } public class Cachorro : Animal { public override void EmitirSom() { Console.WriteLine(“O cachorro late.”); } } |
Aqui, inicialmente temos uma classe base Animal e subclasses Gato e Cachorro, onde cada animal emite um som específico. No exemplo DRY, a classe base Animal é definida como abstrata, com um método abstrato EmitirSom().
As subclasses Gato e Cachorro herdam de Animal e fornecem suas próprias implementações do método EmitirSom(). Dessa forma, o comportamento comum é compartilhado através da herança, evitando duplicação desnecessária de código.
6- Utilizar métodos genéricos para evitar repetição de código similar
| // Exemplo não DRY public void ImprimirInteiros(List<int> numeros) { foreach (int numero in numeros) { Console.WriteLine(numero); } }public void ImprimirStrings(List<string> strings) { foreach (string str in strings) { Console.WriteLine(str); } } |
usando DRY:
| public void ImprimirElementos<T>(List<T> elementos) { foreach (T elemento in elementos) { Console.WriteLine(elemento); } } |
Neste exemplo, inicialmente temos duas funções que imprimem elementos de listas específicas: uma para inteiros e outra para strings.
No exemplo DRY, a duplicação é evitada usando um método genérico ImprimirElementos<T>() que aceita uma lista de qualquer tipo T e itera sobre os elementos para imprimi-los. Isso elimina a necessidade de escrever métodos separados para cada tipo específico.
Esses exemplos demonstram diferentes maneiras de aplicar o princípio DRY em código C#. O objetivo é evitar a duplicação desnecessária de código, centralizar informações comuns, compartilhar comportamentos similares e utilizar abstrações adequadas para promover a reutilização de código. Isso resulta em um código mais conciso, legível e fácil de manter.
E estamos conversados.




