Design Patterns com PHP

1,208 views

Published on

Palestra sobre utilização de design patterns em PHP. Exemplos práticos com Singleton, Facade, Adapter, Front Controller, Template View, Table Data Gateway e Active Record.

Published in: Technology

Design Patterns com PHP

  1. 1. Design Patterns com PHP Conceitos, exemplos, e outras coisas Pablo Dall'Oglio @pablodalloglio fb/pablodalloglio
  2. 2. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #2 Meu caminho ● Clipper (1994-1998): comercial, bibliotecas, funções; ● Delphi (1998-1999): automação, componentes; ● PHP (2000): SAGU (php+html+sql); ● PHP-GTK(2001): PHP só com classes; ● Agata Report (2001-2006); ● Gnuteca (2002): PHP Web com classes; ● PHP-GTK: Criando Aplicações Gráficas com PHP (2004); ● Design Patterns (2004): Unisinos; ● Core (2006): Primeira experiência com Framework MVC; ● PHP: Programando com Orientação a Objetos (2007); ● Mestrado em Engenharia de Software (2008, 2009); ● Criando Relatórios com PHP (2011); ● Adianti Framework para PHP (2012).
  3. 3. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #3 O que são padrões? Existem padrões na diversidade Jargão próprio
  4. 4. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #4 O que são padrões? A aplicação do padrão depende do contexto
  5. 5. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #5 Design Pattern O que são Design Patterns ? “Um Pattern descreve um problema que ocorre com frequência em nosso ambiente, e então explica a essência da solução para este problema, de forma que tal solução possa ser utilizada milhões de outras vezes...” Christopher Alexander (1936 arquiteto) Datas importantes: ● 1977 Christopher o aplica na área da Arquitetura; ● 1987 Kent Beck/Ward Cunningham programação;→ ● 1994 Design Patterns: Elements of Reusable OO Software; ● 2002 Patterns of Enterprise Application Architecture.
  6. 6. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #6 Design Patterns ● Padrões são descobertos, não inventados; ● Na maioria das vezes, já utilizamos algum padrão, sem saber; ● O conhecimento dos padrões nos leva a distinguir em quais situações utilizá-los; ● Não existe padrão melhor ou pior, existem padrões que são mais indicados para determinadas situações; ● Um padrão não é uma solução pronta; ● Os códigos a seguir são apenas UMA das formas de implementar os patterns, provavelmente não a melhor, visto que aqui o enfoque é DIDÁTICO :-)
  7. 7. Padrões GoF
  8. 8. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #8 ● É necessário compartilhar informações (prefs, configs); ● Visíveis dentro de diferentes contextos, classes, métodos; ● Variáveis globais, são do mal, falta encapsulamento; ● Como compartilhar uma única versão da verdade; ● Singleton: ● Classe é visível global; ● Mas permite N Objetos; ● E se retornasse o mesmo? ● Limitar a instanciação; ● Método para criar 1 Obj; ● Não mais do que 1. Singleton
  9. 9. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #9 ● Como fabricar apenas uma instância? ● Seu método construtor é marcado como private; ● Tente dar um new fora da classe terá um: Fatal Error; ● Só poderá instanciar dentro da própria classe; ● É preciso criar outro método de instanciação: getInstance(); ● Retornará sempre a mesma instância. Singleton
  10. 10. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #10 <?php class Preferencias { private $data; private static $instance; private function __construct() { $this->data = parse_ini_file('application.ini'); } public static function getInstance() { if (empty(self::$instance)) { self::$instance = new self; } return self::$instance; } public function setData($key, $value) { $this->data[$key] = $value; } public function getData($key) {} public function save() {} } Singleton Única instância que será criada
  11. 11. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #11 <?php // obtém uma instância $p1 = Preferencias::getInstance(); print 'A linguagem é: '. $p1->getData('language') . "<br>n"; $p1->setData('language', 'pt'); print 'A linguagem é: '. $p1->getData('language') . "<br>n"; <?php // obtém a mesma instância $p2 = Preferencias::getInstance(); print 'A linguagem é: '. $p2->getData('language') . "<br>n"; // Descomentar para gravar o valor // $p1->save(); Singleton
  12. 12. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #12 Acoplamento ● Muitos desenvolvedores integram bibliotecas assim: Alto acoplamento: Até pode funcionar, mas...
  13. 13. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #13 ● É quanto um módulo (classe, método) conhece e depende de outro; ● Baixo: – Não depende de muitas outras; – Menos efeitos colaterais em modificações. ● Alto: – Menos reutilizável sozinha; – Mais sensível à mudanças. ● O objetivo é criar modelos com baixo acoplamento; ● É impossível acoplamento ZERO; Acoplamento
  14. 14. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #14 Facade
  15. 15. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #15 Facade ● Facade ajuda a diminuir o acoplamento; ● Oferece uma interface única para um conjunto de interfaces de um subsistema; ● A APP ficará dependente da Facade, não do subsistema. APP
  16. 16. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #16 Facade <?php if ($paymenttype_id == 1) // PAGSEGURO { $paymentRequest = new PagSeguroPaymentRequest(); $item = new PagSeguroItem; $item->setDescription( $product->description ); $item->setQuantity( $data->amount ); $item->setAmount( $price ); $paymentRequest->addItem($item); $address = new PagSeguroAddress; $address->setPostalCode( $customer->postal ); $address->setStreet( $customer->address ); $address->setCity( $customer->city ); $paymentRequest->setShippingAddress($address); $sender = new PagSeguroSender; $sender->setName( $customer->name ); $sender->setEmail( $customer->email ); $paymentRequest->setSender($sender); }
  17. 17. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #17 Facade <?php else if ($paymenttype_id == 2) // PAYPAL { $total = ($product->price * $data->amount); // dados para enviar para o paypal $padata ='&CURRENCYCODE='.urlencode($ini['currency']). '&PAYMENTACTION=Sale'. '&ALLOWNOTE=1'. '&PAYMENTREQUEST_0_AMT='.$total. '&PAYMENTREQUEST_0_ITEMAMT='.$total. '&L_PAYMENTREQUEST_0_QTY0='. $data->amount. '&L_PAYMENTREQUEST_0_AMT0='.$product->price. '&L_PAYMENTREQUEST_0_NAME0='.$product->description. '&L_PAYMENTREQUEST_0_NUMBER0='.1. '&AMT='.$total; // obtém o token $paypal= new PayPalFacade; $httpresult = $paypal->PPHttpPost('SetExpressCheckout', $padata, $ini['username'], $ini['password']); } ?>
  18. 18. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #18 Facade <?php class PaymentFacade { public function addItem($desc, $qtde, $preco) { //... } public function setCustomer($nome, $ender, $cidade) { //... } public function setPaymentType($type) { //... } public function process() { //... } } ?> Pode ser resolvido com outros padrões também
  19. 19. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #19 Adapter ● Favorece o isolamento e a manutenção; ● Converte a interface de uma classe em outra; ● Também conhecido como Wrapper: – Object Wrapper: Encapsula adaptado por composição; – Class Wrapper: Adapta interface por herança.
  20. 20. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #20 Adapter <?php class PHPMailerAdapter { private $pm; public function __construct() { $this->pm = new PHPMailer; $this->pm-> CharSet = 'utf-8'; } public function setFrom($from, $name) { $this->pm-> From = $from; $this->pm-> FromName = $name; } public function setTextBody($body) { $this->pm-> Body = $body; $this->pm-> IsHTML(false); } Wrapper por Composição
  21. 21. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #21 Adapter <?php require_once 'PHPMailer.php'; require_once 'classes/PHPMailerAdapter.php'; $mail = new PHPMailerAdapter; $mail->setUseSmtp(); $mail->setSmtpHost('smtp.gmail.com', 465); $mail->setSmtpUser('pablo@dalloglio.net', 'minhasenha'); $mail->setFrom('pablo@dalloglio.net', 'Pablo Dall Oglio'); $mail->addAddress('destinatario@gmail.com', 'Destinatário'); $mail->setSubject('Oi Cara'); $mail->setHtmlBody('<b>Isso é um <i>teste</i></b>'); $mail->send();
  22. 22. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #22 Evolução "... the hardest is evolving reusable object- oriented software We touch on this a little bit in Design Patterns. For example, factories, adapters and facades can help when it comes to changing and evolving a reusable library" Erich Gamma - Gang of Four evoluir
  23. 23. Padrões de Visualização e controle
  24. 24. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #24 #Go Horse 1 <?php // configuração $conn = pg_connect("host=localhost port=5432 dbname=exemplos..."); // query $query = 'SELECT id, nome, endereco FROM cliente WHERE id not in (...)'; // resultados $result = pg_query($conn, $query); if ($result) { // apresentação echo '<table border="1">'; while ($row = pg_fetch_assoc($result)) { echo '<tr>'; echo '<td>' . $row['id'] . '</td>'; echo '<td>' . $row['nome'] . '</td>'; echo '<td>' . $row['endereco'] . '</td>'; echo '</tr>'; } echo '</table>'; } pg_close($conn); Business rule? Apresentação Configuração
  25. 25. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #25 One script per page ● Você começou a programar Web... ● Cada coisa é um script; ● Um script representa um programa; ● Podemos ter vários scripts para o mesmo sistema; ● Listagem a seguir:
  26. 26. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #26 Front Controller ● Um objeto que interpreta uma requisição para uma ação e decide o fluxo de execução a tomar; ● Existem tarefas que se repetem por todas as rotinas (logs, autenticação, internacionalização e padronização de interface); ● A aplicação pode ter um ponto central de acesso, que coordena qual programa será executado; ● O Front Controller é um tipo de script centralizador, também conhecido por “One script serves all”; ● Um Front Controller geralmente é representado por um objeto que recebe todas as requisições de um site; ● A tarefa deste objeto é analisar alguns parâmetros, como a URL e decidir qual comando executar, qual objeto instanciar.
  27. 27. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #27 Front Controller
  28. 28. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #28 Front Controller index.php <?php if ($_GET) { $class = $_GET['class']; if (class_exists($class)) { $pagina = new $class; $pagina->show(); } } Page.php class Page { public function show() { if ($_REQUEST) { $method = $_REQUEST['method']; if (method_exists($this, $method)) { call_user_func(array($this, $method), $_REQUEST); } } } } sessão, permissões, timezone, segurança, layout... Todos precisam ter um método show? Layer Supertype
  29. 29. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #29 Front Controller <?php class CidadeControl extends Page { public function listar() { try { Transaction::open('livro'); $cidades = $cidade->all(); if ($cidades) { foreach ($cidades as $cidade) { print "{$cidade->id} - {$cidade->nome}<br>"; } } Transaction::close(); } catch (Exception $e) { print $e->getMessage(); } } } index.php?class=CidadeControl&method=listar
  30. 30. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #30 Template View ● Você precisa caprichar no Visual; ● Os componentes prontos não são suficientes; ● Não cometa o erro de ecoar HTML do seu Controller; ● Você precisa misturar conteúdo dinâmico e estático; ● Templates permitem separar as coisas (PHP/HTML); ● HTML com pequenas marcações em seu conteúdo; ● Marcações são substituídas por conteúdo dinâmico; ● Permite substituição, repetição; ● Permite isolar chamadas de bibliotecas (jQuery, Bootstrap); ● Quando a biblioteca muda, é necessário atualizá-lo (!SASS, LESS).
  31. 31. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #31 Html Renderer (AF) <!--[main]--> <table class="tform" style="border:1px solid #B7B7B7"> <tr> <td colspan="2">Customer data</div></td> </tr> <tr> <td width="50%"> ID </td> <td width="50%"> {$id} </td> </tr> <tr> <td width="50%"> Name </td> <td width="50%"> {$name} </td> </tr> <tr> <td width="50%"> Address </td> <td width="50%"> {$address} </td> </tr> </table> <!--[/main]--> Replaces
  32. 32. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #32 Html Renderer (AF) app/control/TemplateController.php class TemplateController extends TPage { public function exibe() { $html = new THtmlRenderer('app/resources/customer.html'); TTransaction::open('samples'); $customer = Customer::find(1); $replace = ['code' => $customer->id, 'name' => $customer->name, 'address' => $customer->address]; $html->enableSection('main', $replace); $html->show(); TTransaction::close(); } }
  33. 33. Padrões de persistência
  34. 34. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #34 #Go Horse 2 class ContaReceber { function inserir($id, $a, $b, $c) { $sql = "INSERT INTO conta_receber..."; // exec $sql; } function listar() { $sql = "SELECT * FROM conta_receber"; // exec $sql } function getContasEmAberto() { $sql = "SELECT id, sum(valor) FROM contas_receber cr, lancamentos l WHERE l.conta_id = cr.id GROUP BY 1 HAVING sum(valor) >1”; } function getContasEmAtraso() { $sql = "SELECT ... ... WHERE dt_vencimento <= date(now())"; } } Lógicas complexas extensas em SQL 10 piores SQL.pdf Começou a separar Código reflexo do BD Pensamento estruturado
  35. 35. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #35 Mapeamento Obj-Rel ● BDR surgiram na década 70 (padrão estruturado); ● BDR não suportam diversos conceitos OO (herança, composição, agregação, polimorfismo, métodos, etc); ● BDOO ainda tem pouca adoção (desempenho inferior, pouca documentação, ferramentas não tão maduras); ● BDR são amplamente utilizados para armazenar objetos; ● Há uma dissonância corrigida pelo uso de técnicas MOR; ● Muitas ferramentas de persistência surgiram; ● Persistência significa continuar a existir, perseverar; ● Objetos existirem em um meio externo à aplicação; ● Vamos ver algumas técnicas.
  36. 36. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #36 Table Data Gateway ● Uma classe responsável por persistir (inserir, alterar, excluir) e retornar dados do BD; ● Uma (1) classe por tabela do banco de dados. Apenas uma instância desta classe irá manipular todos os registros; ● Stateless: É necessário sempre identificar o registro sobre o qual o método estará operando;
  37. 37. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #37 Table Data Gateway <?php class ProdutoGateway { private static $conn; public static function setConnection( PDO $conn ) { self::$conn = $conn; } public function find($id, $class = 'stdClass') { $sql = "SELECT * FROM produto where id = '$id' "; $result = self::$conn->query($sql); return $result->fetchObject($class); } public function save($data) { if (empty($data->id)) { $sql = "INSERT INTO produto (descricao, estoque, ...)". " VALUES ('{$id}', '{$data->descricao}', ... )"; } Injeção de dependência Usada pela Model INSERT or UPDATE
  38. 38. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #38 Table Data Gateway <?php class Produto { private $data; function __get($prop) {} function __set($prop, $value) {} public static function find($id) { $gw = new ProdutoGateway; return $gw->find($id, 'Produto'); } public function save() { $gw = new ProdutoGateway; return $gw->save( (object) $this->data); } public function getMargemLucro() {} public function registraCompra($custo, $quantidade) {} } Model Chamada do Gateway Lógica de negócios
  39. 39. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #39 Active Record ● Classe que conjuga lógica e persistência; ● Um (1) objeto por linha do banco de dados; ● Statefull: Retém os dados do objeto corrente.
  40. 40. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #40 Layer Supertype ● Métodos se repetem (load, update, delete, ...); ● Métodos podem ser generalizados se seguirmos convenções; ● Vamos criar uma superclasse para persistência; ● Herdada por todos objetos de negócio; ● Ela implementará métodos de maneira genérica; ● Layer Supertype: Superclasse para uma camada inteira; ● Funcionalidades comuns para todos objetos na superclasse; ● Métodos de NEGÓCIO ficam na classe filha;
  41. 41. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #41 Active Record Armazenar um novo objeto (INSERT). class Pessoa extends TRecord { const TABLENAME = 'tab_pessoas'; const PRIMARYKEY= 'id'; const IDPOLICY = 'max'; // {max, serial} } TTransaction::open('samples'); $object = new Pessoa; $object->name = 'Maria da Silva'; $object->address = 'Rua da Conceicao'; $object->phone = '(51) 8111-2222'; $object->status = 'S'; $object->email = 'maria@email.com'; $object->store(); // armazena o objeto TTransaction::close(); // fecha a transação. Business Rules Supertype Statefull
  42. 42. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #42 Active Record Alterar um objeto já existente (UPDATE). TTransaction::open('samples'); // abre uma transação $customer = Customer::find(31); // carrega o cliente 31 if ($customer) // se existe { $customer->phone = '51 8111-3333'; // muda o fone $customer->store(); // armazena o objeto } new TMessage('info', 'Objeto atualizado'); TTransaction::close(); // fecha a transação. INSERT OR UPDATE STATEFULL
  43. 43. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #43 Atalhos Manipular um conjunto de objetos conforme um filtro. $product = Product::find(2); $products = Product::all(); $count = User::where('age', '>', 18)->count(); $products = Product::where('name', 'like', '%Computer%')->load(); $products = Product::where('id', '>', '1')->orderBy('price')->load(); $products = Product::where('type', '=', '10')->take(10)->skip(20)->load(); User::where('age', '>', 100)->delete(); $contacts = Customer::find(123)->hasMany('Contact'); $turma = Turma::find(10); foreach ($turma->getMatriculas() as $matricula) { print $matricula->aluno->nome; } SQL gerado com Prepared NAVEGABILIDADE entre relações
  44. 44. Adianti Solutions Ltda © Pablo Dall'Oglio Programando para Programadores #44 Obrigado ● Contato: – pablo.blog.br – adianti.com.br – @pablodalloglio – @adiantisolution ● Não esquecer de falar do Sorteio!

×