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 de
dependência sempre que possível
UNTESTABILITY
 Testes não devem ser difíceis
 Deixar de escrever um teste economiza tempo de imediato, mas perde
muito 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 em
vá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 subtipos
sem 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 devem
depender 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
<?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 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
<?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 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

DevTalk Zoox 04/2016

  • 1.
    ZOOX DEV TALK04/2016 Princípios para melhorar um código
  • 2.
    MOTIVAÇÃO  Por quedevemos sempre melhorar o código?  Por que um código que funciona não é suficiente?
  • 3.
    AVISO  Estes princípiossão sugestões e não regras!
  • 4.
    STUPID  Singleton  TightCoupling  Untestability  Premature Optimization  Indescriptive Naming  Duplication
  • 5.
    SINGLETON  Representa umestado 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
  • 6.
    TIGHT COUPLING  Generalizaçãodo 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 de dependência sempre que possível
  • 7.
    UNTESTABILITY  Testes nãodevem ser difíceis  Deixar de escrever um teste economiza tempo de imediato, mas perde muito 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
  • 8.
    PREMATURE OPTIMIZATION  Nãosuper 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)
  • 9.
    INDESCRIPTIVE NAMING  Nomeieclasses, 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
  • 10.
    DUPLICAÇÃO  Se duplicarcódigo e tiver que fazer um ajuste, terá que mexer em vários lugares (e se esquecer de um?)  DRY = Don’t Repeat Youself  KISS = Keep It Simple, Stupid!
  • 11.
    SOLID  Single ResponsibilityPrinciple  Open/Close Principle  Liskov Substitution Principle  Interface Segregation Principle  Dependency Inversion Principle
  • 12.
    SINGLE RESPONSIBILITY PRINCIPLE Nunca deve haver mais de uma razão para uma classe mudar  Divida classes grandes  Use camadas  Evite classes God
  • 13.
    SRP - EXEMPLO <?php classLivro { 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?
  • 14.
    SRP – EXEMPLOMELHORADO <?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());
  • 15.
    OPEN/CLOSE PRINCIPLE  Classesdevem 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)
  • 16.
    OCP - EXEMPLO <?php classRetangulo { public $altura; public $largura; } class Area { public function calculaArea(Retangulo $retangulo) { return $retangulo->altura * $retangulo->largura; } }
  • 17.
    OCP – EXEMPLODE 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; } } }
  • 18.
    OCP – EXEMPLODE 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
  • 19.
    OCP – EXEMPLOMELHORADO <?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(); } }
  • 20.
    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 subtipos sem alterar o funcionamento correto do programa
  • 21.
    LSP - EXEMPLO <?php classRetangulo { 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
  • 22.
    LSP - EXEMPLODE 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'); }
  • 23.
    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
  • 24.
    ISP - EXEMPLO <?php interfaceVeiculo { 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; } }
  • 25.
    ISP – EXEMPLODE 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; } }
  • 26.
    ISP – EXEMPLOMELHORADO <?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; } }
  • 27.
    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 devem depender de abstrações  Use o mesmo nível de abstração num determinado nível
  • 28.
    DIP - EXEMPLO <?php classPDFReader { 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()); } }
  • 29.
    DIP – EXEMPLODE 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()); } }
  • 30.
    DIP – EXEMPLOMELHORADO <?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()); } }
  • 31.
    DIP – EXEMPLOMELHORADO 2 <?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 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()); } }
  • 32.
    DIP – EXEMPLOMELHORADO 2 <?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 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