Ir para o conteúdo do rodapé
Iron Academy Logo
Aprenda C#
Aprenda C#

Outras categorias

Principais padrões de projeto em C#

Tim Corey
35m 50s

Os padrões de projeto são soluções reutilizáveis ​​para problemas comuns de desenvolvimento de software, fornecendo modelos para estruturar e implementar código orientado a objetos de forma mais eficiente e fácil de manter. Elas ajudam os desenvolvedores a resolver problemas relacionados à criação, estrutura e comunicação de objetos de forma flexível e escalável. Os padrões de projeto servem como conceitos de boas práticas que orientam os desenvolvedores na escrita de códigos melhores. Um dos princípios fundamentais no design de software é o Princípio da Responsabilidade Única (SRP, na sigla em inglês), que faz parte dos princípios SOLID.

Em seu vídeo " Design Patterns: Single Responsibility Principle Explained Practically in C# (The S in SOLID) ", Tim Corey explora o Princípio da Responsabilidade Única (SRP) , destacando sua importância no design de software e fornecendo insights práticos sobre como implementá-lo de forma eficaz. Este artigo oferece uma visão geral concisa dos principais pontos abordados em seu vídeo, enfatizando a importância do Princípio da Responsabilidade Única (SRP) na criação de código limpo e de fácil manutenção.

Introdução ao SRP

No design de software, os princípios SOLID são cruciais para a criação de código escalável e de fácil manutenção. Eles garantem que o código seja fácil de entender, testar e modificar. Os cinco princípios — Princípio da Responsabilidade Única (SRP), Princípio Aberto/Fechado (OCP), Princípio da Substituição de Liskov (LSP), Princípio da Segregação de Interface (ISP) e Princípio da Inversão de Dependência (DIP) — são parte integrante do design orientado a objetos e podem ser aplicados em padrões de projeto para tornar as soluções mais robustas.

Ao aplicar padrões de projeto em C#, os desenvolvedores podem resolver problemas comuns com mais eficácia. Seja na criação de objetos, na definição de estruturas em árvore ou na garantia da reutilização com instâncias únicas, os padrões de projeto fornecem soluções predefinidas que aprimoram a arquitetura de software. Padrões como o Factory Method, Builder e Singleton fornecem soluções flexíveis e reutilizáveis, enquanto padrões comportamentais e estruturais ajudam a gerenciar a complexidade e a melhorar a comunicação dentro dos sistemas. Ao aprender e utilizar esses padrões, os desenvolvedores podem construir sistemas mais fáceis de manter e expandir.

Tim discute o conceito de SRP (Princípio da Responsabilidade Única), enfatizando que é crucial para os desenvolvedores garantir que seu código esteja em conformidade com as melhores práticas. O Princípio da Responsabilidade Única (SRP) afirma que uma classe deve ter apenas uma responsabilidade ou motivo para mudar. Esse princípio ajuda a manter um código limpo, de fácil manutenção e escalável.

Visão geral do código de demonstração

Tim configura um aplicativo de console simples em C# que solicita o nome e o sobrenome do usuário, valida esses nomes e, em seguida, gera um nome de usuário. A implementação inicial viola o Princípio da Responsabilidade Única (SRP), proporcionando uma excelente oportunidade para demonstrar como refatorar o código para aderir a esse princípio.

Explicação do SRP

Tim explica o SRP destacando as múltiplas responsabilidades dentro da classe inicial:

  1. Interação do usuário : Gerenciamento de mensagens e avisos de boas-vindas.
  2. Captura de dados : Captura do nome e sobrenome do usuário.
  3. Validação : Validação dos nomes de entrada.
  4. Geração de nome de usuário : Gerar um nome de usuário a partir dos nomes inseridos.

Cada uma dessas responsabilidades representa um motivo diferente para a classe mudar, violando o Princípio da Responsabilidade Única (SRP).

Refatoração para aderir ao Princípio da Responsabilidade Única (SRP)

Tim demonstra como refatorar o código para seguir o Princípio da Responsabilidade Única (SRP, na sigla em inglês), extraindo cada responsabilidade para sua própria classe. Essa abordagem garante que cada classe tenha um único motivo para ser alterada, tornando o código mais modular e fácil de manter.

Exemplo prático

Tim fornece um exemplo prático de refatoração:

using System;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Welcome to my application");

        Console.Write("Enter your first name: ");
        string firstName = Console.ReadLine();

        Console.Write("Enter your last name: ");
        string lastName = Console.ReadLine();

        if (string.IsNullOrWhiteSpace(firstName) || string.IsNullOrWhiteSpace(lastName))
        {
            Console.WriteLine("You did not give us valid information!");
            Console.ReadLine();
            return;
        }

        var userName = $"{firstName.Substring(0, 1)}{lastName}".ToLower();
        Console.WriteLine($"Your username is {userName}");

        Console.WriteLine("Press enter to close...");
        Console.ReadLine();
    }
}
using System;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Welcome to my application");

        Console.Write("Enter your first name: ");
        string firstName = Console.ReadLine();

        Console.Write("Enter your last name: ");
        string lastName = Console.ReadLine();

        if (string.IsNullOrWhiteSpace(firstName) || string.IsNullOrWhiteSpace(lastName))
        {
            Console.WriteLine("You did not give us valid information!");
            Console.ReadLine();
            return;
        }

        var userName = $"{firstName.Substring(0, 1)}{lastName}".ToLower();
        Console.WriteLine($"Your username is {userName}");

        Console.WriteLine("Press enter to close...");
        Console.ReadLine();
    }
}

Refatoração passo a passo

Passo 1: Criando a classe StandardMessages

Primeiro, Tim cria uma classe para lidar com as mensagens padrão exibidas ao usuário. Esta classe irá gerenciar mensagens de boas-vindas e mensagens de encerramento.

public class StandardMessages
{
    public static void WelcomeMessage()
    {
        Console.WriteLine("Welcome to my application");
    }

    public static void EndApplication()
    {
        Console.WriteLine("Press enter to close...");
        Console.ReadLine();
    }

    public static void ShowValidationErrorMessage()
    {
        Console.WriteLine("You did not give us valid information!");
    }
}
public class StandardMessages
{
    public static void WelcomeMessage()
    {
        Console.WriteLine("Welcome to my application");
    }

    public static void EndApplication()
    {
        Console.WriteLine("Press enter to close...");
        Console.ReadLine();
    }

    public static void ShowValidationErrorMessage()
    {
        Console.WriteLine("You did not give us valid information!");
    }
}

Na classe Program, substitua as chamadas diretas para Console.WriteLine e Console.ReadLine por chamadas aos métodos da classe StandardMessages:

class Program
{
    static void Main(string[] args)
    {
        StandardMessages.WelcomeMessage();

        // Other code...

        StandardMessages.EndApplication();
    }
}
class Program
{
    static void Main(string[] args)
    {
        StandardMessages.WelcomeMessage();

        // Other code...

        StandardMessages.EndApplication();
    }
}

Etapa 2: Criando a classe PersonDataCapture

Em seguida, Tim cria uma classe para capturar o nome e o sobrenome da pessoa. Esta classe será responsável por coletar a entrada do usuário e retornar um objeto Pessoa.

public class PersonDataCapture
{
    public static Person Capture()
    {
        Person output = new Person();

        Console.Write("Enter your first name: ");
        output.FirstName = Console.ReadLine();

        Console.Write("Enter your last name: ");
        output.LastName = Console.ReadLine();

        return output;
    }
}
public class PersonDataCapture
{
    public static Person Capture()
    {
        Person output = new Person();

        Console.Write("Enter your first name: ");
        output.FirstName = Console.ReadLine();

        Console.Write("Enter your last name: ");
        output.LastName = Console.ReadLine();

        return output;
    }
}

Etapa 3: Criando a classe Pessoa

Você também precisa de uma classe Pessoa para armazenar o nome e o sobrenome do usuário.

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Na classe Program, substitua o tratamento direto da entrada do usuário por uma chamada ao método PersonDataCapture.Capture:

class Program
{
    static void Main(string[] args)
    {
        StandardMessages.WelcomeMessage();

        Person user = PersonDataCapture.Capture();

        // Other code...

        StandardMessages.EndApplication();
    }
}
class Program
{
    static void Main(string[] args)
    {
        StandardMessages.WelcomeMessage();

        Person user = PersonDataCapture.Capture();

        // Other code...

        StandardMessages.EndApplication();
    }
}

Etapa 4: Criando a classe PersonValidator

Em seguida, Tim cria uma classe para lidar com a validação do nome e sobrenome da pessoa. Esta classe será responsável por garantir que os nomes não sejam nulos ou contenham espaços em branco.

public class PersonValidator
{
    public static bool Validate(Person person)
    {
        if (string.IsNullOrWhiteSpace(person.FirstName))
        {
            StandardMessages.ShowValidationErrorMessage("first name");
            return false;
        }

        if (string.IsNullOrWhiteSpace(person.LastName))
        {
            StandardMessages.ShowValidationErrorMessage("last name");
            return false;
        }

        return true;
    }
}
public class PersonValidator
{
    public static bool Validate(Person person)
    {
        if (string.IsNullOrWhiteSpace(person.FirstName))
        {
            StandardMessages.ShowValidationErrorMessage("first name");
            return false;
        }

        if (string.IsNullOrWhiteSpace(person.LastName))
        {
            StandardMessages.ShowValidationErrorMessage("last name");
            return false;
        }

        return true;
    }
}

Na classe Program, substitua o código de validação por uma chamada ao método PersonValidator.Validate:

class Program
{
    static void Main(string[] args)
    {
        StandardMessages.WelcomeMessage();

        Person user = PersonDataCapture.Capture();

        if (!PersonValidator.Validate(user))
        {
            StandardMessages.EndApplication();
            return;
        }

        // Other code...

        StandardMessages.EndApplication();
    }
}
class Program
{
    static void Main(string[] args)
    {
        StandardMessages.WelcomeMessage();

        Person user = PersonDataCapture.Capture();

        if (!PersonValidator.Validate(user))
        {
            StandardMessages.EndApplication();
            return;
        }

        // Other code...

        StandardMessages.EndApplication();
    }
}

Etapa 7: Criando a classe AccountGenerator

Tim move a lógica de geração de nomes de usuário e criação de contas para uma nova classe AccountGenerator.

  1. Criando a classe AccountGenerator :

    • A classe inclui a lógica para gerar o nome de usuário e simular a criação de uma conta.
    public class AccountGenerator
    {
    public static void CreateAccount(Person user)
    {
        string username = $"{user.FirstName.Substring(0, 1)}{user.LastName}".ToLower();
        Console.WriteLine($"Your username is: {username}");
    }
    }
    public class AccountGenerator
    {
    public static void CreateAccount(Person user)
    {
        string username = $"{user.FirstName.Substring(0, 1)}{user.LastName}".ToLower();
        Console.WriteLine($"Your username is: {username}");
    }
    }
  2. Atualizando a classe principal :

    • Chame o AccountGenerator para criar a conta na classe principal.
class Program
{
    static void Main(string[] args)
    {
        StandardMessages.WelcomeMessage();

        Person user = PersonDataCapture.Capture();

        bool isUserValid = PersonValidator.Validate(user);
        if (!isUserValid)
        {
            StandardMessages.EndApplication();
            return;
        }

        AccountGenerator.CreateAccount(user);

        StandardMessages.EndApplication();
    }
}
class Program
{
    static void Main(string[] args)
    {
        StandardMessages.WelcomeMessage();

        Person user = PersonDataCapture.Capture();

        bool isUserValid = PersonValidator.Validate(user);
        if (!isUserValid)
        {
            StandardMessages.EndApplication();
            return;
        }

        AccountGenerator.CreateAccount(user);

        StandardMessages.EndApplication();
    }
}

Csharp Top Design Patterns 1 related to Etapa 7: Criando a classe AccountGenerator

Resumo e Conclusões

Nesta seção final, Tim Corey resume os benefícios e a implementação do Princípio da Responsabilidade Única (SRP) por meio do processo de refatoração do código de demonstração. Ele destaca as vantagens de dividir o aplicativo em classes menores e mais focadas.

Principais benefícios do SRP

  1. Manutenção de código simplificada :

    • Cada classe tem uma única responsabilidade, o que facilita a localização de onde as alterações precisam ser feitas. Por exemplo, a lógica de captura de dados do usuário está claramente localizada em PersonDataCapture.

    • Essa estrutura simplifica a compreensão, pois qualquer pessoa que queira modificar a validação do usuário sabe que deve verificar o PersonValidator.
  2. Legibilidade aprimorada :

    • Com responsabilidades claramente definidas, o fluxo principal do programa torna-se mais legível. O código agora se lê como um conjunto de ações claras e sequenciais:
    StandardMessages.WelcomeMessage();
    Person user = PersonDataCapture.Capture();
    bool isUserValid = PersonValidator.Validate(user);
    if (!isUserValid)
    {
        StandardMessages.EndApplication();
        return;
    }
    AccountGenerator.CreateAccount(user);
    StandardMessages.EndApplication();
    StandardMessages.WelcomeMessage();
    Person user = PersonDataCapture.Capture();
    bool isUserValid = PersonValidator.Validate(user);
    if (!isUserValid)
    {
        StandardMessages.EndApplication();
        return;
    }
    AccountGenerator.CreateAccount(user);
    StandardMessages.EndApplication();
  3. Complexidade reduzida :

    • Classes pequenas com responsabilidades específicas tendem a ter menos linhas de código, tornando-as mais fáceis de entender e manter.

    • Exemplo: Os métodos da classe StandardMessages são concisos e servem a propósitos únicos, como exibir uma mensagem de boas-vindas ou encerrar o aplicativo.
    public class StandardMessages
    {
    public static void WelcomeMessage()
    {
        Console.WriteLine("Welcome to my application");
    }
    
    public static void EndApplication()
    {
        Console.WriteLine("Press enter to close...");
        Console.ReadLine();
    }
    
    public static void ShowValidationErrorMessage(string fieldName)
    {
        Console.WriteLine($"You did not give us a valid {fieldName}!");
    }
    }
    public class StandardMessages
    {
    public static void WelcomeMessage()
    {
        Console.WriteLine("Welcome to my application");
    }
    
    public static void EndApplication()
    {
        Console.WriteLine("Press enter to close...");
        Console.ReadLine();
    }
    
    public static void ShowValidationErrorMessage(string fieldName)
    {
        Console.WriteLine($"You did not give us a valid {fieldName}!");
    }
    }

    Csharp Top Design Patterns 2 related to Principais benefícios do SRP

  4. Facilidade de alterações no código :

    • Como cada classe tem um único motivo para mudar, modificar o código em resposta a novos requisitos torna-se simples.

    • Exemplo: Se o requisito for alterar a mensagem final, a alteração ocorre exclusivamente no método StandardMessages.EndApplication.
  5. Melhor depuração e colaboração :

    • Com classes menores e bem definidas, a depuração torna-se mais simples, pois você pode identificar facilmente a localização de um problema.

    • Novos desenvolvedores podem se integrar mais rapidamente, compreendendo a estrutura clara e as responsabilidades de cada classe.

Abordando preocupações em diversas classes

Tim aborda uma preocupação comum de que a aplicação do SRP resulta em muitas classes, tornando o projeto complexo:

  1. Navegação e Compreensão :

    Ferramentas como o IntelliSense no Visual Studio facilitam a navegação entre várias classes. Por exemplo, pressionar F12 leva diretamente à definição de métodos ou classes.

    • Ter muitas partes pequenas e gerenciáveis ​​pode facilitar a compreensão de toda a aplicação em comparação com grandes classes monolíticas.
  2. Desempenho e armazenamento :

    • As classes adicionais não impactam significativamente o espaço em disco ou o desempenho, considerando os recursos modernos de armazenamento e computação.
  3. Equilíbrio e Excesso :

    Tim aconselha encontrar um equilíbrio. Se a responsabilidade de uma classe a faz crescer demais, considere se existem múltiplos motivos para essa mudança, o que indicaria a necessidade de uma divisão adicional.

    Ele sugere que, se você precisar percorrer uma classe extensivamente no Visual Studio, ela pode ser muito grande e precisar ser dividida.

Implementação prática

Tim incentiva os desenvolvedores a aplicarem o SRP gradualmente, especialmente em bases de código existentes. Comece com pequenas alterações e novo código para se alinhar aos princípios do SRP (Responsabilidade Única). Essa abordagem incremental garante transições mais suaves e melhoria contínua.

Conclusão

O exemplo de refatoração de Tim Corey demonstra como a adesão ao Princípio da Responsabilidade Única (SRP) resulta em um código mais limpo e de mais fácil manutenção. Ao dividir as responsabilidades em classes menores e mais focadas, os desenvolvedores podem melhorar a legibilidade, a depuração e a colaboração em suas bases de código. Este princípio fundamental dos padrões de projeto SOLID abre caminho para princípios mais avançados e melhores práticas no desenvolvimento de software.

Para obter informações mais detalhadas e exemplos de código, assista ao vídeo dele e visite o canal para mais vídeos sobre padrões de projeto.

Hero Worlddot related to Principais padrões de projeto em C#
Hero Affiliate related to Principais padrões de projeto em C#

Ganhe mais compartilhando o que você ama.

Você cria conteúdo para desenvolvedores que trabalham com .NET, C#, Java, Python ou Node.js? Transforme sua expertise em renda extra!

Equipe de suporte de ferro

Estamos online 24 horas por dia, 5 dias por semana.
Bater papo
E-mail
Liga para mim