C# - Apresentando o anti-padrão : God Object

 Hoje vamos apresentar o anti-pattern e também code smell conhecido como God object.

Na programação orientada a objetos (OOP), o termo God Object ou objeto deus representam um anti-padrão porque violam uma série de bons princípios de design.

O God Object são objetos de classes que possuem muita responsabilidade, têm muito conhecimento e realizam muitas ações.

Longe de ser um bom sinal estas características tornam o seu código fortemente acoplado e difícil de manter.

Por que ?

Porque uma técnica de programação comum é separar um grande problema em vários problemas menores (a estratégia de dividir para conquistar) e criar soluções para cada um deles. Assim, depois que os problemas menores forem resolvidos, o problema maior como um todo estará resolvido.

Portanto, um determinado objeto para um pequeno problema precisa apenas conhecer a si mesmo. Da mesma forma, há apenas um conjunto de problemas que um objeto precisa resolver: seus próprios problemas.

Isso também segue o princípio de responsabilidade única.

E um God object faz exatamente o contrário disso.

Vamos apresentar este anti-padrão e ver como podemos evitá-lo ou nos livrar dele.

No exemplo deste artigo vamos criar uma aplicação Console do tipo .NET Core usando o .NET Core 5.0.

O que é um God Object

Quando você escrever código sem seguir as práticas recomendadas pode levar a um código difícil de depurar, estender e manter ao longo do tempo.

A programação orientada a objetos se tornou extremamente popular nas últimas décadas porque é especialista em ajudá-lo a eliminar códigos mal escritos em seu projeto. No entanto, podem surgir anti-padrões se você tomar atalhos e seu objetivo principal for realizar a tarefa, em vez de fazê-la da maneira certa.

Enquanto um padrão de projeto é uma solução comprovada para um problema de design comum no desenvolvimento de software, um anti-padrão é um padrão ineficaz que nos mostra como não resolver um problema, porque fazer isso resultaria em um design ruim.

Em outras palavras, um anti-padrão é uma solução comum para um determinado problema que pode ter consequências negativas - uma solução que é ineficaz ou contraproducente na prática, e, um God Object é um desses anti-padrão.

Um God object contém código desordenado que é difícil de manter, estender, usar, testar e integrar com outras partes do aplicativo. Eles violam o princípio da responsabilidade única(SRP) e a Lei de Deméter ou princípio do conhecimento mínimo que reduz as dependências entre classes e ajuda a construir componentes que são fracamente acoplados.

O acoplamento é o nível de dependência/conhecimento que pode existir entre os módulos/classes do sistema e existem dois tipos de acoplamento:

1 - Acoplamento forte
2 - Acoplamento fraco

Podemos dizer que:

Um código fortemente acoplado indica que as classes e os objetos são dependentes um do outro e, portanto, reduzem a reutilização do código.

Mas como podemos identificar um God object ?

A seguir temos uma lista com as principais características que ajuda a identificar um God Object:

Problemas com objeto de Deus

Os problemas abaixo são enfrentados quando encontramos um God object :

Exemplo de um God Object

A seguir temos uma classe que é uma forte candidata a gerar um God object.

A classe representa um Candidato em um hipotético sistema para gerenciar entrevistas:

    public class Candidato
    {
        public int Id { get; set; }
        public string Nome { get; set; }
        public string SobreNome { get; set; }
        public string Endereco { get; set; }
        public DateTime DataEntrevista { get; set; }
        public int EntrevistadorId { get; set; }
        public string Entrevistador { get; set; }
        public int EntrevistaId { get; set; }
        public string Entrevista { get; set; }
        public string EntrevistaComentarios { get; set; }
        public decimal SalarioAtual { get; set; }
        public decimal SalarioEsperado { get; set; }
        public string CargoAtual { get; set; }
        public string CargoPretendido { get; set; }

        public double CalculaPontuacao(double curriculo, double experiencia, double empatia)
        {
            return (curriculo + experiencia + empatia)/100;
        }
    }

Abaixo temos o diagrama de classe :

A classe parece querer abrigar o mundo e fazer tudo o que é esta relacionado com uma entrevista de emprego, e, não segue nenhum princípio e o seu código não poderá ser reutilizado. Como não utiliza abstração escrever testes de unidade para esta classe será muito difícil.

A classe Candidato contém diversas propriedades e um método que não deveria fazer parte da classe e por isso esta violando o Princípio de Responsabilidade Única(SRP).

Como podemos resolver este problema ?  Como refatorar uma classe God Object ?

Podemos adotar os seguintes procedimentos para refatorar esta classe corrigindo o problema:

- Tente usar o princípio de "Segregação de Interface" e delegue chamadas para as novas classes extraídas;
- Escreva o teste de unidade para cada método;
- Use classes de modelo separadas para campos ou propriedades;
- Não torne os métodos grandes. Divida em diferentes métodos lógicos;
- Se o módulo for grande, tente usar qualquer padrão de design;

Desta forma as funcionalidades da classe devem ser separadas em outras classes e interfaces diferentes com base em tipos semelhantes de responsabilidades. Isso removerá o problema de acoplamento firme e será de fácil manutenção.

Para o nosso exemplo aplicando esses procedimentos teremos o seguinte resultado:

1- Classe Candidato

    public class Candidato
    {
        public int Id { get; set; }
        public string Nome { get; set; }
        public string SobreNome { get; set; }
        public string Endereco { get; set; }
        public decimal SalarioAtual { get; set; }
        public decimal SalarioEsperado { get; set; }
        public string CargoAtual { get; set; }
        public string CargoPretendido { get; set; }
    }

2- Classe Entrevista

    public class Entrevista
    {
        public int Id { get; set; }
        public DateTime DataEntrevista { get; set; }
        public int EntrevistadorId { get; set; }
        public int CandidatoId { get; set; }
        public string EntrevistaComentarios { get; set; }
        public double CalculaPontuacao(double curriculo, double experiencia, double empatia)
        {
            return (curriculo + experiencia + empatia)/100;
        }     
    }

3- Classe Entrevistador

    public class Entrevistador
    {
        public int EntrevistadorId { get; set; }
        public string Nome { get; set; }
    }

A seguir temos o diagrama de classes gerado :

Aplicando a refatoração criamos 3 classes que estão presentes no gerenciamento de entrevistas. Dessa forma se um candidato comparece a várias rodadas de entrevistas, você pode criar várias instâncias da classe Entrevista. Isso seria suficiente para armazenar informações do candidato para várias rodadas de entrevista.

Os 'God Objects' ou Objetos Deus são comuns em sistemas mal projetados.

A solução é refatorar o código-fonte ou o projeto, ou ambos, e criar tipos (classes, estruturas e interfaces) que são altamente coesos e fracamente acoplados.

E estamos conversados..

"Portanto, irmãos, empenhem-se ainda mais para consolidar o chamado e a eleição de vocês, pois se agirem dessa forma, jamais tropeçarão, e assim vocês estarão ricamente providos quando entrarem no Reino eterno de nosso Senhor e Salvador Jesus Cristo."
2 Pedro 1:10,11


Referências:


José Carlos Macoratti