Add new comment

6
Jun

Clean Code: objetos não são estruturas de dados!

Uma leitura mais do que recomendada para programadores é o Clean Code [1], escrito pelo célebre Uncle Bob.

Queria falar nesse post sobre um capítulo que muito me foi esclarecedor, o cap 6: "Objects and Data Structures".

Já faz um tempo que venho reclamando do ORM do Django [2] por dificultar a programação OO nas classes criadas para acessar o banco de dados. No Django, a maior dificuldade vem de um acoplamento bem forte dos objetos com os IDs das tabelas. Mas a verdade é que mesmo usando outros ORMs mais flexíveis como o Hibernate ou mesmo com outros mapeadores entre objetos e estruturas de dados, como o JAXB (objetos-XML), trazem algumas dificuldades que nunca me deixavam plenamente satisfeito. A maior cisma era em não colocar as regras de negócio no "bean", forçando um pouco a programação procedural. Exemplo: se eu tenho uma entidade Funcionario, e quero dar um aumento pra ele, o jeito OO seria ter um método funcionario.daAumento(). Mas quando começamos a criar classes para mapear nossos dados (seja do banco de dados ou de um XML), essas classes acabam ficando só com os dados e colocar as regras de negócio nelas acaba ficando estranho.

Explicado o problema e a confusão em minha cabeça, vamos à esclarecedora explicação do Uncle Bob: objetos e estruturas de dados são coisas diferentes. Estruturas de dados são os beans, possuem apenas dados publicamente acessíveis e não possuem comportamento. Já os objetos fornecem comportamento e devem esconder suas estruturas internas. Citando:

Procedural code (code using data structures) makes it easy to add new functions without
changing the existing data structures. OO code, on the other hand, makes it easy to add
new classes without changing existing functions.

Procedural code makes it hard to add new data structures because all the functions must
change. OO code makes it hard to add new functions because all the classes must change.

In any complex system there are going to be times when we want to add new data
types rather than new functions. For these cases objects and OO are most appropriate. On
the other hand, there will also be times when we’ll want to add new functions as opposed
to data types. In that case procedural code and data structures will be more appropriate.

Mature programmers know that the idea that everything is an object is a myth. Some-
times you really do want simple data structures with procedures operating on them.

No entanto, como observa Uncle Bob, o que acabamos por fazer (pelo menos eu já fiz muito) é misturar as duas coisas. Isso porque a vontade é sempre ser OO. E ao criar uma estrutura de dados, eu não tinha a consciência de que "aquilo não é um objeto".

This confusion sometimes leads to unfortunate hybrid structures that are half object and
half data structure. They have functions that do significant things, and they also have either
public variables or public accessors and mutators that, for all intents and purposes, make
the private variables public, tempting other external functions to use those variables the
way a procedural program would use a data structure.

Such hybrids make it hard to add new functions but also make it hard to add new data
structures. They are the worst of both worlds. Avoid creating them. They are indicative of a
muddled design whose authors are unsure of—or worse, ignorant of—whether they need
protection from functions or types.

Agora voltando aos nossos beans, ou models do Django, ou classes anotadas pro JAXB, ou o que você quiser...

The quintessential form of a data structure is a class with public variables and no func-
tions. This is sometimes called a data transfer object, or DTO. DTOs are very useful struc-
tures, especially when communicating with databases or parsing messages from sockets,
and so on. They often become the first in a series of translation stages that convert raw data
in a database into objects in the application code.

Agora uma das partes que achei mais interessante, que fala especificamente sobre o caso do ORM do Django, que creio ser bem parecido com o do Ruby on Rails:

Active Records are special forms of DTOs. They are data structures with public (or bean-
accessed) variables; but they typically have navigational methods like save and find. Typi-
cally these Active Records are direct translations from database tables, or other data
sources.

Unfortunately we often find that developers try to treat these data structures as though
they were objects by putting business rule methods in them. This is awkward because it
creates a hybrid between a data structure and an object.
The solution, of course, is to treat the Active Record as a data structure and to create
separate objects that contain the business rules and that hide their internal data (which are
probably just instances of the Active Record).

Bom, espero que esses trechos possam passar uma boa ideia do problema e das possíveis soluções!
Claro que a leitura completa do capítulo, e mesmo do livro, é altamente recomendada.

Leonardo Leite

[1] http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132...
[2] Um ORM é um framework que automatiza a conversão entre objetos e entidades de um banco de dados relacional.

PS: os trechos em itálico são citações do livro, e portanto não compartilham da licença desse post.

Edição posterior (04/02/16)

Esta conversa também pode ser pensada sob o prisma do princípio da responsabilidade única. Uma classe de negócio, nosso *objeto*, não deve se contaminar com aspectos dos frameworks (ex: Hibernate, JAX-B, etc.) que usamos no nosso sistema. Mas para usarmos coisas como o banco de dados precisamos criar classes (DTOs) que tenha uma dependência desses frameworks, seja com uso de anotações ou com a utilização de getter e setters pra todos os atributos que devem ser trafegados para o banco (ou virarem XML, por ex). Então assim há uma força que sugere que de um lado deve estar nosso objeto puro (OO) e do outro o DTO, implementando assim uma separação entre objetos e estruturas de dados.