O que é Calisthenics?

IGOR RIBEIRO LOBO
Inside PicPay

--

Sempre perguntamos se podemos melhorar nossa forma de programar, seja ela usando alguma tecnologia, algum framework, mas nem sempre pensamos na nossa escrita, como estamos escrevendo, como as pessoas estão vendo nosso código, ele está passando ao leitor o que se espera, ou deixa a pessoa confusa?

Pois bem, hoje vou falar sobre isso, como escrever melhor, para que possamos passar ao leitor do nosso código aquilo que se espera dele. Vamos falar sobre Calisthenics.

O Calisthenics foi publicado no livro The ThoughtWorks Anthology, e aplicado por Jeff Bay.

Ok, mas o que é Calisthenics? como isso pode ajudar a melhorar o meu código? Calma aí pessoal, vamos chegar lá já.

A principal inspiração do Object Calisthenics é o SOLID ,então basicamente falando, Calisthenics é a aplicação de boas práticas e algumas regras para aprimorar a qualidade de código.

Ok, mas o que são essas regras e qual o foco delas?

No Calisthenics existem 9 regras a serem seguidas, onde seu foco está na manutenibilidade, legibilidade, testabilidade e coesão/compreensão de código. Lembrando que não estamos falando de uma receita de bolo que se aplica em tudo, mas seria importante aplicá-las sempre que possível.

Confira as regras:

1 — Um nível de indentação por método

Tente escrever de forma mais simples e objetiva possível, com poucas linhas, onde um dev. no futuro possa ver seu código e consiga compreendê-lo sem perder muito tempo.

  • Sem obedecer o nível de indentação:
public function lerDados(): string
{
$lines = file ('http://www.example.com/');
$conteudo = null;

foreach ($lines as $line_num => $line) {
$conteudo .= "Linha #<b>{$line_num}</b> : " . htmlspecialchars($line) . "<br>\n";
}

return $conteudo;
}
  • Obedecendo o nível de indentação:
public function lerDados(): string
{
$lines = file ('http://www.example.com/');
$conteudo = null;
$conteudo = $this->lerLinhas($lines, $conteudo);
return $conteudo;
}

private function lerLinhas($lines, $conteudo): string
{
foreach ($lines as $line_num => $line) {
$conteudo .= "Linha #<b>{$line_num}</b> : " . htmlspecialchars($line) . "<br>\n";
}

return $conteudo;
}

2 — Não use ELSE

Ao criar uma condicional, evite usar o else, para isso criamos um padrão em tempo de execução, onde o nosso processo será interrompido imediatamente quando a nossa condição “else” for atingida.

  • Sem utilizar o Early Return:
public function login($user, $pass): array
{
if (!empty($user) && !empty($pass)){
// Executa o processo de login
return true;
} else {
return [
"success" => false,
"message" => "Usuário ou senha inválidos!"
];
}
}
  • Utilizando Early Return:
public function login($user, $pass): array
{
if (empty($user) || empty($pass)){
return [
"success" => false,
"message" => "Usuário ou senha inválidos!"
];
}

// Executa o processo de login

return [
"success" => true
];
}

3 — Envolva tipos primitivos

Pensando na regra, temos que envolver todos os tipos primitivos no nosso código, em minha sincera opinião, cabe a nós, que estamos desenvolvendo o código, analisar se realmente naquele caso em questão, é necessário envolver, a pergunta mais básica que eu aprendi durante a minha carreira: "Qual vantagem terei em envolver este tipo primitivo"? Quando eu consigo responder essa pergunta, eu realmente envolvo. Confesso que esse “macete”, aprendi acompanhando Vinicius Dias, e que me trás grandes vantagens no dia a dia.

  • Não envolvendo tipos primitivos
class Pessoa
{
private string $nome;
private string $sobrenome;
private string $cpf;
private string $genero;

public function __construct($nome, $sobrenome, $cpf, $genero)
{
$this->nome = $nome;
$this->sobrenome = $sobrenome;
$this->cpf = $cpf;
$this->genero = $genero;
}

public function getNomeCompleto()
{
return "{$this->nome} {$this->sobrenome}";
}
}
  • Envolvendo tipos primitivos
class Pessoa
{
private string $nome;
private string $sobrenome;
private Cpf $cpf;
private string $genero;

public function __construct($nome, $sobrenome, Cpf $cpf, $genero)
{
$this->nome = $nome;
$this->sobrenome = $sobrenome;
$this->cpf = $cpf;
$this->genero = $genero;
}

public function getNomeCompleto()
{
return "{$this->nome} {$this->sobrenome}";
}
}

class Cpf
{
public string $cpfFormatado;
public string $cpf;

public function __construct($cpf)
{
if (!$this->valido($cpf)){
throw new CPFInvalido($cpf);
}

$this->cpf = $cpf;
$this->formatar($cpf);
}

private function valido($cpf): void
{
// Valida o CPF
}

private function formatar($cpf): void
{
// $this->cpfFormatado = formata o cpf;
}
}

4 — Coleção

Quando você tem uma estrutura de dados, que possui um conjunto de elementos, ou realiza diversas funções em cima de um elemento, você pode criar uma coleção para tornar mais legível o código, vamos ao exemplo:

  • Não utilizando coleção de dados.
class User
{

public function assistirVideo(): void
{
// Código que retorna o video ao usuário.

// Define o video como assistido.
// Adiciona ao contador de visualização.
}


}
  • Utilizando coleção de dados.
class User
{
private WatchedVideos $watchedVideos;
private Map $video;

public function assistirVideo(): void
{
$this->watchedVideos->watchedVideo($this->video);
// Código que retorna o video ao usuário.
}


}

class WatchedVideos
{
public Map $video;

public function __construct($video)
{
$this->video = $video;
$this->count($this->video);
$this->watchedVideo($this->video);
}

public function count($video)
{
// Busca o contador de visualização do video e add 1.
}

public function watchedVideo($video)
{
// Define o video como assistido.
}
}

5 — Apenas “1” operador por linha

A ideia é não encadear métodos, e sim acionar um objeto que execute exatamente a função que precisamos executar, lembrando que algumas linguagens como java por exemplo usa o operador como ponto "." e outras como o php usa a setinha "->".

  • Sem obedecer a regra
class Videos
{
public Map $video;

public function __construct($video)
{
$this->video = $video;
}

public function limitAge(User $user): void
{
$today = new \DateTimeImmutable();
fn(Videos $video) => $video->getLimitAge() <= $user->getDb()->diff($today);
}
  • Obedecendo a regra
class Videos
{
public Map $video;

public function __construct($video)
{
$this->video = $video;
}

public function limitAge(User $user): void
{
fn(Videos $video) => $video->getLimitAge() <= $user->age();
}

}

class User
{
private WatchedVideos $watchedVideos;
private Map $video;

public function assistirVideo(): void
{
// Código que retorna o video ao usuário.
$this->watchedVideos->watchedVideo($this->video);
}

public function age(): int
{
$today = new \DateTimeImmutable();
$dateInterval = $this->db->diff($today);

return $dateInterval->y;
}

6 — Não abrevie nome de variáveis

Quando estiver escrevendo um código, não abrevie os nomes das variáveis, como por exemplo escrever $ln ao invés de $lastName, ter nomes de variáveis bem escritos auxilia bastante na leitura e correto entendimento do código.

A não abreviação de variáveis também está escrita no livro Clean Code: A Handbook of Agile Software Craftsmanship de Robert C. Martin — “Meaningful Names”

7 — Mantenha as entidades pequenas

Toda classe bem escrita, segundo a regra, deve conter até 50 linhas, mas como falei no começo deste artigo, isso não é uma receita de bolo, temos apenas que tentar seguí-las ao máximo possível, eu por exemplo, costumo usar como limitador o máximo de 300 linhas, e acredito que fica bem tranquilo para qualquer dev., compreender o que ela faz. Mas indo ao conceito desta regra é reduzir ao máximo a quantidade de linhas de uma classe.

8 — Não Instanciar duas ou mais propriedades dentro de uma classe

A ideia desta regra é manter o conceito de responsabilidade única do SOLID, garantindo a coesão entre as classes. Uma boa forma de entender essa regra é responder uma pergunta, Essa classe que vou criar irá atender apenas o que ela foi projetada? Se você conseguir responder sim para essa pergunta, e escrever atentamente dentro dessa linha, você conseguirá seguir essa regra.

9 — Não usar Getter/Setter

Essa regra sempre trás discussão, mas é uma regra do Calisthenics, não deixar exposto os dados de uma classe quando utilizamos os getters e setters, podemos trocar os getters e setters deixando as propriedades da nossa classe como pública, claro que pensando em uma propriedade que não tenha uma regra.

Utilizando um exemplo da apresentação PHP para Adultos: Clean Code e Object Calisthenics de Guilherme Blanco, conseguimos entender na prática.

class BankAccount
{
public $balance = 0;

public function deposit($amount)
{
$this->balance += $amount;
}

public function withDraw($amou): void
{
$this->balance -= $amount;
}
}$account = new BankAccount();
$account->deposit(100.00);
//Balance pode ser alterado sem que a classe saiba, causando erros inesperados.
$account-> balance = 0;
$account->withDraw(10.00);
echo $account->balance . PHP_EOL;

Conclusão

Pode ser difícil inicialmente seguir todas as regras, mas o importante do Calisthenics é a prática. Tentar aplicar pelo menos 3 delas e ir testando em códigos pessoais no dia a dia te fará sentir-se mais confiante no final e tornará seu projeto mais robusto.

--

--