⏩ Introdução

Olá, entusiastas do código! 👋✨

Acho que todos concordamos se dissermos que a Programação Orientada a Objetos (OOP) é bem difícil! 🤔 Depois de todos esses anos, ainda estamos debatendo seus meandros, e este é um feedback muito importante sobre o fato de que é complicado de ser entendido e há algo que não funcionou sobre a disseminação dos conceitos de OOP! 💥

A verdade é que a OOP pode não ser tão impenetrável quanto parece. O verdadeiro desafio está em como isso foi ensinado o tempo todo. 📚😱

Nesta empolgante edição do boletim informativo #LAP, estamos prestes a virar o jogo sobre OOP e lançar luz sobre uma abordagem revolucionária: Object Calisthenics! 🏋️ ♂️✨ Essas regras poderosas seguem a heurística da OOP, levando os desenvolvedores a evitar cheiros de código e chegar a um design melhor. Essa é a chave que abre a porta para uma compreensão verdadeira e profunda da OOP! 🗝️🚀

Object Calisthenics é um conceito fascinante que mostra como a OOP pode se tornar uma jornada legal em vez de um enigma. 🌟🔍

❌ A OOP está cheia de equívocos 🙅

Muitas pessoas veem a OOP da maneira errada, e a culpa não é delas: por alguma razão, na história do desenvolvimento de software, muitos equívocos e conceitos errados sobre abordagens orientadas a objetos se espalharam pelo mundo.

A definição original de Programação Orientada a Objetos é construir um sistema feito de objetos que se comunicam entre si através de mensagens.

Muitos equívocos afastam os desenvolvedores dos conceitos originais - aqui estão alguns deles:

  1. Equiparar OOP com o uso de classes: embora as classes sejam um conceito fundamental na OOP, elas não são o único aspecto. A OPO é um paradigma mais amplo que inclui encapsulamento, herança e polimorfismo, entre outros princípios.

  2. Tratando objetos como meras estruturas de dados: OOP enfatiza o comportamento e as interações dos objetos. Um equívoco comum é tratar objetos como estruturas de dados simples.

  3. Uso excessivo de herança: Alguns desenvolvedores tendem a confiar muito na herança, criando hierarquias de classe profundas que podem se tornar difíceis de manter e entender. A herança representa uma relação “é-a” (“Labrador” é um “Cão”), enquanto a composição representa uma relação “parte de” (“Olhos” é parte de “Cabeça”, que faz parte de “Corpo Humano”).

  4. Ignorando os princípios SOLID: Os princípios SOLID (Responsabilidade Única, Aberto/Fechado, Substituição de Liskov, Segregação de Interface e Inversão de Dependência) fornecem diretrizes para escrever código extensível e de manutenção em OOP. Ignorar esses princípios ou não entender sua importância pode levar a um código difícil de manter, bem acoplado e difícil de testar.

  5. Usando getters, setters e propriedades públicas: em geral, fazer com que os objetos se comuniquem com dados é um grande erro que não respeita os princípios de OOP (se comunica por meio de mensagens, não dados) e leva a um design acoplado.

Existem muito mais equívocos, mas, em geral, acho que a maioria deles é consequência do fato de que muitas pessoas esquecem os conceitos de “se comunica por mensagens” e começam a compartilhar dados entre objetos de maneira errada.

[

](https://substackcdn.com/image/fetch/f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b030c5b-7fa4-4899-974d-38ee5f536d2f_1024x1024.jpeg)

Um desenvolvedor muito legal e bonito devastado pelo fato de que OOP é tão difícil que ele ainda não pode obtê-lo. Estilo realista.

❓ Por que a calistenia de objetos? 🙋♂️

Object Calisthenics foi introduzido por Jeff Bay em The ThoughtWorks Anthology, e consiste em um conjunto de 10 regras a serem respeitadas ao escrever seu código.

As regras da Calistenia de Objetos nasceram com o objetivo de prevenir os cheiros de código. Os cheiros de código são características que você pode reconhecer em uma base de código que indica problemas mais profundos no design, como uma violação dos princípios fundamentais de design. Os termos vêm da ideia de que esses pedaços de código são reconhecíveis, como se fedessem.

Existe uma forte correlação entre os princípios SOLID e a prática da Calistenia de Objetos. O objetivo do Object Calisthenics é evitar que os cheiros de código cheguem a um alvo maior: fornecer uma solução prática passo a passo para aplicar os princípios SOLID, para obter projetos que sejam mais fáceis de entender, manter e estender.

Para isso, as regras são baseadas nas mesmas heurísticas que a OOP nasceu e baseadas em:

  • Diga, não pergunte: diga aos objetos para executar ações em vez de pedir que os dados sejam processados fora dele, isso vem da ideia original da programação orientada a objetos de fazer com que os objetos se comuniquem uns com os outros por meio de mensagens

  • Lei de Deméter: também conhecido como “não fale com estranhos”, cada componente deve falar apenas com seus amigos próximos, a fim de favorecer a simplicidade

O termo calistenia refere-se a uma forma de treinamento de força, o nome vem de duas palavras gregas: “kalòs”, que significa bonito, e “sthènos”, que significa força. As principais características deste esporte são que ele pode ser praticado com nenhum ou muito mínimo equipamento e sua dificuldade pode ser progressivamente aumentada para se adaptar a diferentes níveis de treinamento. No design de software, o paralelismo é entre nosso corpo e o design de nosso código: graças à calistenia, nosso corpo se tornará mais forte e belo; graças ao Object Calisthenics, o design do nosso código se tornará mais forte e bonito.

Object Calisthenics é sobre restringir decisões de design de software: não é sobre o que você pode fazer, mas mais sobre o que você não pode fazer. Você notará nas regras que se seguem e em seus exemplos que essas regras restringem as escolhas sobre design de software, a fim de ajudá-lo a evitar armadilhas e más escolhas de design.

Por que precisamos considerar o design importante?

  • O princípio “Não se repita” (DRY) não é suficiente: os esforços de refatoração não podem ser colocados apenas na remoção da duplicação, precisamos de mais do que isso

  • Ótimas práticas punem você se você não entender o design: TDD, testes de escrita em geral, baseados em CI/Trunk, etc… Todas essas práticas de alto nível se tornam muito difíceis se você não cuidar do design - eles não cuidarão disso para você

Eu acredito fortemente que aprender regras de Calistenia de Objetos é a melhor maneira de começar a aprender OOP.

Vamos descobrir as regras ⬇️

1º) Apenas um nível de recuo

Você deve manter apenas um nível de recuo por método, evitando o ninho de blocos de código. Isso ajuda a garantir que um método se concentre em fazer apenas uma coisa e reduza o tamanho dos métodos, permitindo uma reutilização mais fácil. Essa abordagem favorece a legibilidade e a simplicidade. Para atingir esse objetivo, extraia blocos de código em métodos para dar um nome a essa parte do comportamento.

Exemplo ❌ errado

class WrongExample {
 public function doSomething(int $a) {
  if ($a > 0) {
   if ($a < 10) {
    return true;
   }
  }
  return false;
 }
}

Exemplo ✅ correto

class CorrectExample {
 public function isBetween1And10(int $a) {
  if ($a > 0) {
   return $this-> checkIfLowerThan10($a);
  }
  return false;
 }
 
 public function checkIfLowerThan10(int $a) {
  if ($a < 10) {
   return true;
  }
  return false;
 }
}

2º) Não use a palavra-chave “else”

Evitar a else palavra-chave else promove uma linha de execução principal com casos especiais tratados. Ele sugere polimorfismo para lidar com casos condicionais complexos, tornando o código mais explícito. Podemos usar o padrão de objeto NULL para expressar que um resultado não tem valor ou usar protetores para criar uma saída para casos especiais.

Exemplo ❌ errado

class WrongExample {
 public function doSomething(int $a) {
  if ($a < 0) {
   // do something
  } else {
   // handle special case
  }
 }
}

Exemplo correto (com guarda)

class CorrectExample {
 public function doSomething(int $a) {
  if ($a > 0) {
   // guard to handle special case
   return;
  }
  // do something
 }
}

3º) Encapsular todas as primitivas e cadeia de caracteres

Nenhum argumento de métodos públicos deve ser primitivo, exceto construtores. Além disso, nenhum valor de retorno deve ser primitivo, para métodos públicos. Em vez de primitivos, devemos criar uma classe para descrever o conceito e conter seus comportamentos.

Exemplo ❌ errado

class WrongShop {
 public function buy(int $money, string $productId) {
  // do something
 }
}

Exemplo ✅ correto

class CorrectShop {
 public function buy(Money $money, Product $product) {
  // do something
 }
}

class Money {
 public function convert(Currency $from, Currency $to) { /** ... */ }
 public function __toString() { /** ... for example a specific format like "$ 1.000,00" */ }
}

4º) Coleções de primeira classe

Nenhum argumento de métodos públicos deve ser coleções primitivas (matriz, hash, tabelas, etc.). Precisamos criar uma classe para lidar com essa coleção e o comportamento de passar por seus valores.

Exemplo ❌ errado

class WrongLeague {
 public function newParticipants(array $newParticipantsList) {
  // do something
 }
}

Exemplo ✅ correto

class CorrectLeague {
 public function newParticipants(Participants $newParticipants) {
  // do something
 }
}

// Participants class allow us to create a list of participants validating data and offer behavior of order and go throught all the list

5º) Sem getters/setters/propriedades

Seguimos a ideia original de OOP como uma rede de entidades que colaboram passando mensagens umas para as outras. Não peça dados e depois aja de acordo com eles; em vez disso, diga ao objeto o que você precisa que ele faça por você. Estruturas de dados e objetos têm responsabilidades diferentes.

Exemplo ❌ errado

class WrongUser {
 public Id $id;
 public Email $email;
 public Password $password;
 
 public function setId(Id $newId) { /** ... */ }
 public function setEmail(Email $newEmail) { /** ... */ }
 public function setPassword(Password $newPassword) { /** ... */ }
 public function getId() { return $this->id; }
 public function getEmail() { return $this->email; }
 public function getPassword() { return $this->password; }
}

Exemplo ✅ correto

class User {
 private Id $id;
 private Email $email;
 private Password $password;
 
 // you can only set them in constructor, then we only expose behaviors
 
 public function login() { /** ... */ }
 // ...
}

6º) Um ponto por linha

Evite situações como dog→body()→tail()→wag() com uma cadeia de chamadas, porque isso é estritamente acoplado a aulas muito distantes do chamador. O interlocutor aqui conhece apenas a classe Dog e deve falar apenas com ela, então, por exemplo, poderíamos ter um método para encapsular esse comportamento.dog→expressHappiness()

Exemplo ❌ errado

class WrongDog {
    private WrongDogBody $body;

    public function body(): WrongDogBody { return $this->body; }
}

class WrongDogBody {
    private WrongDogTail $tail;

    public function tail(): WrongDogTail { return $this->tail; }
}

class WrongDogTail { 
    public function wag(): void { /** wag the tail action */ }
}
// used somewhere
$dog = new WrongDog();
$dog->body()->tail()->wag();

Exemplo ✅ correto

class Dog {
    private DogBody $body;
    
    public function expressHappiness(): void { return $this->body->wagTail(); }
}

class DogBody {
    public function wagTail(): void { /** do something */ return; }
}

// used somewhere
$dog = new Dog();
$dog->expressHappiness();

7º) Não abreviar

Sempre deixe os nomes explícitos, mesmo que custe um nome longo: não há necessidade de salvar caracteres. Abreviações só podem levar a mal-entendidos e a um código difícil de ler.

Exemplo ❌ errado

class Calc { 
 public function calcSumTwoNums() { /** */ }
}

Exemplo ✅ correto

class Calculator { 
 public function calculateSumOfTwoNumbers() { /** */ }
}

// sum would be a good name for this method, just made an example to show problems with abbreviations so I didn't care about choosing the best name possible.

8º) Mantenha todas as entidades pequenas

As classes pequenas tendem a ser focadas em fazer apenas uma coisa, melhorando a responsabilidade única e a reutilização e legibilidade desse código. Use pacotes/namespaces para agrupar classes relacionadas. Além disso, as embalagens devem ser pequenas para ter um propósito claro.

9º) Nenhuma classe com mais de 2 variáveis de instância

Quanto mais variáveis de instância, menor a coesão dentro da classe. Classes com mais de um parâmetro geralmente são orquestradores, aqueles com apenas um são atuadores.

Exemplo ❌ errado

class Example { 
 public function __construct(string $firstName, string $lastName, string $email, string $password) { /** ... */ }
}

Exemplo ✅ correto

class Example { 
 public function __construct(User $user) { /** ... */ }
}

10) Todas as turmas devem ter estado

Nenhum método estático deve ser usado, para evitar a criação de classes de utilitário que coletam alguns comportamentos aleatórios juntos. Crie classes com responsabilidade clara e um estado a manter. Isso forçará você a criar uma rede de colaboradores que expõem os comportamentos necessários, mas ocultam seu estado.

Exemplo ❌ errado

class Utilities { 
 public static function log() { /** ... */ }
 public static function translate() { /** ... */ }
}

Exemplo ✅ correto

class Logger { 
 public function log() { /** ... it's state might include the logger technique and some persisted logs ... */ }
}

class Translator { 
 public function translate() { /** ... it's state might include the translations and languages ... */ }
}

Até a próxima, feliz codificação! 🤓👩💻👨💻

Opinião 🙋🏻 ♂️ de Dan

Para mim, Object Calisthenics foi uma verdadeira revelação: imagine lutar por anos para entender OOP, ter a ilusão de que eu estava começando a realmente obtê-lo - então encontrar algo que inverte tudo e faz você entender que tudo o que lhe foi ensinado estava basicamente errado.

Lembro-me de quando comecei a trabalhar como desenvolvedor: eu estava acostumado com PHP processual, e minha primeira empresa pagou por um curso de OOP para nós com coaches da Zend, uma grande empresa no mundo PHP.

Aprender OOP parecia algo realmente difícil e complicado; tudo o que eles falaram foi sobre o que é um Objeto e uma Classe, Encapsulamento, Herança e Princípios SÓLIDOS. A lacuna parecia simplesmente demais para mim, e minha primeira síndrome do impostor surgiu.

Nos anos seguintes, lutei muito para começar a entender um pouco de como o OOP funcionava, e senti muitos equívocos da seção anterior: eu achava que getters e setters eram boas práticas, por exemplo. Eu tinha a sensação de que aprender OOP era muito difícil por algum motivo que eu não conseguia entender, então finalmente conheci alguns desenvolvedores de Backend Sênior que abriram meus olhos.

Em particular, em um livro que me sugeriram (Agile Technical Practices Distilled), encontrei um capítulo dedicado às regras da Calistenia de Objetos - e foi a primeira vez que ouvi falar sobre essas regras; foi também a primeira vez que eu estava realmente entendendo o que é OOP.

Eu realmente acredito que todos devem conhecer as regras da Calistenia de Objetos, e que essas regras são o melhor ponto de partida para aprender OOP.

Object Calisthenics pode parecer nada tão importante à primeira vista, mas acho que é uma ótima maneira de aprender a base da OOP de uma maneira clara e não incompreensível.

Acho que eles são como parar e passar a bola no futebol: é apenas o básico de todas as habilidades técnicas deste esporte, mas eles são um ponto de partida fundamental não apenas para começar a entender como controlar a bola, mas também como o jogo completo funciona - não se trata apenas de controlar a bola, mas também sobre a posição do corpo, Olhe para seus companheiros de equipe, etc.

Não subestime essa abordagem: antes de aprender a correr, você deve aprender a andar, caso contrário, provavelmente fracassará ou, no máximo, investirá muito mais tempo do que o necessário para aprender a correr - e provavelmente fará errado.

🛃 Aplique-os rigorosamente primeiro, depois torne-os naturais. 🌟

A rules estrutura de regras é realmente útil no início: você pode começar a aplicar essas regras cegamente e notará que elas funcionam, mesmo que você ainda não entenda totalmente por que elas funcionam. Em seguida, sua jornada de aprendizado pode começar, e você pode se concentrar em aprender o que são cheiros de código, por que as regras ajudam a evitá-los e como eles o aproximam dos Princípios SOLID.

Você não precisa memorizar todas as regras, cheiros de código e princípios SÓLIDOS e ser capaz de pronunciá-los a qualquer momento. Você tem que entender o núcleo dessas regras e princípios para torná-los naturais - você sempre pode verificar o catálogo de regras e cheiros de código quando precisar.

Em geral, acho que é uma boa ideia evitar aprender esse tipo de coisa memorizando cada detalhe: no início, tente aplicá-los com precisão, depois você tem que se concentrar em aprender o núcleo. Criar um hábito e uma abordagem pessoal para o aprendizado é fundamental para conseguir encarar temas complexos como esse.

❌ Programação Orientada a Objetos é um dos tópicos mais incompreendidos em software. 🏅

É incrível a quantidade de equívocos que existem sobre OOP - livros, artigos, palestras e qualquer outro tipo de conteúdo sobre programação estão todos cheios de exemplos com getters e setters, princípios de responsabilidade única quebrados e muito mais problemas.

É difícil entender como chegamos a esse ponto, mas é fato que algo deu errado e os conceitos originais do OOP se perderam.

OOP é sobre a criação de um sistema de objetos que se comunicam uns com os outros através de mensagens.

Com essa simples afirmação, é fácil ver que há algo errado quando vemos uma classe recebendo dados de outra e fazendo coisas.

Para enfrentar essa questão, o melhor conselho que posso te dar é procurar autoridades na área e ler seus livros e blogs. Kent Beck, Martin Fowler e Robert Martin - partem de seu conteúdo.

Não confie em ORMs, Frameworks, etc: esses são tipicamente muito opinativos - e seu objetivo é ocultar a complexidade, normalmente com muitas compensações no design.

Aprenda Objetos Calistenia e Cheiros de Código: esses dois são a base para aprender Princípios SÓLIDOS, 4 Elementos de Design Simples e OOP.

Lembre-se: se algo não te convencer ou te causar mais problemas do que deveria, mergulhe fundo e descubra o porquê - provavelmente há algo mais para aprender sobre isso, e você se tornará melhor.

Aprofunde-se 🔎

📚 Livros

  • Práticas técnicas ágeis destiladas - Aprender as práticas fundamentais comuns de desenvolvimento de software pode ajudá-lo a se tornar um programador melhor. Este livro usa o termo Agile como um amplo guarda-chuva e abrange princípios e práticas ágeis, bem como a maioria das metodologias associadas a ele. O único livro que conheço que aborda Object Calisthenics!

  • Clean Code: A Handbook of Agile Software Craftsmanship - Até mesmo código ruim pode funcionar. Mas se o código não estiver limpo, ele pode colocar uma organização de desenvolvimento de joelhos. Todos os anos, incontáveis horas e recursos significativos são perdidos por causa de código mal escrito. Mas não precisa ser assim.

  • Refatoração: Melhorando o design do código existente - Martin Fowler fornece um catálogo de refatorações que explica por que você deve refatorar, como reconhecer o código que precisa de refatoração e como realmente fazê-lo com sucesso, independentemente da linguagem que você usa.

  • Growing Object-Oriented Software, Guided by Tests - este livro explora a relação entre TDD e bom design OOP

📩 Edições do boletim informativo

📄 Postagens no blog

🎙️ Vídeos do Youtube

👨🏻 🏫 Cursos online


Artigo Original