Clean Code e a assimetria entre objetos e estruturas de dados
Tags: cleancode, datastructure, oop
Published: 2022-10-01 14:24:10

Talvez nós desenvolvedores tenhamos ouvido ou até reproduzido bastante a afirmação de que tudo na programação é objeto. O capítulo 6 do livro Clean Code, escrito pelo Robert C. Martin (também conhecido como _Uncle Bob_, ou tio Bob), explica porque nem sempre precisamos de objetos, às vezes precisamos somente de simples estruturas de dados e qual a diferença entre os dois conceitos.

## Introdução

O capítulo começa questionando o porquê dos desenvolvedores exporem variáveis privadas como se fossem públicas através de métodos acessores (*gets* and *sets*), mesmo quando se quer preservar essas variáveis para que não gere dependência.

## Abstração de Dados

A abstração de dados é super importante para que ocultemos os detalhes de nossos dados. Para isso, não utilizamos meramente métodos de escrita e leitura dos dados, isso faria com que ficassem expostos, nem tão somente trata-se de manipulação de métodos e funções. É necessário um entendimento da melhor maneira de manipular a *essência* dos dados que um objeto contenha, de maneira em que a implementação deste permaneça oculta.

## Antissimetria data/objeto

- Objetos ocultam seus dados através de abstrações e expõem métodos que manipulam esses dados.

- Estruturas de dados expõem seus dados, mas suas funções não são significativas.
    
    Ou seja, basicamente, as definições de objeto e estrutura de dados são quase que complementares.
    
Em resumo:

- Em estruturas de dados, temos facilidade de adição de novas funções sem precisar alterar as estruturas de dados existentes, enquanto no código orientado a objeto, a facilidade está em adicionar novas classes sem alterar os métodos existentes.

- Em estruturas de dados, temos dificuldade em adicionar novas estruturas de dados, pois demandaria a alteração de todas as funções. Enquanto que na programação orientada a objeto, a dificuldade está em adicionar novos métodos, pois demandaria a alteração das classes existentes.

O que é difícil em orientação a objeto é fácil para estruturas de dados e vice-versa. Assim, podemos tratar cada caso de acordo com o que for cabível e adequado.

Em casos em que desejamos adicionar novos tipos de dados sem alterar a função, o ideal seria utilizar a orientação a objeto, enquanto em casos em que desejamos adicionar novas funções mantendo os dados preservados, então estruturas de dados são mais adequadas.

Isso acaba respondendo o que foi questionado no início e desmistificando a afirmação de que em programação tudo é um objeto. Muitas vezes temos e desejamos estruturas de dados simples com funções operando sobre elas.

## Lei de Demeter

A lei de Demeter é uma heurística (heurística: _método que utiliza a pesquisa e prática para descobrir novos conceitos e provar fatos em busca de resposta para questões complexas_) que afirma que um módulo não deve enxergar o interior dos objetos que ele manipula. Objetos abstraem os dados e expõem as operações sobre eles, ou seja, devem ocultar sua estrutura externa e não expô-la através de métodos acessores.

Mais precisamente, se temos uma função f em uma classe C, este só deve chamar métodos de:

- C
- Um objeto criado por f
- Um objeto passado por parâmetro para f
- Um objeto dentro de uma variável de instância C

## Train Wrecks

Quando temos um código com métodos acoplados, como por exemplo:

const cep = ctx.getPessoa().getEndereco().getCep()

Chamamos de *Train Wreck*, pois a estrutura se comporta como um conjunto de vagões acoplados, se assemelhando a um trem. Esse tipo de código é considerado descuidado e inapropriado. O ideal seria escrevermos dessa maneira:

const pessoa = ctx.getPessoa();

const endereco = pessoa.getEndereco();

const cep = endereco.getCep();

A função getCep() possui acesso a muita informação em níveis aos quais não deveria ter acesso, certamente tem conhecimento de que o objeto _ctx_ possui _pessoas_, que por sua vez possuem objetos do tipo _endereco_ e, por consequência, possuem objetos do tipo _cep_.

Se isso é uma violação da lei de Demeter, dependerá se *ctx*, *pessoa*, *endereco* e *cep* são simples estruturas de dados ou objetos. Caso sejam objetos, não deveriam estar expondo sua estrutura interna, esta deveria estar oculta. Portanto, a informação sobre o seu interior viola a heurística. Por outro lado, caso sejam estruturas de dados simples, sua implementação interna é naturalmente exposta, não violando essa lei.

Esse questionamento acaba sendo menos confuso quando estruturas de dados simplesmente tem variáveis públicas e nenhuma função, enquanto objetos tem apenas variáveis privadas e funções públicas. Porém existem frameworks e padrões (como, por exemplo, o padrão *beans*) que demandam métodos acessores e de alteração, mesmo em estruturas de dados simples.

## Híbridos

Estruturas híbridas são compostas por parte objeto e parte estruturas de dados. Essa configuração é ruim pois acaba expondo variáveis internas, fazendo com que funções externas tenham acesso a essas variáveis como numa estrutura de dados, assim como funções significativas e exposição de métodos acessores. O que dificulta tanto a adição de novas funções como a criação de novas estruturas de dados. São estruturas confusas, que não possuem coerência e devem ser evitadas.

## Estruturas Ocultas

Devemos evitar que estruturas internas sejam expostas dentro de um objeto, ou que este não acabe se sobrecarregando de métodos para tratar informações. Para isso, utilizamos estruturas ocultas, métodos que lidem com a manipulação dessa estrutura sem expô-la ou comprometer a sua abstração.

## Objetos de Transferência de Dados

Os DTO’s, ou objetos de transferência de dados, são basicamente classes que possuem variáveis públicas e nenhuma função. São estruturas úteis, especialmente para se comunicar com banco de dados ou analisar sintaticamente mensagens provenientes de sockets. São úteis para converter dados brutos vindos do banco de dados em objetos no código do aplicativo.

## Active Record

Active Records são formas especiais de DTOs. São estruturas de dados com variáveis públicas, mas tipicamente possuem métodos de navegação como *find* e *save.* Os Active Records são traduções diretas das tabelas de bancos ou outras fontes de dados.

A solução para evitar que essas estruturas sejam tratadas como objetos, criando um híbrido entre uma estrutura de dados e um objeto, é tratar o Record Active como uma estrutura de dados e criar objetos separados que contenham as regras de negócio e ocultem seus dados internos.

## Conclusão

- Objetos expõem ações e ocultam seus dados, facilitando a adição de novos tipos de objetos sem modificar os métodos existentes e dificulta a inclusão de novas atividades em objetos existentes.
- Estruturas de dados expõem dados e não possuem ações significativas, facilitando a adição de novos métodos sem afetar os dados existentes e dificultando a adição de novas estruturas em funções existentes.

Em um sistema, é desejável que haja flexibilidade para adicionar novos tipos de dados, para isso prefere-se o uso de objetos. Em outras ocasiões, queremos flexibilidade para adicionar novas ações, sendo preferível optar pela estrutura de dados e procedimentos. Portanto, cabe a nós desenvolvedores entendermos e decidirmos pela melhor estrutura aplicável no momento.