Upload
michael-castillo-granados
View
226
Download
1
Embed Size (px)
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.
Citation preview
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