Clean Code

como tornar nossos códigos legíveis para seres humanos

     Vinícius B. Alonso

vba321@hotmail.com

Quem sou eu

  • Desenvolvedor Web Pleno                                                          
  • Graduado em Sistemas para Internet (UTFPR)                          
  • Cursando Esp. em Engenharia de Software (PUCPR)                
  • Membro do GURU/PR

Contato

Mas afinal, o que é um código limpo ?

Definições

"Clean code is simple and direct. Clean code reads like well-written prose. Clean code never obscures the designer’s intent but rather is full of crisp abstractions and straightforward lines of control."

 

Grady Booch, autor do livro Object Oriented Analysis and Design with Applications

Definições

 

"You know you are working on clean code when each routine you read turns out to be pretty much what you expected. You can call it beautiful code when the code also makes it look like the language was made for the problem."

Ward Cunningham, co-inventor do eXtreme programming

Obra e Autor

Livro Clean Code

Robert C. Martin

"Uncle Bob"

Por que devo me importar com isso ?

Sumário

  • Nomes significativos     
  • Funções bem escritas   
  • Comentários                 
  • Classes

Nomes Significativos

Nomes Significativos

  • Devem ser fáceis de pronunciar               
  • Devem expressar a intenção                     
  • Não utilizar abreviações e siglas, exceto, se todos conhecerem (ex: API)                       
  • As classes devem usar substantivos em seus nomes e verbos em seus métodos

Nomes Significativos

$d      =  10;
$cust_n =  "Juca";
$brt    =  "1990-03-30 18:00:38";

Nomes abreviados e difíceis de pronunciar!

Nomes Significativos

$workedDays    =  10;
$customerName  =  "Juca";
$birthday      =  "1990-03-30 18:00:38";

Nomes Significativos

function calc(x, y) {
  return x / y;
}

O nome da função não expressa sua intenção!

Nomes Significativos

function division(dividend, divider) {
  return dividend / divider;
}

Nomes Significativos

class ProgrammerFL {
  private $h;
  private $v;
    
  public function __construct($h, $v) {
    $this->h = $h;
    $this->v = $v;
  }
    
  public function pay() {
    return $this->h * $this->v;
  }
}

A classe não expressa sua intenção e tem muita abreviação!

Nomes Significativos

class ProgrammerFreeLancerPayment {
  private $hoursWorked;
  private $valuePerHour;
    
  public function __construct($hoursWorked, $valuePerHour)
  {
    $this->hoursWorked = $hoursWorked;
    $this->valuePerHour = $valuePerHour;
  }
    
  public function calculateTotalValueOfWork()
  {
    return $this->hoursWorked * $this->valuePerHour;
  }
}

Nomes Significativos

  • Nomear não é uma tarefa fácil                 
  • Devemos escolher bem os nomes para facilitar o entendimento do código          
  • Lembre-se, outros desenvolvedores vão trabalhar naquela funcionalidade no futuro

Funções bem escritas

Funções bem escritas

  • Pequenas!                                                      
  • Devem fazer apenas uma coisa                
  •  Poucos níveis de indentação (max: 2)                         
  • Não devem ter efeitos colaterais

Funções bem escritas

À seguir um exemplo da fórmula Bhaskara

function quadratic_equation($a,$b,$c) {
 if($a === 0) {
  echo "Para o cálculo de uma equação quadrática, 
  \"a\" não pode ser igual a zero.";
 } else {
  $delta = pow($b,2) - ((4*$a)*$c);
  if($delta === 0) {
   $raiz = -($b)/(2*$a);
   echo "Delta igual a zero. A equação tem 1 raiz.
   <br>Raiz da equação: ".$raiz;
  } elseif($delta > 0) {
   $x1 = (-$b + sqrt($delta))/(2*$a);
   $x2 = (-$b - sqrt($delta))/(2*$a);
   $raiz = array($x1, $x2);
   echo "Delta maior que zero. A equação tem 2 raizes.
   <br>Raizes da equação: " .$raiz[0]. "," .$raiz[1];
  } else {
   echo "Delta menor que zero: equação sem solução.";
  }
 }
}

Fonte: http://codetalkbr.blogspot.com.br/2014/10/formula-de-bhaskara-em-php.html

Funções bem escritas

  • A função anterior é muito grande            
  • Possui muitos níveis de indentação         
  • Muitos fluxos de execução, aumentando a complexidade ciclomática
function calculate_bhaskara($a,$b,$c) {
 if(not_is_zero($a)) {
  $delta  =  calculate_delta($a, $b, $c);
  return calculate_root($a, $b, $delta);
 }
}
function not_is_zero($param) {
  return $param !== 0;
}
function calculate_delta($a, $b, $c) {
  return pow($b,2) - ((4*$a)*$c);
}
function calculate_root($a, $b, $delta) {
  if(is_zero($delta))
    return calculate_simple_root($b, $a);
  if(is_greater_than_zero($delta))
   return calculate_double_root($b, $a, $delta);
 
 return null;
}

Continuação: https://gist.github.com/viniciusalonso/dceaace37d95481bcdbb87f75f283ffa#file-clean_code_refactored_function-php

Funções bem escritas

  • Agora temos várias funções pequenas   
  • Fazendo apenas uma coisa cada              
  • Facilitando a manutenção                          
  • E isolando os bugs

Funções bem escritas

# Primeira opção
if ($delta === 0)
  return -($b)/(2*$a);

# Segunda opção
if(is_zero($a))
  return calculate_simple_root($b, $a);

Use suas funções para tentar formar uma narrativa

Funções bem escritas

function checkPassword($email, $password) {
  $credentials = ['email' => $email,
                'password' => $password];

  $userExists = User::check($credentials);

  if ($userExists) {
    session_start();
  }

  return $userExists;
}

O que há de errado com esse código ?

Funções bem escritas

function checkPassword($email, $password) {
  $credentials = ['email' => $email,
                'password' => $password];

  $userExists = User::check($credentials);

  if ($userExists) {
    session_start();
  }

  return $userExists;
}

Cuidado com funções que fazem mais do que se propõe a fazer!

Comentários

Comentários

  • Em geral se precisam ser usados significa que o código não está bom       
  • Quando se atualiza o código deve-se atualizar o comentário, isso gera retrabalho e quase sempre é esquecido                                                                         
  • Podem ser usados para documentação e APIs públicas                                           

Comentários

O código deve ser auto explicativo

# Não pode ser vazio e nem nulo
if (!is_empty($name) && !is_null($name))

Comentários

function notIsEmptyOrNull($param) {
  return !is_empty($name) && !is_null($name);
}

if (notIsEmptyOrNull($name))

Comentários

Lembre-se, não deixe trechos de código comentados em sua aplicação

O Git nos permite consultar versões antigas do sistema

Classes

Classes

  • Pequenas!                                                     
  • Devem possuir apenas uma responsabilidade                                         
  • Devem ser coesas                                       
  • Os tópicos abordados em funções também servem para os métodos
class UserRepository extends BaseRepository
{
    public function getUsersWithAdresses(){}
    public function countCollaborators(){}
    public function getCollaborators(){}
    public function update($id, $data){}
    public function delete($id){}
    public function paginate($value, $field = '',
                             $order = '', $keyword = null){}
    public function create($data){}
    public function findByAdmin(){}
    public function filterManagers(){}
}

Problema também conhecido como Bad Smell Code God Class

class UserRepository extends BaseRepository
{
    public function getUsersWithAdresses(){}

    public function countCollaborators(){}
    public function getCollaborators(){}

    public function update($id, $data){}
    public function delete($id){}
    public function create($data){}

    public function paginate($value, $field = '',
                             $order = '', $keyword = null){}

    public function findByAdmin(){}
    public function filterManagers(){}
}

Inicialmente, podemos dividir em 5 responsabilidades distintas

1

2

3

4

5

Classes

  • A classe anterior faz coisas demais          
  • Pode ser dividias em pelo menos 5 classes                                                           
  • Para essa refatoração serão utilizadas traits

Classes

trait UserRelationships {
  public function getUsersWithAdresses(){}
}

trait UserCollaborators {
  public function countCollaborators(){}
  public function getCollaborators(){}
}


trait UserCRUDOperations {
  public function update($id, $data){}
  public function delete($id){}
  public function create($data){}
}

Classes

trait UserPagination {
  public function paginate($field = '',
                           $order = '',
                           $keyword = null){}
}


trait UserHighLevel {
  public function findByAdmin(){}
  public function filterManagers(){}
}

Classes

class UserRepository extends BaseRepository {
  use UserRelationships,
      UserCollaborators,
      UserCRUDOperations,
      UserPagination,
      UserHighLevel;
}

Cada trait faz uma coisa e a classe ficou mais simples

Classes

  • Relembrando o que Grady Booch disse "ler como uma boa prosa"                         
  • O uso das classes deve ser próximo de uma narrativa

Classes

$hoursWorked = 30;
$valuePerHour = 50;

$programmerFreeLancer = 
new ProgrammerFreeLancerPayment($hoursWorked, $valuePerHour);

$programmerFreeLancer->calculateTotalValueOfWork();

Cuidado para não deixar a linha muito grande

Classes

# ...

public function buildJson()
{
  $this->getUsersData();
  return $this->build();
}

private function getUsersData() {
  # ...
}

private function build() {
  # ...
}

O métodos públicos devem ser quebrados em métodos privados

Conclusão

  • Escrevemos código para outros entenderem e por isso ele deve ser claro                                                               
  • Utilizando clean code evitamos diversos bad smell codes, como por exemplo, God Classes, Long list parameters, etc.                                           
  • E assim, evitamos que nossos projetos de hoje, serão os legados horríveis de amanhã

Referências

Palestra sobre Clean Code

By Vinícius Alonso

Palestra sobre Clean Code

Palestra apresentada no TDC 2017 em Florianópolis.

  • 1,390