DevTalk Zoox 04/2016
-
Upload
leandro-silva -
Category
Software
-
view
123 -
download
1
Transcript of DevTalk Zoox 04/2016
ZOOX DEV TALK 04/2016
Princípios para melhorar um código
MOTIVAÇÃO
Por que devemos sempre melhorar o código?
Por que um código que funciona não é suficiente?
AVISO
Estes princípios são sugestões e não regras!
STUPID
Singleton
Tight Coupling
Untestability
Premature Optimization
Indescriptive Naming
Duplication
SINGLETON
Representa um estado global do seu código
Programas que usam estado global são difíceis de testar e debugar
Programas que dependem do estado global mascaram suas dependências
TIGHT COUPLING
Generalização do problema com Singleton
Se mudar um módulo te obriga a mudar outro módulo
Torna o código difícil de reusar e testar
Para evitar, favorecer composição sobre herança e usar injeção dedependência sempre que possível
UNTESTABILITY
Testes não devem ser difíceis
Deixar de escrever um teste economiza tempo de imediato, mas perdemuito mais depois ”caçando” bugs e testando manualmente (falho)
Se escrever um teste é difícil, o problema está no código
Normalmente, untestability é causada por tight coupling
PREMATURE OPTIMIZATION
Não super complique o código para otimizá-lo
Terá apenas custo e não benefício
Meça a performance antes de otimizar (benchmarks)
YAGNI = You Ain’t Gonna Need It (Você não vai precisar disso)
INDESCRIPTIVE NAMING
Nomeie classes, métodos, atributos apropriadamente
Não abrevie
Se não consegue achar um nome apropriado para uma classe, talvez a responsabilidade dela não esteja bem definida.
Linguagem de programação é para humanos
Para o computador, $vlna é o mesmo que $valorLitraoNoAlto
DUPLICAÇÃO
Se duplicar código e tiver que fazer um ajuste, terá que mexer emvários lugares (e se esquecer de um?)
DRY = Don’t Repeat Youself
KISS = Keep It Simple, Stupid!
SOLID
Single Responsibility Principle
Open/Close Principle
Liskov Substitution Principle
Interface Segregation Principle
Dependency Inversion Principle
SINGLE RESPONSIBILITY PRINCIPLE
Nunca deve haver mais de uma razão para uma classe mudar
Divida classes grandes
Use camadas
Evite classes God
SRP - EXEMPLO
<?php
class Livro
{
function getAutor()
{
return 'João da Silva';
}
function getTitulo()
{
return 'Grande Livro';
}
function virarPagina()
{
// Avança ponteiro de página
}
function mostraPaginaAtual()
{
echo 'Conteúdo da página';
}
}
O que há de errado no código?
SRP – EXEMPLO MELHORADO
<?php
class Livro
{
function getTitulo()
{
return 'Grande Livro';
}
function getPaginaAtual()
{
echo 'Conteúdo da página';
}
}
interface Printer
{
function printPage($page);
}
class PlainTextPrinter implements Printer
{
function printPage($page) {
echo $page;
}
}
class HtmlPrinter implements Printer
{
function printPage($page) {
echo '<div style="single-page">' . $page . '</div>';
}
}
$livro = new Livro();
$printer = new HtmlPrinter();
$printer->printPage($livro->getPaginaAtual());
OPEN/CLOSE PRINCIPLE
Classes devem ser abertas para expansão, mas fechadas para alteração
Defina todos os atributos como private
Sem variáveis globais
Evite setter (sempre que possível)
OCP - EXEMPLO
<?php
class Retangulo
{
public $altura;
public $largura;
}
class Area
{
public function calculaArea(Retangulo $retangulo)
{
return $retangulo->altura * $retangulo->largura;
}
}
OCP – EXEMPLO DE VIOLAÇÃO
<?php
class Retangulo
{
public $altura;
public $largura;
}
class Circulo
{
public $radio;
}
class Area
{
public function calculaArea($objeto)
{
if ($objeto instanceof Retangulo) {
return $objeto->altura * $objeto->largura;
} elseif ($objeto instanceof Circulo) {
return $objeto->circulo * $objeto->circulo * PI;
}
}
}
OCP – EXEMPLO DE VIOLAÇÃO
<?php
class Retangulo
{
public $altura;
public $largura;
}
class Circulo
{
public $radio;
}
class Area
{
public function calculaArea($objeto)
{
if ($objeto instanceof Retangulo) {
return $objeto->altura * $objeto->largura;
} elseif ($objeto instanceof Circulo) {
return $objeto->circulo * $objeto->circulo * PI;
}
}
}
Tivemos que alterar a classe Area para poder extendê-la. Ela não é
fechada para modificações = não éaberta para expansão
OCP – EXEMPLO MELHORADO
<?php
abstract class Forma
{
abstract function calculaArea();
}
class Retangulo extends Forma
{
public $altura;
public $largura;
public function calculaArea()
{
return $this->altura * $this->largura;
}
}
class Circulo extends Forma
{
public $radio;
public function calculaArea()
{
return $this->circulo * $this->circulo * PI;
}
}
class Area
{
public function calculaArea(Forma $objeto)
{
return $objeto->calculaArea();
}
}
LISKOV SUBSTITUTION PRINCIPLE
Subclasses devem poder ser substituidas por sua classe base
Classes filhas não devem quebrar as definições de suas bases
Objetos num código devem poder ser substituídos por seus subtipossem alterar o funcionamento correto do programa
LSP - EXEMPLO
<?php
class Retangulo
{
protected $altura;
protected $largura;
public function setLargura($largura)
{ $this->largura = $largura; }
public function setAltura($altura)
{ $this->altura = $altura; }
public function getArea()
{
return $this->altura * $this->largura;
}
}
$retangulo = new Retangulo();
$retangulo->setAltura(10);
$retangulo->setLargura(20);
if ($retangulo->getArea() !== 200) {
throw new \Exception('Área errada');
}
0
LSP - EXEMPLO DE VIOLAÇÃO
<?php
class Retangulo
{
protected $altura;
protected $largura;
public function setLargura($largura) { $this->largura = $largura; }
public function setAltura($altura) { $this->altura = $altura; }
public function getArea()
{
return $this->altura * $this->largura;
}
}
class Quadrado extends Retangulo
{
public function setLargura($largura)
{ $this->largura = $this->altura = $largura; }
public function setAltura($altura)
{ $this->altura = $this->largura = $altura; }
}
$quadrado = new Quadrado();
$quadrado->setAltura(10);
$quadrado->setLargura(20);
if ($quadrado->getArea() !== 200) {
throw new \Exception('Área errada');
}
INTERFACE SEGREGATION PRINCIPLE
Melhor ter várias interfaces específicas a uma só genérica
Uma classe não deveria implementar métodos que não usa
Usando ISP você garante low coupling e high coersion
ISP - EXEMPLO
<?php
interface Veiculo
{
public function liga();
public function abreMala();
public function numEixos();
public function ehMensalista();
}
class Carro implements Veiculo
{
}
class Estacionamento
{
public function permiteEntrada(Veiculo $veiculo)
{
return $veiculo->ehMensalista();
}
}
class Pedagio
{
public function custo(Veiculo $veiculo)
{
return $veiculo->numEixos() * 5;
}
}
ISP – EXEMPLO DE VIOLAÇÃO
<?php
interface Veiculo
{
public function liga();
public function abreMala();
public function numEixos();
public function ehMensalista();
}
class Carro implements Veiculo {}
class Moto implements Veiculo {}
class Estacionamento
{
public function permiteEntrada(Veiculo $veiculo)
{
return $veiculo->ehMensalista();
}
}
class Pedagio
{
public function custo(Veiculo $veiculo)
{
return $veiculo->numEixos() * 5;
}
}
ISP – EXEMPLO MELHORADO
<?php
interface Veiculo
{
public function liga();
}
interface Mensalista
{
public function ehMensalista();
}
interface Mala
{
public function abreMala();
}
interface Eixos
{
public function numEixos();
}
class Carro implements Veiculo, Mensalista, Mala, Eixos {}
class Moto implements Veiculo, Mensalista, Eixos {}
class Estacionamento
{
public function permiteEntrada(Mensalista $veiculo)
{
return $veiculo->ehMensalista();
}
}
class Pedagio
{
public function custo(Eixos $veiculo)
{
return $veiculo->numEixos() * 5;
}
}
DEPENDENCY INVERSION PRINCIPLE
Módulos de alto nível não devem depender de baixo nível, ambos devem depender de abstrações
Abstrações não devem depender de detalhes. Detalhes devemdepender de abstrações
Use o mesmo nível de abstração num determinado nível
DIP - EXEMPLO
<?php
class PDFReader {
private $book;
function __construct(PDFBook $book) {
$this->book = $book;
}
function read() {
return $this->book->read();
}
}
class PDFBook {
function read() {
return "lendo um livro pdf.";
}
}
class Test extends PHPUnit_Framework_TestCase {
function testItCanReadAPDFBook() {
$b = new PDFBook();
$r = new PDFReader($b);
$this->assertRegExp('/livro pdf/', $r->read());
}
}
DIP – EXEMPLO DE VIOLAÇÃO
<?php
class EBookReader {
private $book;
function __construct(PDFBook $book) {
$this->book = $book;
}
function read() {
return $this->book->read();
}
}
class PDFBook {
function read() {
return “lendo um livro pdf.";
}
}
class Test extends PHPUnit_Framework_TestCase {
function testItCanReadAPDFBook() {
$b = new PDFBook();
$r = new EBookReader($b);
$this->assertRegExp('/livro pdf/', $r->read());
}
}
DIP – EXEMPLO MELHORADO
<?php
interface EBook {
function read();
}
class EBookReader {
private $book;
function __construct(EBook $book) {
$this->book = $book;
}
function read() {
return $this->book->read();
}
}
class PDFBook implements Ebook {
function read() {
return " lendo um livro pdf.";
}
}
class Test extends PHPUnit_Framework_TestCase {
function testItCanReadAPDFBook() {
$b = new PDFBook();
$r = new EBookReader($b);
$this->assertRegExp('/livro pdf/', $r->read());
}
}
DIP – EXEMPLO MELHORADO 2
<?phpinterface EBook {
function read();}
class EBookReader {
private $book;
function __construct(EBook $book) {$this->book = $book;
}
function read() {return $this->book->read();
}
}
class PDFBook implements EBook {
function read() {return “lendo um livro pdf.";
}}
class MobiBook implements EBook {
function read() {return “lendo um livro mobi.";
}}
class Test extends PHPUnit_Framework_TestCase {
function testItCanReadAPDFBook() {$b = new PDFBook();$r = new EBookReader($b);
$this->assertRegExp('/livro pdf/', $r->read());}
function testItCanReadAMobiBook() {$b = new MobiBook();$r = new EBookReader($b);
$this->assertRegExp('/livro mobi/', $r->read());}
}
DIP – EXEMPLO MELHORADO 2
<?phpinterface EBook {
function read();}
class EBookReader {
private $book;
function __construct(EBook $book) {$this->book = $book;
}
function read() {return $this->book->read();
}
}
class PDFBook implements EBook {
function read() {return “lendo um livro pdf.";
}}
class MobiBook implements EBook {
function read() {return “lendo um livro mobi.";
}}
class Test extends PHPUnit_Framework_TestCase {
function testItCanReadAPDFBook() {$b = new PDFBook();$r = new EBookReader($b);
$this->assertRegExp('/livro pdf/', $r->read());}
function testItCanReadAMobiBook() {$b = new MobiBook();$r = new EBookReader($b);
$this->assertRegExp('/livro mobi/', $r->read());}
}
• Não precisamos mudar a classe EBooReader = OCP• Separamos as responsabilidades = SRP• Segregamos nossas interfaces = ISP• Usamos corretamente o subtipo = LSP
• DIP nos ajuda a manter os outros 4 princípios