Dominando o princípio DRY: Aplicando padrões de projeto em C# para um código mais limpo.
Os padrões de projeto em C# são ferramentas essenciais para escrever código eficiente, reutilizável e de fácil manutenção. Esses padrões fornecem soluções padronizadas para problemas comuns de design de software, promovendo as melhores práticas e ajudando os desenvolvedores a evitar código redundante. Um dos princípios fundamentais na aplicação de padrões de projeto é o princípio DRY (Don't Repeat Yourself, ou Não se Repita), que enfatiza a minimização da repetição no código para melhorar a legibilidade e a manutenção.
Este artigo foi inspirado no vídeo esclarecedor de Tim Corey, " Design Patterns: Don't Repeat Yourself in C# ", que explora em profundidade o princípio DRY e sua aplicação prática na criação de um código mais limpo e organizado. Ao explorar os principais conceitos e estratégias discutidos no vídeo de Tim, este artigo tem como objetivo fornecer um guia completo para implementar o princípio do padrão de projeto DRY (Don't Repeat Yourself) de forma eficaz em seus projetos C#.
Introduction to the DRY Principle in C
Na introdução, Tim Corey explica o princípio DRY, que significa "Don't Repeat Yourself" (Não se Repita). Esse princípio é um conceito fundamental em programação que enfatiza a importância de evitar redundância, garantindo que cada informação ou lógica esteja representada em um único lugar no código. Tim ilustra o princípio usando um exemplo simples de um aplicativo WinForms com um formulário de painel de controle. O formulário inclui campos para inserir o nome e o sobrenome, além de um botão para gerar um ID de funcionário com base nesses campos.
Identificação e antecipação de repetição de código
Em (0:53), Tim passa a identificar e antecipar repetições no código. Ele usa o exemplo do aplicativo WinForms para mostrar como a repetição pode ocorrer, mesmo quando os métodos são chamados apenas uma vez. No aplicativo, a lógica de geração do ID do funcionário envolve a extração de substrings dos campos de texto para o primeiro e último nome e a adição de um código de 3 dígitos no final.

Na captura de tela acima, em (1:31), Tim demonstra a funcionalidade do aplicativo, mostrando como ele gera um ID de funcionário combinando as quatro primeiras letras do primeiro e do último nome com um código de três dígitos. Ele destaca que, embora o código pareça seguir o princípio DRY (Don't Repeat Yourself - Não se Repita) por não repetir explicitamente a mesma lógica, existem problemas subjacentes com o padrão de repetição que precisam ser resolvidos.
Em (1:51), ele aponta que, embora o código pareça simples, ele não adere totalmente ao princípio DRY porque a lógica para gerar o ID do funcionário está fortemente acoplada ao evento de clique do botão. Isso significa que, se essa lógica fosse necessária em outro lugar no código do cliente, como ao processar uma lista de novos funcionários (3:58), o código precisaria ser repetido ou adaptado, levando à redundância.
Criando métodos independentes e reutilizáveis
Neste segmento, Tim Corey demonstra como criar um método independente e reutilizável para aderir ao princípio DRY (Don't Repeat Yourself). Ele começa extraindo a lógica para gerar um ID de funcionário do manipulador de eventos para um método separado. Essa refatoração envolve a criação de um método privado chamado GenerateEmployeeID e a movimentação do código existente para este método (5:15). O código revisado no manipulador de eventos simplesmente chama esse método.
Passos e exemplo:
-
Código inicial: A lógica para gerar o ID do funcionário estava diretamente no manipulador de eventos de clique de um botão.

-
Código refatorado: Tim aprimora o método, tornando-o mais flexível. Em vez de depender de elementos específicos da UI, o método agora aceita
firstNameelastNamecomo parâmetros e retorna o ID gerado. Essa alteração permite que o método seja usado em diversos contextos e elementos da interface do usuário:private string GenerateEmployeeID(string firstName, string lastName) { string employeeID = firstName.Substring(0, 4) + lastName.Substring(0, 4) + DateTime.Now.Millisecond.ToString(); return employeeID; }private string GenerateEmployeeID(string firstName, string lastName) { string employeeID = firstName.Substring(0, 4) + lastName.Substring(0, 4) + DateTime.Now.Millisecond.ToString(); return employeeID; }Tim demonstra então como esse método é chamado a partir do evento de clique:
employeeIdText.Text = GenerateEmployeeID(firstNameText.Text, lastNameText.Text);employeeIdText.Text = GenerateEmployeeID(firstNameText.Text, lastNameText.Text);Ele também observa que esse método agora pode ser usado em outras partes do aplicativo, como no processamento de arquivos CSV com vários registros de funcionários, sem repetir o código.
Construindo e utilizando uma biblioteca de sala de aula
Tim Corey explora então o conceito de uma biblioteca de classes para melhorar ainda mais a reutilização e a manutenção do código. Ele ilustra como encapsular o método GenerateEmployeeID em um objeto de biblioteca de classes, que pode ser utilizado em vários projetos.
Às 8h, Tim explica que o design continua mudando com base nas necessidades do usuário ou nas políticas da empresa para torná-lo mais interativo com gráficos e animações. Assim, ele introduz um projeto WPF dentro da solução com campos específicos e um botão para gerar o ID do funcionário.
Tim, em (9:15), apresenta um argumento convincente para o uso de uma biblioteca de classes, dizendo que, se quisermos evitar repetições, o código teria que ser copiado e colado no novo projeto WPF. Portanto, para manter o código DRY (Don't Repeat Yourself), precisamos criar classes em uma biblioteca de classes.
Passos e exemplo:
-
Criando a Biblioteca de Classes:
-
Tim às (9:47), cria um novo projeto de biblioteca de classes no .NET Framework, nomeando-o DRYDemoLibrary.
-
Dentro desta biblioteca, ele define uma classe pública
EmployeeProcessore move o métodoGenerateEmployeeIDpara essa classe:public class EmployeeProcessor { public string GenerateEmployeeID(string firstName, string lastName) { string employeeID = firstName.Substring(0, 4) + lastName.Substring(0, 4) + DateTime.Now.Millisecond.ToString(); return employeeID; } }public class EmployeeProcessor { public string GenerateEmployeeID(string firstName, string lastName) { string employeeID = firstName.Substring(0, 4) + lastName.Substring(0, 4) + DateTime.Now.Millisecond.ToString(); return employeeID; } }
-
-
Utilizando a Biblioteca de Classes em Projetos:
-
Em seus projetos WinForms (13:18) e WPF (14:00), Tim adiciona uma referência à biblioteca de classes DRYDemoLibrary.
-
Ele então substitui o código antigo por chamadas ao método
GenerateEmployeeIDda biblioteca de classes:EmployeeProcessor processor = new EmployeeProcessor(); employeeIDText.Text = processor.GenerateEmployeeID(firstNameText.Text, lastNameText.Text);EmployeeProcessor processor = new EmployeeProcessor(); employeeIDText.Text = processor.GenerateEmployeeID(firstNameText.Text, lastNameText.Text); - Essa abordagem elimina a redundância, pois o método agora é mantido em um único local. Tim demonstra que a mesma biblioteca de classes pode ser usada em diferentes frameworks de interface do usuário (WinForms e WPF) sem repetir o código.
-
-
Vantagens:
-
Consistência: Ao centralizar a lógica em uma biblioteca de classes, Tim garante que as alterações na lógica (por exemplo, correções de bugs) sejam aplicadas uniformemente em todos os projetos.
- Manutenção reduzida: as alterações no método precisam ser feitas apenas na biblioteca de classes, evitando inconsistências e reduzindo a sobrecarga de manutenção.
-
Integrando a Biblioteca de Classes em Múltiplos Projetos
Tim Corey continua a explorar como usar a biblioteca de classes DRYDemoLibrary em diferentes tipos de projetos, com foco específico na integração da biblioteca em um novo aplicativo de console. Isso demonstra como a funcionalidade da biblioteca pode ser reutilizada em diversas aplicações, não apenas em uma única instância ou somente naquelas dentro da mesma solução.
Passos e exemplo:
-
Criação de uma nova solução e projeto:
- Tim em (17:29), começa criando uma nova solução para um aplicativo de console, simulando um cenário onde você pode precisar usar a DRYDemoLibrary em um tipo diferente de projeto, como um serviço do Windows ou um aplicativo de console.
Ele batiza o novo projeto de ConsoleUI e mostra como configurar um aplicativo de console básico.
class Program { static void Main(string[] args) { Console.ReadLine(); } }class Program { static void Main(string[] args) { Console.ReadLine(); } } -
Adicionando uma referência à biblioteca de classes:
-
Tim explica como adicionar uma referência à DLL DRYDemoLibrary no novo projeto. Isso envolve navegar até o arquivo DLL na pasta bin do projeto da biblioteca de classes e adicioná-lo ao aplicativo de console.
using DRYDemoLibrary;using DRYDemoLibrary; -
Depois que a referência é adicionada, Tim (19:24) usa a classe EmployeeProcessor da biblioteca para gerar um ID de funcionário com base na entrada do usuário.
Console.WriteLine("What is your first name?"); string firstName = Console.ReadLine(); Console.WriteLine("What is your last name?"); string lastName = Console.ReadLine(); EmployeeProcessor processor = new EmployeeProcessor(); string employeeID = processor.GenerateEmployeeID(firstName, lastName); Console.WriteLine($"Your employee ID is {employeeID}");Console.WriteLine("What is your first name?"); string firstName = Console.ReadLine(); Console.WriteLine("What is your last name?"); string lastName = Console.ReadLine(); EmployeeProcessor processor = new EmployeeProcessor(); string employeeID = processor.GenerateEmployeeID(firstName, lastName); Console.WriteLine($"Your employee ID is {employeeID}");
-
-
Executando o aplicativo de console:
-
Tim demonstra a execução do aplicativo de console para mostrar que ele gera com sucesso o ID do funcionário usando a biblioteca. Isso confirma que o mesmo código da biblioteca de classes pode ser reutilizado em diferentes projetos.

-
-
Atualizando a DLL:
- Tim menciona brevemente que, se a DLL for alterada, você poderá atualizá-la nos projetos que a referenciam. Ele observa que, embora este vídeo não aborde o assunto em detalhes, o uso de pacotes NuGet é uma abordagem recomendada para gerenciar e atualizar DLLs em vários projetos.
Atualizando DLLs e gerenciando pacotes NuGet
Tim Corey apresenta brevemente o conceito de usar pacotes NuGet para gerenciar e atualizar bibliotecas de classes. Essa abordagem oferece uma solução mais escalável para lidar com dependências e atualizações, especialmente em projetos ou organizações de maior porte.
Pontos principais:
-
Criando um pacote NuGet :
Em vez de gerenciar manualmente os arquivos DLL, Tim sugere criar um pacote NuGet para a biblioteca de classes. Isso envolve empacotar a DLL em um pacote NuGet e carregá-lo em um servidor NuGet (privado ou público).
-
Atualizando pacotes:
- Ao usar um pacote NuGet , você pode atualizar a biblioteca em todos os projetos que a referenciam simplesmente atualizando a versão do pacote. Isso garante consistência e reduz o risco de incompatibilidades de versão ou falta de atualizações.
-
Benefícios:
-
Gerenciamento centralizado: os pacotes NuGet oferecem uma maneira centralizada de gerenciar versões e dependências de bibliotecas.
-
Facilidade de atualizações: Atualizar a biblioteca em vários projetos torna-se mais fácil e confiável.
- Integração: O NuGet integra-se com diversas ferramentas e ambientes de desenvolvimento, simplificando o processo de gerenciamento de dependências de bibliotecas.
-
Implementando o princípio DRY em testes unitários: um curso intensivo
Neste segmento, Tim Corey demonstra como a aplicação do princípio DRY (Don't Repeat Yourself - Não se Repita) pode aprimorar os testes unitários. Ele demonstra como implementar os princípios DRY (Don't Repeat Yourself) no desenvolvimento, com foco especial em testes unitários.
Configuração inicial de teste
Tim começa executando um teste unitário que atualmente falha devido a um bug na DLL. Ele destaca a importância dos testes unitários na identificação de problemas, mesmo quando o código está fora da solução principal. O código esperava uma entrada de 4 letras, mas em vez disso, Tim passou um nome de 3 letras, o que causa uma falha no arquivo DLL, mesmo que ele não esteja diretamente incluído na solução.

Refatoração de código para corrigir erros
Para resolver o problema com o tratamento de nomes próprios, Tim refatora o código. Ele explica como o princípio DRY pode ser aplicado ao desenvolvimento através da criação de um novo projeto de biblioteca de classes (23:50). Essa abordagem garante que as alterações em vários objetos possam ser feitas uma única vez e testadas com eficácia, sem a necessidade de correções repetidas.

Adicionando testes unitários
Tim introduz uma nova classe de teste chamada EmployeeProcessorTest no projeto da biblioteca de classes e configura testes unitários usando o XUnit. Ele demonstra como criar um método de teste para gerar IDs de funcionários e discute a importância de simular dependências em vez de confiar em valores reais.

Como escrever um método de teste
Tim escreve um método de teste unitário chamado GenerateEmployeeID_ShouldCalculate. Ele estabelece uma teoria com dados embutidos para testar diferentes cenários, garantindo que o método retorne os resultados esperados. Ele também explica como usar o Assert.Equal para verificar a saída.
public class EmployeeProcessorTest
{
[Theory]
[InlineData("Timothy", "Corey", "TimoCore")]
public void GenerateEmployeeID_ShouldCalculate(string firstName, string lastName, string expectedStart)
{
// Arrange
var processor = new EmployeeProcessor();
// Act
var actualStart = processor.GenerateEmployeeID(firstName, lastName).Substring(0, 8);
// Assert
Assert.Equal(expectedStart, actualStart);
}
}
public class EmployeeProcessorTest
{
[Theory]
[InlineData("Timothy", "Corey", "TimoCore")]
public void GenerateEmployeeID_ShouldCalculate(string firstName, string lastName, string expectedStart)
{
// Arrange
var processor = new EmployeeProcessor();
// Act
var actualStart = processor.GenerateEmployeeID(firstName, lastName).Substring(0, 8);
// Assert
Assert.Equal(expectedStart, actualStart);
}
}
Executando o teste unitário
Tim enfatiza a importância de simular dados dinâmicos, como valores de data e hora, para controlar as condições e os resultados dos testes. Ele discute o desafio de trabalhar com strings dinâmicas e como testar diferentes cenários usando valores controlados. Ele então executa o teste de unidade, mas antes disso adiciona dois pacotes NuGet necessários para executar os testes: xunit.runner.console e xunit.runner.visualstudio.

Após a execução bem-sucedida de todos os testes para um dado embutido, a saída é exibida da seguinte forma:

Agora em (31:30), Tim adicionou outro dado inline e mudou o segundo parâmetro da substring para expectedStart.Length:
public class EmployeeProcessorTest
{
[Theory]
[InlineData("Timothy", "Corey", "TimoCore")]
[InlineData("Tim", "Corey", "TimCore")]
public void GenerateEmployeeID_ShouldCalculate(string firstName, string lastName, string expectedStart)
{
var processor = new EmployeeProcessor();
var actualStart = processor.GenerateEmployeeID(firstName, lastName).Substring(0, expectedStart.Length);
Assert.Equal(expectedStart, actualStart);
}
}
public class EmployeeProcessorTest
{
[Theory]
[InlineData("Timothy", "Corey", "TimoCore")]
[InlineData("Tim", "Corey", "TimCore")]
public void GenerateEmployeeID_ShouldCalculate(string firstName, string lastName, string expectedStart)
{
var processor = new EmployeeProcessor();
var actualStart = processor.GenerateEmployeeID(firstName, lastName).Substring(0, expectedStart.Length);
Assert.Equal(expectedStart, actualStart);
}
}
Após executar o teste unitário novamente às 32:05, com a segunda teoria, o teste falhou:

Melhorando o código com métodos privados
Para aderir ao DRY, Tim refatora o código ainda mais criando um método privado GetPartOfName na classe EmployeeProcessor real sob a DRYDemoLibrary. Este método lida com a extração de partes de um nome, melhorando a reutilização e a legibilidade do código. Tim fez as seguintes alterações:
public string GenerateEmployeeID(string firstName, string lastName)
{
string employeeID = $@"{GetPartOfName(firstName, 4)}{GetPartOfName(lastName, 4)}{DateTime.Now.Millisecond.ToString()}";
return employeeID;
}
private string GetPartOfName(string name, int numberOfCharacters)
{
string output = name;
if (name.Length > numberOfCharacters)
{
output = name.Substring(0, numberOfCharacters);
}
return output;
}
public string GenerateEmployeeID(string firstName, string lastName)
{
string employeeID = $@"{GetPartOfName(firstName, 4)}{GetPartOfName(lastName, 4)}{DateTime.Now.Millisecond.ToString()}";
return employeeID;
}
private string GetPartOfName(string name, int numberOfCharacters)
{
string output = name;
if (name.Length > numberOfCharacters)
{
output = name.Substring(0, numberOfCharacters);
}
return output;
}
Atualizando testes unitários
Tim atualiza os testes unitários para refletir as mudanças no código, como a modificação do comprimento esperado das substrings. Ele explica como a execução desses testes ajuda a identificar rapidamente problemas e validar se o código atende aos novos requisitos. Tim adiciona novas teorias e, em seguida, executa os testes unitários para verificar se os resultados são os esperados:

Ampliando a versatilidade com as bibliotecas .NET Standard.
Criando uma biblioteca .NET Standard
Para aumentar a versatilidade da sua biblioteca de classes, Tim Corey recomenda a transição de uma biblioteca de classes .NET Framework para uma biblioteca de classes .NET Standard . Essa alteração permite que a biblioteca seja compatível com diversas plataformas, incluindo:
- Plataformas Windows : WinForms, WPF e aplicativos de console
- Multiplataforma : .NET Core, Xamarin (para iOS e Android), Linux e macOS
Passos para criar uma biblioteca .NET Standard :
- Adicionar novo projeto : Clique com o botão direito do mouse na sua solução e escolha adicionar um novo projeto.
-
Selecione .NET Standard : Em vez de selecionar uma biblioteca de classes do .NET Framework , escolha .NET Standard. Este tipo de biblioteca suporta uma ampla variedade de plataformas.

- Migração de código : Copie e cole seu código existente (por exemplo, a classe EmployeeProcessor) na nova biblioteca .NET Standard . Esse processo pode envolver pequenos ajustes, mas a lógica central permanece a mesma.
Ao converter for .NET Standard, você torna sua biblioteca acessível a partir de várias plataformas, reduzindo a repetição de código em diferentes tipos de aplicativos e economizando esforço de desenvolvimento.
Evitando repetição em código e testes
Reduzindo a repetição no desenvolvimento
Tim Corey enfatiza que, ao adotar uma biblioteca .NET Standard , você minimiza a repetição de código não apenas em sua base de código, mas também no processo de desenvolvimento. Em vez de duplicar o código em diferentes projetos específicos de plataforma, você o centraliza em uma única biblioteca que funciona em vários ambientes.
Benefícios:
- Base de código unificada : Uma única base de código para diversas plataformas reduz o esforço necessário para manter e atualizar seu código.
- Testes simplificados : Com uma biblioteca .NET Standard , você pode escrever testes unitários uma única vez e garantir que eles se apliquem a todas as plataformas suportadas.
Testes e depuração: Tim apresenta os testes unitários como uma forma de reduzir ainda mais o esforço e a repetição. Os testes automatizados verificam a correção do seu código sem a necessidade de testar manualmente cada iteração da aplicação.
Dicas para aplicar o produto a seco: Saiba quando parar
Tim Corey enfatiza que, embora seguir o princípio DRY (Don't Repeat Yourself - Não se Repita) seja crucial para escrever código de fácil manutenção, é importante saber quando e onde aplicá-lo. Nem todos os cenários exigem a mesma abordagem, por isso aqui estão algumas dicas práticas inspiradas nas ideias de Tim:
-
Evite colocar código diretamente no code-behind e na interface do usuário : Tim desaconselha colocar lógica diretamente nos arquivos code-behind ou nas interfaces do usuário. Por exemplo, a lógica de negócios não deve ser incorporada em um formulário ou evento de clique de botão. Em vez disso, mantenha essa lógica em classes ou bibliotecas separadas. Essa separação ajuda a manter uma arquitetura limpa e torna seu código mais reutilizável em diferentes interfaces de usuário.
-
Aproveite as bibliotecas .NET Standard : Ao criar bibliotecas, Tim sugere usar bibliotecas .NET Standard em vez de bibliotecas .NET Framework sempre que possível. As bibliotecas .NET Standard são mais versáteis, permitindo que seu código seja usado em diferentes plataformas, incluindo .NET Core, Xamarin e muito mais. Essa abordagem reduz a duplicação de código e aumenta a portabilidade do código.
-
Código específico da plataforma separado : Alguns códigos podem não se encaixar em uma biblioteca .NET Standard devido a requisitos específicos da plataforma, como manipulação de arquivos ou gerenciamento de configuração. Tim recomenda a criação de duas bibliotecas nesses casos: uma para código .NET Standard e outra para código específico da plataforma. Dessa forma, você ainda pode reutilizar a lógica principal, ao mesmo tempo que atende às necessidades específicas da plataforma.
-
Dê ênfase aos testes unitários : Tim recomenda fortemente a escrita de testes unitários para o seu código. Os testes unitários ajudam a identificar erros precocemente e garantem que seu código se comporte conforme o esperado. Elas podem acelerar significativamente o processo de depuração, pois você pode verificar rapidamente as alterações sem precisar testar manualmente toda a aplicação.
- Considere o tamanho do projeto : Para projetos muito pequenos ou experimentais, Tim reconhece que o custo adicional de criar bibliotecas separadas e testes unitários extensivos pode não ser necessário. No entanto, para aplicações de produção, começar com uma arquitetura limpa e testes unitários é aconselhável, visto que projetos pequenos geralmente crescem e evoluem com o tempo.
Seguindo essas dicas, você pode aplicar o princípio DRY (Don't Repeat Yourself) de forma eficaz, equilibrando a necessidade de reutilização e manutenção do código com considerações práticas.
Conclusão
Dominar o princípio DRY (Don't Repeat Yourself) por meio de padrões de projeto é essencial para escrever código C# limpo e de fácil manutenção. Conforme demonstrado por Tim Corey, a aplicação eficaz do princípio DRY envolve a criação de métodos reutilizáveis, o aproveitamento de bibliotecas de classes e a adoção do .NET Standard para uma compatibilidade mais ampla. Ao entender quando e como aplicar essas práticas, você pode melhorar significativamente a qualidade e a flexibilidade do seu código.
Para obter informações mais detalhadas, confira o vídeo de Tim Corey sobre este tópico aqui . Para ficar por dentro das últimas novidades do Tim, visite o canal dele no YouTube .


