código legado - php conference brasil - 2014

Post on 08-Jul-2015

226 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

Código sujo, código ruim, código feio, código mal-feito, código não orientado a objetos, código rebuscado, espaguete de código. Em duas simples palavras: código legado. Algumas dicas simples de como melhorar o seu dia-a-dia e como ganhar a confiança do seu chefe para tomar decisões nos projetos.

TRANSCRIPT

CÓDIGO LEGADO / Michael Granados @dgmike

MICHAEL GRANADOSConversor de sonhos de grandes investidores em realidade

AGENDAWTF?A vítimaVersionamentoPacotes, pacotes, pacotes...Um grupo, um códigoTestesProgramação Calistênica

A VÍTIMA

http://github.com/dgmike/phpconference2014-vitima

WTF?

WTF?Código sujo

WTF?Código ruim

WTF?Código feio

WTF?Código mal-feito

WTF?Código não orientado a objetos

WTF?Código rebuscado

WTF?Código espaguete

CÓDIGO LEGADO!To me, legacy code is simply code without tests.

Michael Feathers

VERSIONAMENTO

COMECE AGORA!csvsubversion (svn)mercurial (hg)git

PARE DE TRABALHAR NO SERVIDOR!Tenha uma cópia do projeto em sua máquina

$ cd projeto_que_fiz_download

$ git init

$ git add .

$ git commit -m "Importando projeto para o git"

AHH.... MAS EU TENHO MEDO DESSA "TELA PRETA"Chega de desculpas: TortoiseGit

DIA-A-DIA

LOG DE ATIVIDADES$ git log --oneline -n 10

0b66a73 Adding stepup uptater207d43f Updating phpdocumentor to get last corrections6015990 creating make filed54aafa some doc miscb2a8726 miscc8c3624 Documentor settings70697d4 Adding phpdocumentor to dev239f47d Merge pull request #16 from dgmike/enhancement/#14bf53652 "method" implementationf3653c8 "method" signature

O QUE MUDOU?$ git diff --name-only b2a8726..HEAD

.stepuprcMakefilecomposer.jsonsrc/Apolo.phpsrc/Route.php

FUNCIONA SOMENTE NA MINHA MÁQUINA?

No seu servidor linux (git init --bare / gitosis)Na máquina do seu amigo (via ip de rede + ssh)

githubgitlabbitbucket

NOVO FLUXO DE TRABALHO

PACOTES, PACOTES, PACOTES...Iremos precisar de ferramentas que irão nos auxiliar ao longo de

nossa jornada.

Fazer download do zip e descompactar é tão anos 90...

Utilize algum gerenciador de pacotes: pear, composer, npm,bower

UTILIZAREMOS COMPOSERFácil de instalar

$ curl -sS https://getcomposer.org/installer | php

UTILIZAREMOS COMPOSERFácil de usar

// composer.json{ "require": { "monolog/monolog": "1.2.*" }}

$ php composer.phar install

UTILIZAREMOS COMPOSERUsar em sua aplicação

require 'vendor/autoload.php';

UM GRUPO, UM CÓDIGO

UM GRUPO, UM CÓDIGOfunction dinheiro($valor) { return 'R$ ' . number_format( $valor, 2, '.', '' );}

function precoComDesconto ($produtos, $codigo_promocional = false){ $total = 0;

foreach ( $produtos as $produto ) { $total += $produto['preco_final']; }

$porcentagem = 0;

if ( count($produtos) == 2 ) {

UM GRUPO, UM CÓDIGOTabs vs Espaços?

2 ou 4 espaços?

Final de linha? mac, linux, windows?

EDITORCONFIG.ORG

EDITORCONFIG.ORG# .editorconfig# top-most EditorConfig fileroot = true

# Unix-style newlines with a newline ending every file[*]end_of_line = lfinsert_final_newline = true

# Matches multiple files with brace expansion notation# Set default charset[*.{js,py}]charset = utf-8

# 4 space indentation[*.php]indent_style = spaceindent_size = 4

CODESNIFFER// composer.json{ "require-dev": { "squizlabs/php_codesniffer": "*" }}

$ composer install

CODESNIFFER$ ./vendor/bin/phpcs --standard=PSR2 funcoes.php

FILE: ./funcoes.php--------------------------------------------------------------------------------FOUND 16 ERROR(S) AFFECTING 10 LINE(S)-------------------------------------------------------------------------------- 7 | ERROR | Expected "foreach (...) {\n"; found "foreach(...)\n {\n" 7 | ERROR | Space found after opening bracket of FOREACH loop 7 | ERROR | Space found before closing bracket of FOREACH loop 7 | ERROR | Expected 0 spaces after opening bracket; 1 found 7 | ERROR | Expected 0 spaces before closing bracket; 1 found 30 | ERROR | Expected "if (...) {\n"; found "if (...)\n {\n" 34 | ERROR | Opening parenthesis of a multi-line function call must be the | | last content on the line 36 | ERROR | Closing parenthesis of a multi-line function call must be on a | | line by itself 47 | ERROR | Expected "function abc(...)"; found "function abc (...)" 47 | ERROR | Expected 0 spaces between argument "$codigo_promocional" and | | closing bracket; 1 found 47 | ERROR | Opening brace should be on a new line

CODESNIFFERfunction dinheiro($valor) { return 'R$ ' . number_format( $valor, 2, '.', '' );}

function precoComDesconto ($produtos, $codigo_promocional = false){ $total = 0;

foreach ( $produtos as $produto ) { $total += $produto['preco_final']; }

$porcentagem = 0;

if ( count($produtos) == 2 ) {

CODESNIFFERfunction dinheiro($valor){ return 'R$ ' . number_format($valor, 2, '.', '');}

function precoComDesconto($produtos, $codigo_promocional = false){ $total = 0;

foreach ($produtos as $produto) { $total += $produto['preco_final']; }

$porcentagem = 0;

if (count($produtos) == 2) { $porcentagem = 5; $total = $total - ($total * 5/100);

TESTESfunction calculaJuros($produtos, $juros){ echo '<pre>'; print_r($produtos); echo '</pre>'; // teste $total = 0; foreach($produtos as $produto) { $total += $produto['valor_real']; } $total = $total + ($total * $juros / 100); die($total); // teste return $total;}

TESTESPacotes: PHPUnit, SimpleTest

Tipos: unitários, integração, aceitacao

SIMPLETEST$ php composer.phar require --dev "lastcraft/simpletest:*"

SIMPLETESTdefine('SIMPLETEST_DIR', realpath(__DIR__ . '/../../vendor/lastcraft/simpletest'));

require SIMPLETEST_DIR . '/autorun.php';require SIMPLETEST_DIR . '/web_tester.php';

class TestIndex extends WebTestCase{ function testHomePage() { $this->assertTrue($this->get('http://localhost:3001')); $this->assertText('Loja Legado'); }

function testCliqueComprarAdicionaLinkCarrinhoDeCompras() { $this->assertTrue($this->get('http://localhost:3001')); $this->assertNoText('Carrinho de compras');

SIMPLETEST$ php tests/aceitacao/test_index.php

test_index.phpOKTest cases run: 1/2, Passes: 13, Failures: 0, Exceptions: 0

test_index.php1) Text [texto inexistente] not detected in [String: Loja Legado Loja Legado Loja Legado Carrinho de compras casacos saias vestidos in testCliqueComprarAdicionaLinkCarrinhoDeCompras in TestIndexFAILURES!!!Test cases run: 1/2, Passes: 12, Failures: 1, Exceptions: 0

PHPUNIT$ php composer.phar require --dev 'phpunit/phpunit:*'

PHPUNITrequire_once __DIR__ . '/../../funcoes.php';

class FuncoesTest extends PHPUnit_Framework_TestCase{ public function testDinheiro() { $dinheiro = dinheiro(24); $this->assertEquals('R$ 24,00', $dinheiro); }}

PHPUNIT$ ./vendor/bin/phpunit tests/unitarios/funcoes_test.php

PHPUnit 4.3.5 by Sebastian Bergmann.

..

Time: 33 ms, Memory: 3.00Mb

OK (2 tests, 4 assertions)

PHPUNIT$ ./vendor/bin/phpunit --testdox tests/unitarios/funcoes_test.php

PHPUnit 4.3.5 by Sebastian Bergmann.

Funcoes [x] Dinheiro [x] Preco com desconto

$ ./vendor/bin/phpunit --tap tests/unitarios/funcoes_test.php

TAP version 13ok 1 - FuncoesTest::testDinheirook 2 - FuncoesTest::testPrecoComDesconto1..2

PHPUNITO que dá pra fazer?

Mock/StubPre-popular banco de dadosCodeCoverageSelenium

PROGRAMAÇÃO CALISTÊNICA

PROGRAMAÇÃO CALISTÊNICA é uma forma de atividade que consiste em uma

variedade de exercícios físicos feitos sem equipamentos oupesos que têm por objetivo aumentar a força e a flexibilidade

usando o peso do próprio corpo como resistência

Calistenia

Jeff Bay escreveu um artigo no livro "The ThoughtWorksAntology" entitulado "Object Calisthenics". Ele propõe um

exercício "calistênico" para melhorar o design de software eajudar a internalizar princípios de bom design de programação

orientada a objetos

ALGUNS PONTOS RELEVANTES!PHP não é JAVA!!!

Algumas alterações serão feitas!

São apenas dicas e não regras!

REGRAS DO EXERCÍCIO1. Use apenas um nível de identação por método2. Não use else3. Crie objetos para tipos primitivos e strings4. Use apenas um ponto por linha5. Não abrevie6. Mantenha todas as entidades pequenas7. Não use classes com mais de duas variáveis instanciadas8. Não use coleções de primeira-classe9. Não use Getters/Setters/Properties

USE APENAS UM NÍVEL DE IDENTAÇÃO POR MÉTODOfunction ganho($registros){ $ganhosTotais = 0; foreach($registros as $registro) { foreach ($registro['produtos'] as $produto) { $ganhosTotais += $produto['valor_real'] - $produto['valor_compra']; } } return $ganhosTotais;}

USE APENAS UM NÍVEL DE IDENTAÇÃO POR MÉTODOfunction ganho($registros){ $ganhosTotais = 0; foreach($registros as $registro) { $ganhosTotais += ganhosPorRegistro($registro); } return $ganhosTotais;}

function ganhosPorRegistro($registro){ $ganhos = 0; foreach ($registro['produtos'] as $produto) { $ganhos += $produto['valor_real'] - $produto['valor_compra']; } return $ganhos;}

NUNCA USE ELSEfunction precoFinal($produto){ if ($produto['desconto'] > 0) { $precoFinal = $produto['preco'] - ($produto['preco'] * $produto['desconto'] / 100); } else { $precoFinal = $produto['preco']; } return $precoFinal;}

NUNCA USE ELSEfunction precoFinal($produto){ if ($produto['desconto'] > 0) { return $produto['preco'] - ($produto['preco'] * $produto['desconto'] / 100); } return $precoFinal = $produto['preco'];}

Outro exemplofunction precoFinal($produto){ $precoFinal = $produto['preco']; if ($produto['desconto'] > 0) { $precoFinal = $produto['preco'] - ($produto['preco'] * $produto['desconto'] / 100); } return $precoFinal;}

CRIE OBJETOS PARA TIPOS PRIMITIVOS E STRINGS SE ELESPOSSUIREM ALGUM COMPORTAMENTO

CRIE OBJETOS PARA TIPOS PRIMITIVOS E STRINGS SE ELESPOSSUIREM ALGUM COMPORTAMENTO

function desconto(array $produtos, $desconto){ $total = 0; foreach($produtos as $produto) { $total += $produto['valor_real']; } return $total - ($total * $desconto / 100);}

CRIE OBJETOS PARA TIPOS PRIMITIVOS E STRINGS SE ELESPOSSUIREM ALGUM COMPORTAMENTO

class ProdutosLista { protected $produtos;

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

public function valor_real_total() { $total = 0; foreach ($this->produtos as $produto) { $total += $produto['valor_real']; } return $total; }}

CRIE OBJETOS PARA TIPOS PRIMITIVOS E STRINGS SE ELESPOSSUIREM ALGUM COMPORTAMENTO

class Numeral { protected $valor;

public function __construct($valorInicial = 0) { $this->valor = $valorInicial; } public function valor() { return $this->valor; } public function adiciona(Numeral $valor) { $this->valor += $valor->valor(); } public function removerPercentual(Numeral $percentual) { $this->valor -= $this->valor() * $percentual / 100; }}

CRIE OBJETOS PARA TIPOS PRIMITIVOS E STRINGS SE ELESPOSSUIREM ALGUM COMPORTAMENTO

function desconto(ProdutosLista $produtos, Numeral $desconto){ $total = new Numeral($produtos->valor_real_total()); $total->removerPercentual($desconto); return $total;}

USE APENAS UM PONTO ( -> ) POR LINHA SE NÃO FOR UMENCADEAMENTO OU UM GETTER

USE APENAS UM ( -> ) POR LINHA SE NÃO FOR UMENCADEAMENTO OU UM GETTER

function categoria(ProdutoLista $produtos){ return $produtos->primeiro->categoria->nome->humanizado();}

USE APENAS UM ( -> ) POR LINHA SE NÃO FOR UMENCADEAMENTO OU UM GETTER

function categoria(ProdutoLista $produtos){ $categoria = $produtos->primeiraCategoria(); $template = $categoria->decorator(new HumanizadoDecorator); return $template->renderiza();}

USE APENAS UM ( -> ) POR LINHA SE NÃO FOR UMENCADEAMENTO OU UM GETTER

$this possui um valor especial no PHPclass Produtos{ protected $produtos;

public function primeiraCategoria() { // em java: primeiroProduto.categoria() return $this->primeiroProduto->categoria(); }}

USE APENAS UM ( -> ) POR LINHA SE NÃO FOR UMENCADEAMENTO OU UM GETTER

$this possui um valor especial no PHP$filtros->adicionaImagem($imagem) ->adicionarFiltro(new PretoBranco) ->adicionarFiltro(new BordaMadeira);

NÃO ABREVIE

ESTÁ ESCREVENDO A MESMA VARIÁVEL MUITAS VEZES?Parece que temos um código reutilizado várias vezes.

Talvez tenhamos duplicação de código

O NOME DO MÉTODO ESTÁ MUITO GRANDE?Talvez sua classe/metodo está executando mais do que deveria.

Talvez tenhamos um problema de responsabilidade

MANTENHA TODAS AS ENTIDADES PEQUENASNada de classes com mais de 50 linhas

Nada de pacotes/diretórios com mais de 10 arquivos

MANTENHA TODAS AS ENTIDADES PEQUENASNada de classes com mais de 100 linhas

Nada de pacotes/diretórios com mais de 15 arquivos

NÃO USE CLASSES COM MAIS DE DUAS VARIÁVEISINSTANCIADAS

TÁ... EU SEI...

NÃO USE CLASSES COM MAIS DE CINCO VARIÁVEISINSTANCIADAS

USE COLEÇÕES COMO PRIMEIRA-CLASSEclass Usuario{ protected $fotos = array(); protected $nome = '';

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

public function linkFotos() { $links = array(); foreach($this->fotos as $foto) { $links[] = $foto['link']; } return implode("\n", $links); }}

USE COLEÇÕES COMO PRIMEIRA-CLASSEclass Usuario{ protected $fotos = null; protected $nome = ''; public function nome() { return $this->nome; } public function fotos() { return $this->fotos; }}

ListaFotos { protected $fotos = array();}

USE COLEÇÕES COMO PRIMEIRA-CLASSE$fotos->filtrar(...);$fotos->mapear(...);$fotos->adicionar(...);$fotos->original(...);

NÃO USE GETTERS/SETTERSclass Pessoa{ protected $nome; public getNome() { return $this->nome; } public setNome($nome) { $this->nome = $nome; }}

USE GETTERS/SETTERSclass Pessoa{ protected $nome;

public getNome() { return $this->nome; }

public maiusculizaNome() { $this->nome = strtoupper($this->nome); }}

SURPRESA! DOCUMENTE SEU CÓDIGO

// verifica se tem mais de 20 produtosif ($produtos->total() > 20) { $produtos->aplicaDesconto(new Desconto(10));}

DOCUMENTE SEU CÓDIGO// resgata somente fotos ativas$fotos = array_map( $fotos->filter('ativos'), create_function('$foto', 'return $foto["link"];'));

Não explique um código ruim, corrijá-o!$fotos->filter('ativos')->links();

PHPDOC/** * Verifica se as credenciais do usuário estão corretas * * @todo implementar conexao com banco de dados * @param String $email Email do usuario * @param String $senha Senha do usuario * * @return Boolean */function verificaLogin(String $email, String $senha) { // ...}

DÚVIDAS?Seu momento de me deixar sem graça! ;-)

OBRIGADO! - @dgmike http://www.slideshare.net/dgmike

top related