Menu

Object Calisthenics: Regras pra um código melhor

11 de maio de 2016 - carreira, clean code
Object Calisthenics: Regras pra um código melhor

Acredito que tudo que possa nos aperfeiçoar deve ser compartilhado. Por isso estou publicando a tradução para o português que fiz de um artigo muito interessante, do William Durand ( 3 June 2013 — Clermont-Fd Area, France ),  denominado Object Calisthenics. Bem, vamos lá.

 

Object Calisthenics

No mês passado eu estive conversando sobre Object Calisthenics em Clermont’ech’s APIHour #2, que é um grupo para desenvolvedores com base em Clermont-Ferrand (França).

Eu descobri sobre Object Calisthenics há quase dois anos atrás, mas na época eu não tinha a mente aberta o suficiente pra dar uma chance a isso. Então há um ano atrás, Guilherme Blanco e eu estávamos bebendo algumas cervejas em Paris. Ele me contou que Rafael Dohms e ele portaram o conceito de Object Calisthenics para PHP. Isso foi incrível, e eu decidi aprender, entender, e tentar usar estas regras. Agora eu estou convencido que essas regras realmente são úteis, e que se você tentar respeitá-las, elas te ajudarão a escrever um melhor código.

Cal • is • then • ics – /ˌkaləsˈTHeniks/

Object Calisthenics são exercícios de programação, formalizado como um conjunto de 9 regras inventadas por Jeff Ray no livro The ThoughWorks Anthology. A palavra Objeto (Object) está relacionada com Programação Orientada a Objeto (POO). A palavra Calisthenics é derivada do grego, e significa exercício (exercises) no contexto da ginástica. Ao tentar seguir essas regras, você irá naturalmente mudar como escreve seu código. Isso não significa que você deve seguir todas elas, o tempo todo. Procure o melhor equilíbrio pra você, use algumas delas somente se você ficar confortável com elas.

Estas regras tem foco na manutenibilidade, clareza, testabilidade e compressibilidade do seu código. Se você já escreve códigos sustentáveis, limpos, testáveis e compreensíveis, então estas regras o ajudarão a escrever códigos ainda mais sustentáveis, limpos, testáveis e compreensíveis.

Na sequência eu irei revisar as 9 regras listadas abaixo:

  1. Somente um nível de indentação por método
  2. Não use o ELSE
  3. Encapsule todos os tipos primitivos e Strings
  4. First Class Collections
  5. Um ponto por linha
  6. Não abrevie
  7. Mantenha todas as entidades pequenas
  8. Nenhuma classe com mais de duas variáveis de instância.
  9. Sem getters / setters / propriedades

 

1. Somente um nível de indentação por método

Ao ter muitos níveis de indentação seu código muitas vezes fica ruim para ler ou para dar manutenção. Na maioria das vezes, você não poderá entender mentalmente e de forma fácil o código, sem compilá-lo, especialmente se você tiver várias condições em níveis diferentes de indentação, ou um loop dentro de outro loop, como no exemplo abaixo:

A fim de seguir esta regras, você deve dividir seu método. Martin Fowler, no livro Refactoring, introduz o padrão Extract Method, que é exatamente o que você deveria fazer aqui.

Você não irá reduzir o número de linhas do código, mas irá aumentar a legibilidade dele de uma maneira significativa:

2. Não use o ELSE

O else é bem conhecido de todo programador, muito usado na construção if/else e está presente em praticamente todas as linguagens de programação. Você se lembra da última vez que viu condições aninhada? Você gosta de ler um código assim? Eu acho que não, e isso é exatamente o porque deve ser evitado. Como é mais fácil adicionar um novo If do que refatorar o código (n.t.: “mas é só colocar mais um if”. Quem nunca ouviu né?), você muitas vezes acaba ficando com um código realmente ruim.

Uma maneira fácil de remover o else é colocar uma condição de retorno “precoce”.

A condição pode ser otimista, significando que você tem condições que tratam os cenários de erro quando necessário, do contrário, seu método segue o cenário padrão, ou você pode adotar uma abordagem defensiva (algo relacionado a Defensive Programming), significando que coloca seu cenário padrão em uma condição, que se não for satisfeita, então retornar um erro. Isso é o melhor para prevenir potenciais problemas que você pode não ter pensando.

Como uma alternativa, você pode introduzir uma variável a fim de fazer um retorno parametrizável. Apesar de nem sempre isso ser possível.

Alem disso, é importante mencionar que a Programação Orientada a Objetos nos da recursos poderosos, como polimorfismo. Por último mas não menos importante, os padrões Null Object, State e Strategy podem ser de grande ajuda.

Por exemplo, ao invés de usar if/else para determinar uma ação baseada em status (Ex. running, waiting, etc.) prefira usar o padrão State, uma vez que ele é usado para encapsular o comportamento variável para uma mesma rotina, tendo como base o estado de um objeto.

 

3. Encapsule todos os tipos primitivos e strings

Seguir esta regra é muito fácil, você simplesmente deve encapsular todos os tipos primitivos dentro de objetos, a fim de evitar o anti-pattern Primitive Obsession.

Se uma variável do seu tipo primitivo possui um comportamento, você deve encapsular ela. E isso é especialmente verdade ao trabalhar com DDD – Domain Drive Design. DDD fala sobre Objetos de Valor, como “Money” ou “Hour” por exemplo.

4. First-Class Collections*

Qualquer classe que contenha uma coleção não deverá conter nenhuma outra variável de instância. Se você precisa de um conjunto de elementos e quer manipulá-los, crie uma classe que seja dedicada somente a esse conjunto.

Cada coleção é encapsulada na sua própria classe, assim os comportamentos relacionados ao conjunto ficarão centralizados. Ex: Método de filtro, aplicação de regras a cada elemento da coleção, etc).

*n.t.: falta de um termo em português para representar o título deste tópico. A documentação da microsoft (https://blogs.msdn.microsoft.com/cellfish/2009/08/23/object-calisthenics-rule-8-use-first-class-collections/) acrescenta que: evita duplicação de código e orienta a responsabilidade única.

5. Um ponto por linha

O ponto a que me refiro é o utilizado para invocar métodos em java, ou C# por exemplo. Ele seria uma seta em PHP, bem, mas quem usa PHP né? 😀

Basicamente, a regra diz que você não deve encadear chamadas. No entanto isso não se aplica para Fluent Interfaces e mais genericamente para qualquer implementação do padrão Method Chaining (Ex: Query Builder).

Para as outras classes, você deveria respeitar esta regra. Isso é o uso direto do Law of Demeter, quando ele diz converse somente com amigos (n.t.:don’t talk to strangers.)

Veja estes casos:

Está “meio-certo” ter atributos públicos em Piece e Location. Na realidade, ter uma propriedade pública ou privada com setter/getter é a mesma coisa da regra 9.

No entanto, o método boardRepresentation() está horrível, dê uma olhada nessa linha:

Ele acessa a Location, e então acessa a classe Piece e só depois a representação string de Piece, que é quem vai faz a ação. Isto está longe de um ponto por linha.

Felizmente, a Law of Demeter nos diz para conversar com amigos, então vamos fazer isso:

Fazendo com que a instância de Piece seja privada, garante-se que você não irá tentar fazer algo ruim com o código. Contudo, conforme você precise fazer uma ação nesse atributo, você precisará de um novo método AddTo(). Não é de responsabilidade do Location determinar como Piece irá ser adicionado.

Então novamente você deveria mudar a visibilidade do seu novo atributo. Como um lembrete, o principio Open/Closed diz que entidades (classes, modulos, funções, etc) devem ser abertos para extensão e fechados para modificação.

Além disso, extrair o código que obtém o primeiro carácter de representation “representation.substring(0, 1)” em um novo método parece uma boa ideia, pois poderá ser reutilizado em algum ponto. Finalmente, aqui está a classe Board atualizada:

Muito melhor, certo?

6. Não abrevie

A pergunta certa é: porque você quer abreviar?

Talvez você diga que é porque quer vai escrever o nome muitas vezes ao longo do código? E eu vou responder que se este método vai ser usado tantas vezes, então parece que temos duplicação de código.

Então você diria que o nome do método é muito longo. E eu lhe diria que talvez seja porque sua classe tem muitas responsabilidades, o que é ruim e viola o Princípio da Responsabilidade Única.

Eu costumo dizer que se você não consegue encontrar um nome decente para uma classe ou método, algo provavelmente está errado. Essa é uma regra que eu uso para desenvolver um software por nomeação de coisas.

Não abrevie.

7. Mantenha todas as entidades pequenas

Nenhuma classe deverá ter mais do que 50 linhas e nenhum pacote mais que 10 arquivos. Bem, isso depende de você, mas eu penso que você pode mudar o numero de linhas de 50 para 150.

A ideia por traz dessa regra é que arquivos longos são difíceis de ler, e difíceis de dar manutenção.

8. Nenhuma classe com mais de duas variáveis de instância.

Eu pensei que as pessoas iriam gritar comigo quando falei dessa regra, mas isso não aconteceu. Essa regra provavelmente é a mais difícil, mas ela promove alta coesão e um melhor encapsulamento.

Como uma imagem vale mais que mil palavras, aqui tem a explicação da ideia na forma de desenho. Note que essa regra se basei na regra 3 – Encapsule todos os tipos primitivos e Strings.

A pergunta principal foi “Por que dois atributos?”. E minha resposta foi “Por que não”? Não é a melhor explicação mas, na minha opinião, a ideia principal é para distinguir dois tipos de classes, aquelas que mantêm o estado de uma única variável de instância, e aquelas que coordenam duas variáveis separadas. Dois é uma escolha arbitrária que força você desacoplar muito as suas classes.

8. Sem getters / setters / propriedades

Essa é minha regra favorita. Eu poderia reescreve-la para Tell, don’t ask.

Não há problema em usar o get para obter o estado de um objeto, desde que você não use o resultado para fazer decisões fora do objeto. Toda decisão baseada inteiramente no estado do objeto deve ser feita dentro do próprio objeto.

Isto é porque getters e setters são comumente considerados perigosos. Então novamente, eles violam o princípio Open/Closed.

Vamos ver um exemplo:

No código acima, o método getScore() é usado para tomar uma decisão, você escolhe como o score é aumentado, ao invés de deixar essa responsabilidade para a instância game.

Uma solução melhor seria remover os getters/setters, e prover métodos que fazem isso. Lembre-se, você deve dizer a classe o que fazer. E não perguntar a ela. A seguir, você diz a classe game como atualizar seu score quando destruir inimigos ENEMY_DESTROYED_SCORE .

É responsabilidade da classe game determinar como atualizar o score.

Nessa caso, você poderia manter o método getSscore(), pois você pode querer exibi-lo em algum lugar na interface do usuário, mas tenha em mente que setters não devem ser permitidos.

 

Conclusão

Se você não se sente confortável com estas regras, está tudo bem, mas acredite em mim quando eu digo para você que elas podem ser usadas na vida real. Tente-as utilizar no seu tempo livre então, ao refatorar seus projetos Open Sources por exemplo. Eu digo que é uma questão de prática. Algumas regras são fáceis de seguir, e podem ajudar você.

Slides

 

9 passos para melhor o desenvolvimento de software hoje, by Jeff Bay:

  1. Somente um nível de indentação por método
  2. Não use o ELSE
  3. Encapsule todos os tipos primitivos e Strings
  4. First Class Collections
  5. Um ponto por linha
  6. Não abrevie
  7. Mantenha todas as entidades pequenas
  8. Nenhuma classe com mais de duas variáveis de instância.
  9. Sem getters / setters / propriedades

A propósito, se você encontrou um erro de digitação, por favor entrar em contato. Muito obrigado.

Este post está sob a licença Creative Commons Attribution-ShareAlike 3.0 Unported License

Se você gostou deste artigo, pode visitar o original também, em inglês, de autoria do William Durand.

 

William, thank you for allowing the translation of this excellent article….

 

The following two tabs change content below.

Paulo Rodrigues

Software Architect / Software Engineer at Softplan
Passionate about technology, research and innovation. Interested in software development technologies such as Ruby on Rails, Python, AngularJS, RabbitMQ, microservices, SOA etc. Eager to learn and enthusiastic about topics ranging from computer science to machine learning and artificial intelligence .

Latest posts by Paulo Rodrigues (see all)

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Clef two-factor authentication