Refatorando na medida certa
Vinícius Bail Alonso
Quem sou eu
- Desenvolvedor na BairesDev
- Graduado em Sistemas para Internet (UTFPR)
- Mentor pelo Training Center
Mas afinal, o que é refatoração?
Refactoring is the process of changing a software system in such a way that it does not alter the external behavior of the code yet improves its internal structure.
Martin Fowler
Quando não devo refatorar?
- Quando um trecho de código não é muito utilizado
- Quando o código será descartado em breve
Quando devo refatorar?
- Quando um trecho de código é mudado constantemente
- Quando o código deverá ser mantido ao longo do tempo
Como identificar um trecho que precisa ser refatorado?
- Code Smells
- Complexidade ciclomática
- Código duplicado
Como dizer se um código está bom?
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

Por que devo me importar com isso?

Dívida Técnica
Profissionalismo
Evitar desperdício
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: 1)
- 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.";
}
}
}

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;
}

Funções bem escritas
- Agora temos várias funções pequenas
- Fazendo apenas uma coisa cada
- Facilitando a manutenção
- 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
- Não devem explicar o código
- Atualizar o código e o comentário gera retrabalho
- Podem ser usados para documentação e API's públicas
- Também são úteis em arquivos de configuração
Comentários
O código deve ser autoexplicativo
# Não pode ser vazio e nem nulo
if (!is_empty($name) && !is_null($name))

Comentários
function notIsEmptyOrNull($param) {
return !is_empty($param) && !is_null($param);
}
if (notIsEmptyOrNull($name))

Comentários
use Carbon\Carbon;
# Retorna a data atual
# Formato Y-m-d
Carbon::now();
O comentário é mentiroso!
use Carbon\Carbon;
# Retorna a data atual
# Formato Y-m-d
echo Carbon::now();
// 2016-06-24 15:18:34


Comentários
/**
* @SWG\Post(
* path="/pets",
* operationId="addPet",
* description="Creates a new pet in the store. Duplicates are allowed",
* produces={"application/json"},
* @SWG\Parameter(
* name="pet",
* in="body",
* description="Pet to add to the store",
* required=true,
* @SWG\Schema(ref="#/definitions/NewPet"),
* ),
* @SWG\Response(
* response=200,
* description="pet response",
* @SWG\Schema(ref="#/definitions/Pet")
* ),
* @SWG\Response(
* response="default",
* description="unexpected error",
* @SWG\Schema(ref="#/definitions/ErrorModel")
* )
* )
*/
public function addPet()
{
}

São úteis para gerar documentação
Comentários
/*
|--------------------------------------------------------------------------
| Application URL
|--------------------------------------------------------------------------
|
| This URL is used by the console to properly generate URLs when using
| the Artisan command line tool. You should set this to the root of
| your application so that it is used when running Artisan tasks.
|
*/
'url' => env('APP_URL', 'http://localhost'),
/*
|--------------------------------------------------------------------------
| Application Timezone
|--------------------------------------------------------------------------
|
| Here you may specify the default timezone for your application, which
| will be used by the PHP date and date-time functions. We have gone
| ahead and set this to a sensible default for you out of the box.
|
*/
'timezone' => 'Africa/Windhoek',

São úteis em arquivos de configuração
Arquivo config/app.php gerado pelo Laravel Framework
Comentários
private function updateListOrder($id, $listOrder, $parentId = 0)
{
//$token = token($this->email);
//if (self::where('confirmation_token', $token)->first()) {
// return $this->getUniqueConfirmationToken();
//}
$row = Testimonial::find($id);
$row->list_order = $listOrder;
$row->save();
return $row;
}
Não deixe trechos de código comentados

Comentários
- Podemos consultar códigos removidos pelo git
- Um comentário é útil se colocado no lugar certo
- Comentários não devem explicar o código
Classes
Classes
- Pequenas!
- Devem ser coesas
- Os tópicos abordados em funções também servem para os métodos
- Respeite o encapsulamento
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() {
# ...
}

Seu código deve ser lido de cima para baixo
COESÃO
X
ACOPLAMENTO
Classes
class Person {
private $name;
private $age;
private $zipCode;
private $street;
private $number;
private $neighborhood;
private $state;
private $city;
private $cellphone;
private $phone;
private $agency;
private $accountNumber;
# ...
}
A classe não está coesa! God Class!

Classes
class Address {
private $zipCode;
private $street;
private $number;
private $neighborhood;
private $state;
private $city;
}
class Contact {
private $cellphone;
private $phone;
}
Agora temos classes mais coesas.
class CustomerAccount {
private $agency;
private $number;
}
class Person {
private $name;
private $age;
private $account;
private $contact;
private $address;
}

Classes
$address = new Address();
$contact = new Contact();
$account = new CustomerAccount();
$person = new Person('Juca', 20, $address, $contact, $account);
Favorecendo a composição :)

ENCAPSULAMENTO
Classes
- Será mostrado um exemplo com um carrinho de compras em uma loja virtual
- Com as classes Product, Item e Cart
Classes
class Product {
private $value;
private $name;
public function __construct($name, $value) {
$this->name = $name;
$this->value = $value;
}
public function getValue() {
return $this->value;
}
}
Classes
class Item {
private $product;
private $quantity;
public function __construct($product, $quantity) {
$this->product = $product;
$this->quantity = $quantity;
}
public function getProduct() {
return $this->product;
}
public function getQuantity() {
return $this->quantity;
}
}
Classes
class Cart {
private $owner;
private $items;
public function __construct($owner, $items) {
$this->owner = $owner;
$this->items = $items;
}
public function getTotalValueOfItems() {
$total = 0;
foreach ($this->items as $item) {
$total += $item->getProduct()->getValue() * $item->getQuantity();
}
return $total;
}
}
O que há de errado nessa classe?
Classes
class Cart {
private $owner;
private $items;
public function __construct($owner, $items) {
$this->owner = $owner;
$this->items = $items;
}
public function getTotalValueOfItems() {
$total = 0;
foreach ($this->items as $item) {
$total += $item->getProduct()->getValue() * $item->getQuantity();
}
return $total;
}
}

Classes
- A classe Cart está acessando dados da classe Product sem a conhecer, violando o encapsulamento
-
Quem deve retornar o valor de cada produto é a classe Item
-
Para refatorar isso usamos a Lei de Demeter
Classes
class Item {
private $product;
private $quantity;
public function __construct($product, $quantity) {
$this->product = $product;
$this->quantity = $quantity;
}
public function getProduct() {
return $this->product;
}
public function getQuantity() {
return $this->quantity;
}
public function totalValue() {
return $this->product->getValue() * $this->quantity;
}
}

Classes
class Cart {
private $owner;
private $items;
public function __construct($owner, $items) {
$this->owner = $owner;
$this->items = $items;
}
public function getTotalValueOfItems() {
$total = 0;
foreach ($this->items as $item) {
$total += $item->totalValue();
}
return $total;
}
}

Classes
- Com o uso correto da orientação a objetos evitamos Code Smells
-
Devemos sempre conciliar coesão e acoplamento
-
Nosso foco deve estar na troca de mensagens entre objetos
Conclusão
- Escrevemos código para outros desenvolvedores, por isso deve ser claro
- Assim evitamos que nossos projetos de hoje sejam os legados horríveis de amanhã
Lembre-se
"Any fool can write code that a computer can understand. Good programmers write
code that humans can understand."
Martin Fowler


Entre em contato



vba321@hotmail.com


Referências
- Clean Code
- Refactoring: Improving the Design of Existing Code
- Complexidade ciclomática
- Fórmula de Bhaskara
- Code smells
- Palestra Uncle Bob
- PHP Traits
- Orientação a objetos e SOLID para ninjas
- Dívida Técnica por Ward Cunningham
- Dívida Técnica por Martin Fowler
- The Paperboy, The Wallet, and The Law Of Demeter
Refatorando na medida certa
By Vinícius Alonso
Refatorando na medida certa
- 902