2008-01-09 11:15:42
tags: 

O Zen e a arte cavalheiresca da programação orientada a objeto (Parte 22)

                

O Zen e a arte cavalheiresca da programação orientada a objeto (Parte 22)

Para ver os artigos anteriores desta série, clique aqui.

Olá amigos. Estamos de volta depois de muita farofa, bacalhau, doce de leite, maionese, pamonha, queijo cavalinha, uma nova geladeira que não chega, livros e muita alegria. Espero que todos tenham passado dias maravilhosos nesse fim-de-ano e que possam ter aproveitado esses dias pra curtir a família e os amigos!

Mas chegou a hora de voltar para o batente. Antes que vocês comecem a mandar os já tradicionais emails "Cadê o 22! Queremos o 22!" eu já estou escrevendo mais um capítulo desta série singela, que procura explicar os conceitos mais enrolados de orientação a objetos em uma linguagem que todos aqui possam facilmente entender. E, pra ser bem honesto, os emails cobrando o 22 na verdade já começaram a chegar, então a hora é essa. Não estou reclamando de vocês de forma alguma, caros leitores! A pressão ajuda! Obrigado a todos que cobram, que agradecem, que mandam correções e puxões de orelha, sua contribuição faz disso aqui um lugar melhor.

Ah, o Natal. Que loucura que é o Natal. Falar sobre oop aqui no Digitalminds sempre é divertido porque eu tenho que bolar metáforas interessantes pra que o assunto não fique chato no primeiro parágrafo.  Então, como não poderia deixar de ser, o artigo de hoje está em clima de Natal. Não, eu não estou maluco, eu sei que o Natal já passou. Mas a segunda semana de janeiro não tem muita coisa interessante pra nos oferecer, a não ser que você conte o Dia de Reis.

O DIA DE REIS! É isso! Meu Deus como pude esquecer! Muito bem, senhores, esqueçam o natal. É hora de falar de Belchior (ou Melchior), Gaspar e Baltazar.
Não, senhores, não estamos falando de Belchior, nem de Gaspar, ou mesmo de Baltazar. Estamos falando de três magos persas, sacerdotes de Zaratrustra, homens sábios que conheciam profundamente a astrologia e que vislumbraram um sinal - na noite do dia 25 de dezembro do ano 1, souberam pelo brilho da estrela de Belém que um Rei nascera. E para comprovar que a wikipedia é sempre uma diversão, veja este parágrafo:

A melhor descrição dos reis magos foi feita por São Beda, o Venerável (673-735), que no seu tratado “Excerpta et Colletanea” assim relata: “Melchior era velho de setenta anos, de cabelos e barbas brancas, tendo partido de Ur, terra dos Caldeus. Gaspar era moço, de vinte anos, robusto e partira de uma distante região montanhosa, perto do Mar Cáspio. E Baltazar era mouro, de barba cerrada e com quarenta anos, partira do Golfo Pérsico, na Arábia Feliz”.

Uau. Sábios, caldeus, Ur, Pérsia. Agora que consegui a atenção total e irrestrita de vocês, futuros reis magos da OOP, e cobri todos de Incenso e Mirra, é hora de falar de uma classe importantíssima que ficou faltando no nosso grande esquema das coisas: a venerável classe ModelCollection.

Lembram que optamos por simplificar as funcionalidades da classe Model, fazendo com que ela realizasse operações somente em registros únicos no banco de dados? Muito bem, senhores, a classe ModelCollection aparece para nos salvar nos casos em que precisamos trabalhar com conjuntos de registros. Aqui é bom repetir o aviso: esta implementação é diferente da utilizada nos frameworks que utilizam o pattern ActiveRecord, como o Cake. Nestes frameworks todas as operações com registros e conjuntos de registros são feitas pela classe Model e suas subclasses. Assim, um objeto da classe model típico destes frameworks tem funções tanto para buscar, editar, salvar e deletar um registro quanto para buscar conjuntos de registros. Isso faz com que o objeto da classe que estamos usando não tenha um mapeamento direto com os dados que estão no banco. Isso me incomoda muito, pois eu gosto de pensar no objeto como uma coisa, uma representação virtual dos dados que estão no banco. Pra mim é estranho usar o modelo para retornar uma lista de registros. A nossa implementação vai claramente mapear um registro no banco a um objeto da classe correspondente, e um conjunto de registros a uma coleção de objetos da classe correspondente. Exemplo: nossa classe Post. Teremos no banco a tabela Posts, com os campos title e text, por exemplo. Para acessar e alterar  o campo de título de um post em nosso framework vamos seguir os seguintes passos:

  1. Criar um novo objeto da classe Post fazendo $post = new Post();
  2. Carregar os dados do registro que queremos alterar com id número $id  usando $post->loadById($id);
  3. Mudar o título usando $post->setTitle("Novo Título!");
  4. Salvar o post com $post->save();
Para criar um novo post, é simples:
  1. Criar um novo objeto da classe Post fazendo $post = new Post();
  2. Mudar o título usando $post->setTitle("Título do Novo Post!");
  3. Mudar o texto usando $post->setText("Texto do Post!");
  4. Salvar o post com $post->save();
Nessa implementação vamos poder acessar as variáveis do objeto usando getters normalmente, por exemplo, $post->getTitle(); Podemos também criar vários atalhos: o construtor do Post pode receber  o Id diretamente e já carregar os dados automaticamente. Podemos implementar a interface ArrayAccess para permitr coisas como print $post['title'] sem nenhuma dificuldade. Enfim. A coisa é boa. Aguardem que vem coisa ótima por aí.
Mas até aí, nenhuma novidade. O que muda é a forma de trabalhar com conjuntos de dados. No Cake, esse trabalho seria feito por funções na classe Post que retornariam Arrays contendo vários ítens. E isso deixa tudo confuso, no final das contas. O que proponho é o seguinte:
  1. Criar um novo objeto da classe PostCollection fazendo $collection = new PostCollection();
  2. Carregar o conjunto de registros usando uma das várias funções disponíveis, por exemplo $collection->loadAll();
Muito simples. E agora temos um objeto da classe PostCollection carregado de registros, com métodos super úteis para manipular os dados, paginar, enfim, uma belezinha. E vamos implementar uma coisa sensacional: a interface iterator, disponível na fantástica biblioteca SPL, proveniente de UR, na Pérsia! Mas isso já é assunto para o próximo episódio. Até lá!


2007-12-04 02:20:34
tags: 

O Zen e a arte cavalheiresca da programação orientada a objeto (Parte 21)

Para ver os artigos anteriores desta série, clique aqui.

Olá amigos. Em nosso último encontro finalizamos nossa primeira análise das classes principais do MVC: Model, View e Controller. Sei que muitos de vocês estão ansiosos para começar a codificar mas ainda temos bastante mufa pra queimar antes de começar a digitar código php!

Bom, pra começar, um pouco da história do cinema: quem não se lembra de Sean Connery como o policial irlandês em Os Intocáveis ? E de Kevin Spacey, como o bobalhão "Verbal" em Os Suspeitos ? E de Gene Hackman como o xerife mau, muito mal de Os Imperdoáveis ? Muito bem, o que esses três papéis interpretados por estes grandes atores têm em comum? As três interpretações foram premiadas com Oscars de melhor ator coadjuvante. E por quê todo este papo cinematográfico? É que hoje vamos falar de uma classe coadjuvante que é fundamental em nosso quase-framework: nosso amigo Dispatcher.

Muito bem, como já falamos algumas vezes, nosso framework é bastante esperto: ele vai permitir que acessemos métodos de nossos controllers simplesmente interpretando uma URL. Por exemplo, para acessar o método view() do controller BlogPost para ver o post com o id 19 nossa url seria

http://nossoblog.com.br/blogpost/view/19

Ok, mas quem transforma uma coisa na outra? Quem chama o que? Como isso funciona na prática?

Senhores, eu vos apresento a classe Dispatcher, encarregada de interpretar as urls de nossa aplicação e executar os métodos apropriados nos controllers corretos. Ela também vai checar se o controller e o método requisitados realmente existem, e vai apresentar mensagens de erro em caso negativo. Por fim, a classe vai separar corretamente os parametros do request, enviando um array já certinho para o método que foi chamado.


Uma outra função muito interessante de nossa classe Dispatcher vai ser interpretar extensões; sim, amigos, vamos fazer com que a extensão definida na url defina o layout e o template a ser utilizado, seguindo uma estrutura de arquivos pré-determinada. Exemplo: se ao invés da url acima tivessemos alguma coisa como

http://nossoblog.com.br/blogpost/view/19.xml

Nosso framework, ao invés de utilizar o layout em /layouts/default/blogpost/view.php e o template /templates/default/blogpost/view.php utilizaria o layout /layouts/xml/blogpost/view.php e o template /templates/xml/blogpost/view.php. Deu pra sacar o poder? Assim vc pode criar outputs diferentes pra toda a sua aplicação sem nenhum estresse. Bonito demais. Pra colocar o sistema disponível em wap, bastaria fazer um layout e um template, no caso /templates/wml/view.php e /layouts/wml/view.php e utilizar os códigos wml ao invés de html dentro deles. Eu adoro isso.

Você acaba de perceber a beleza de uma solução MVC. A lógica fica totalmente separada da apresentação, o que faz com que seja fácil fazer apresentações totalmente diferentes com o mesmo conteúdo. Sweet!

Vamos então à descrição da classe?

Class Dispatcher


Propriedades

$url

String. Propriedade que irá conter a url requisitada.


$controller

Objeto do tipo Controller. Esta propriedade irá conter uma nova instância do controller requisitado. No nosso exemplo, BlogPost.

$method

String. Método a ser executado no controller requisitado. O método será view(), no nosso exemplo.


$parameters

Array com os parâmetros enviados na requisição via get ou post. No exemplo acima, ele teria uma entrada $parameters["default"]=>"19" já que não nomeamos este parâmetro no request. Poderíamos, se nós fossemos chatos, ter montado a url assim:

http://nossoblog.com.br/blogpost/view/id/19


e aí nosso array seria montado na forma $parameters["id"]=>"19"

O framework vai ser esperto o suficiente pra perceber quando temos só o valor ou o par nome/valor e vai resolver tudo isso pra nós.

Métodos


parseURL()

O método parseURL() interpreta uma URL segundo os padrões combinados e popula as propriedades do nosso Dispatcher. Na prática, ele vai separar os pedacinhos da url, dividindo a string em partes. Em primeiro lugar, ele remove a string relativa ao protocolo (http:// ou https://). Depois, o domínio. Ficamos então com a estrutura

controller/método/parametros.extensao

Aí fica facil separar tudo, não?


parseParams()
O método parseParams() faz o mesmo que a função acima, só que para os parâmetros enviados por get e post. Tudo o que estiver entre um ? e o ponto da extensão será considerado parâmetro get. Também será considerado parâmetro get tudo o que vier depois da action requisitada, no caso de nossos exemplos acima "19" e "id"=>"19". Tudo o que vier no $_POST vai ser incluído também em nosso array $parameters.

Repare que pra simplificar as coisas não vamos considerar a diferença entre get e post em nosso array $parameters; será igual fazer um request por get e post. Juntaremos tudo no array, sem distinção.

2007-11-22 01:44:13
tags: 

O Zen e a arte cavalheiresca da programação orientada a objeto (Parte 20)

O Zen e a arte cavalheiresca da programação orientada a objeto (Parte 20)

Para ver os artigos anteriores desta série, clique aqui.

Olá pessoALL. Estamos aqui mais uma vez pra falar de Orientação a Objetos de um jeito bem prático, utilizando o pattern MVC para exemplificar conceitos importantes. A linguagem escolhida, você provavelmente já sabe, é PHP. Então sinta-se à vontade, sente mais perto da fogueira que já vamos começar nosso papo.

Chegamos a um momento importante de nossa série. Vamos falar hoje do último vértice da tríade MVC, mais especificamente a classe View. Você lembra que deixamos a classe View por último por se tratar do conceito mais simples dos três? Muito bem, chegou a hora de ver se a coisa é tão simples quanto falamos.

Nossa classe base View terá a responsabilidade de imprimir nossas páginas HTML, juntando o conteúdo dinâmico obtido por nossos Controllers ao código que colocamos nos templates que criamos. Portanto, primeiro precisamos definir direitinho o que é um template dentro de nosso framework, já que existem inúmeras definições por aí.

Nossos templates serão páginas php especiais, contendo basicamente html, que vão ser utilizadas por nossa classe View para imprimir as páginas de nossas aplicações MVC na tela. Normalmente nestes templates vão conter todo o html básico da página e comandos PHP para imprimir as informações dinâmicas provenientes do controller, normalmente usando só algumas poucas funções do PHP como echo, print e foreach.

Um exemplo de template talvez deixe isso tudo mais claro. Imagine que já temos tudo pronto em nosso sistema de gerenciamento de blogs, e chegou a hora de fazer o template que mostra um post. O código deste template (muito simplificado, claro) seria algo assim:
<h1><?php echo $this->data['BlogPost']['title'] ?></h1>
<div class="post"><?php echo $this->data['BlogPost']['text'] ?>
</div>

Reparem que não precisamos incluir no template nenhuma informação de tipo de documento (DOCTYPE) ou mesmo a seção <head>. Nossa classe View vai colocar tudo isso automaticamente pra nós, de acordo com definições gerais que vamos colocar em outros arquivos, chamados de layouts. Os layouts são basicamente arquivos HTML sem a seção <body> do html, e servem justamente para que não precisemos ficar repetindo estas informações toda hora. A classe View vai combinar layouts e templates para construir a página. De forma simplificada, portanto, podemos dizer que nossos templates são o corpo do documento html (<body>), e os layouts são o resto. Deu pra sacar?

O que os templates têm de especial? Nada, sinceramente. Especial é o acordo que fazemos para só utilizar html e funções de apresentação dentro deles. Isso é fundamental para que não misturemos lógica (que deve estar toda no controller) e apresentação (que deve estar toda no template). Se você começar a usar outras funções que não echo, print ou foreach (ou alguma outra função de formatação, tipo printf ou number_format) dentro dos templates você vai voltar imediatamente pro inferno.

Alguns caras levaram isso tão a sério que chegaram a criar linguagens especiais só para não usar PHP dentro dos templates. O Smarty, um dos mais poderosos sistemas de template já escritos em php, faz exatamente isso.  Eles criaram tags especiais para cada uma das funções PHP de apresentação, além de terem bolado uma série de outras interessantes. Ao invés de escrever algo tipo


<h1><?php echo $nome ?></h1>


no Smarty você escreveria

 

<h1>{$nome}</h1>

Bem interessante, não? Isso força o programador a utilizar somente os comandos do Smarty, que são somente de apresentação.

Mas veja que desenvolver uma solução como essa dá bastante trabalho: no fundo, toda essa codificação especial precisa ser transformada novamente em PHP alguma hora. Os críticos dizem que isso é desnecessário e prejudica a performance, já que o PHP oferece todas essas funções nativamente. Bem, os caras do Smarty (e de outros template engines que existem por aí) contra-atacam dizendo que todos os templates são compilados e cacheados em PHP, de forma que a diferença de performance é mínima.

Nós não temos aqui a pretensão de escrever um Smarty; o caminho mais simples é usar PHP puro. Este, portanto, é o caminho que vamos seguir.

Vamos à nossa descrição genérica da classe View:

Class View
Propriedades

$data

Array associativo contendo todas as informações dinâmicas de modelos e coleções.

$url

String com a url que está sendo acessada.

$layout

String com o nome do layout a ser utilizado. O Default é nulo, o que irá fazer com que a classe View utilize um layout padrão.

$baseurl

String com a url base do site para facilitar a montagem de links e caminhos de arquivos.

 $variables

Array associativo que pode conter outras variaveis genéricas definidas pelo controller.

 

Métodos

render($action,$layout)

Imprime a ação $action usando o layout $layout. Os defaults são a ação corrente e o layout default.

setVariable($variablename,$value)

Define uma variável para uso no view.

setPageTitle

Define o título da página a ser impressa (na tag <title>)

renderError($error)

Imprime uma página de erro utilizando o layout default para erros.