Skip to main content

Command Palette

Search for a command to run...

Primitive Obsession

Updated
Primitive Obsession
R

Hi, I'm Rafael, but you can call me Rafa! 😄 I'm a software engineer passionate about everything that involves technology, with more than 5 years of experience in the market. Throughout my career, I worked in banking institutions, developing innovative solutions and contributing to the digital transformation of the financial sector.

With a strong interest in software architecture and testing, I am constantly looking to specialize and improve my skills in this area.

Here, you will find tips, tutorials and discussions on the most varied topics related to architecture, good practices and software testing, always with the aim of sharing knowledge and learning from other developers. I value the exchange of experiences and I am always willing to listen and learn from my professional colleagues.

Feel free to get in contact, leave your comments and share your own experiences and knowledge. Together, we can build a stronger, more collaborative community in the software development world.

É comum ver em nosso dia a dia ao desenvolver software tipos primitivos. Mas muitas vezes usamos string, int, bool entre outros tipos de forma desnecessária. O que vamos aprender neste artigo é exatamente como evitar o uso de tipos primitivos e criar classes especializadas. Esse assunto é muito abordado em livros, como Domain Driven Design e também citado por Martin Fowler.

Principais Tipos de dados primitivos em Csharp

Abaixo segue uma lista dos tipos primitivos:

  • int: Números inteiros - Tamanho 32 bits.
  • long: Números Inteiros (Intervalo Maior) - 64 bits.
  • float: Números de ponto flutuante - 32 bits.
  • double: Número de ponto flutuante de precisão dupla - 64 bits.
  • decimal: Valores Monetários - 128 bits.
  • string: Sequência de caracteres. - 16 bits por Caractere.
  • char: Caractere único - 16 bits.
  • bool: Booleano - 8 bits.

O que é Obsessão por Tipos Primitivos?

Obsessão primitiva pode ser considerado um code smell e um anti-pattern onde o desenvolvedor tenta usar primitivos para modelos de domínio. Isso significa que o desenvolvedor está criando um software sem pensar, e acaba apenas criando classes e atribuindo tipos como string e int sem antes pensar se isso realmente faz sentido. A melhor maneira de entender é com um exemplo:

code1.png

No código acima existem três tipos primitivos: string, int e decimal. Sabemos que um CPF precisa ser validado para que a entidade tenha aquele campo salvo com formato válido e não apenas um texto qualquer seja salvo. Então podemos fazer uma validação nessa classe. Essa validação depende muito da regra de negócio e como o domínio será modelado. O exemplo na imagem é simples e está sendo utilizado para deixar o mais didático possível:

code1.png

Temos uma validação. Mas existe um problema. E se mais tarde a aplicação necessite de uma outra classe que precisa utilizar o Cpf também? Vamos ficar replicando o mesmo método de ValidarCPF() para outra classes? Isso não seria muito inteligente e até mesmo fere as boas práticas de desenvolvimento de software. Então aqui vemos que propriedade (atributo) começa agora a ter responsabilidades únicas, que devem ser passadas para uma classe especializada e que possui suas próprias validações. Então agora temos uma classe CPF:

code.png

Primeiro verificamos e retornamos no método apenas números. Depois, se o tamanho do CPF for maior que 11 dígitos será retornado false e será lançada uma ArgumentException que passamos no construtor da classe. Existem outras validações que poderiam ser aplicadas a classe, mas como objetivo é apenas mostrar como podemos evitar em certas ocasiões o uso excessivo de tipos primitivos, esse exemplo já é o suficiente.

Vale destacar que acabamos de criar um Objeto de Valor (Value Object). Mas o que é um objeto de valor?

São objetos sem identidade conceitual e que dão característica a algum outro objeto (no caso classe Funcionário). Em geral, estamos interessados no que eles fazem e não em quem eles são. Eles agregam valor a entidade principal, que em nosso exemplo é a classe funcionário. Mas o que isso realmente significa? Se revisar o código, antes o atributo CPF era um tipo string. Isso é muito ruim pois assim permitimos que o usuário nos envie qualquer tipo de texto, sendo que na verdade podemos apenas receber números, pois o CPF é constituído apenas de números. Então se não existisse um objeto que agregasse valor para esse atributo na classe funcionário, um CPF inválido poderia facilmente ser persistido na base.

Veja como a classe funcionário foi refatorada:

code4.png

Substituímos o que era um tipo primitivo string , por um tipo CPF que tem suas próprias validações. Assim se outra classe no futuro necessitar de um CPF já temos uma classe especializada para ele. Ou seja agregamos valor a classe funcionário utilizando o objeto CPF.

Mas temos também a propriedade Email. Será que ela se encaixa no mesmo caso do CPF? Sim! Se você permitir que o usuário crie um email qualquer e nem mesmo validar se ele contém a estrutura correta de um email, isso pode causar problemas no futuro. Por isso podemos também criar um Value Object para Email :

code7.png

Veja como está a classe Funcionário após a refatoração:

code5.png

Novamente podemos perceber que temos mais um Value Object, que agrega valor a entidade Funcionário. Podemos ir além e criar um objeto de valor para Name, e atribuir um método que nos retorna name e fullName sem a necessidade de declarar dois tipos primitivos dentro da classe Funcionário.

Benefícios de se evitar a Obsessão Primitiva

  • Evita a duplicação de código ajudando na melhor manutenção do Software, seguindo o princípio de design DRY (Don Don't Repeat Yourself).

  • Auxilia a construir um modelo de domínio adequado com objetos que contém suas próprias validações e apenas tem uma única responsabilidade segundo seu próprio contexto, seguindo o princípio de responsabilidade única (SRP).

  • Software com tipos mais fortes, agora é impossível atribuir erroneamente um CPF ou Email com qualquer tipo de texto, o que resultaria em uma falha grave de persistência de dados.

  • Evitar o uso de Primitive Obsession nos ajuda a seguir qualquer Padrão de Design, incluindo Design Orientado a Domínio (DDD).

  • Outro desenvolvedor ao criar uma nova classe com outras responsabilidades, poderá utilizar esse Value Object, em vez de simplesmente passar tipos primitivos. Assim novamente ele está agregando validações a entidade principal e modelando cada vez mais seu domínio.

Conclusão

Vimos como podemos melhorar cada vez mais o nosso Software e como desenvolvemos. Se quiser saber mais sobre o assunto, recomendo a leitura do livro:

Fico por aqui neste post, agradeço muito por ler até aqui! Até o próximo! 😉

Referência:

Refactoring adaptive model