terça-feira, 22 de dezembro de 2009

Skoob - A rede social de leitores do Brasil


Esta mensagem que não é exatamente sobre desenvolvimeto de software, mas achei genial a idéia da aplicação Skoob: vc monta sua estante virtual, e avalia os livros que vc já leu. É uma ferramenta interessante, que pode ajudar a fazer boas compras, baseado na opinião de outras pessoas que já leram livros de seu interesse.

Quem se cadastrar essa semana tb está concorrendo a livros: http://www.skoob.com.br/promocao/codigo/119777

Eu definitivamente me tornei entusiasta da idéia dessa rede... Depois do trabalho, passei a última hora inteira apenas navegando entre os títulos. :-P

É interessante como idéias relativamente simples e novas tecnologias dão origem a novos produtos. apesar de conhecer e consultar bastante o serviço de avaliações da Amazon, o Skoob teve um apelo diferente.

Por ser uma rede social (fácil familiarização), e por permitir uma experiência única, de vc montar sua estante, com livros já lidos e com livros que se pretende ler, me senti de fato como se estivesse numa livraria; isso além de relembrar títulos que li na infância e adolescência, que já tinha até esquecido.

Acho que tb será interessante para saber o que meus amigos/conhecidos estão lendo, assim, além de identificar títulos em comum, há a possibilidade de conhecer e se interessar pelos títulos de pessoas conhecidas.

Por ser em português, ele ataca diretamente o nicho de mercado brasileiro. Fico na torcida que o modelo de negócio deles dê certo, bem como fico na torcida que nós desenvolvedores tenhamos sempre essa criatividade de reinventar o mundo e gerar novos negócios. Ganhar $$$ fazendo aplicações úteis para a sociedade.

quarta-feira, 16 de dezembro de 2009

MVC x MVP: Resumo

No artigo Considerações Históricas sobre o padrão MVC, tradução de "MVC (Model View Controller) Design Pattern", você encontra uma série de 4 posts explicando as origens do MVC e suas variações, mostrando também como as interações entre Modelo, View e Controller são reorganizadas dando origem ao padrão MVP. Em resumo:

O MVC surgiu no início dos anos 80 e inspirou muitas frameworks na época. Cada elemento na tela possuia dois componentes: uma view, responsável pela sua exibição, e um controller, responsável por interpretar os eventos disparados pelos usuários. O controller comandava o modelo, que podia enviar notificações (indiretas) para a view poder se atualizar.

Para evitar o alto acoplamento entre o modelo e a View, surgiu o Application Model MVC, ou AM-MVC. Basicamente era uma camada intermediária, que permitia blindar melhor o modelo com relação à camada de apresentação (view e controllers).

Com o surgimento de novas tecnologias nos anos 90, o papel do controller caiu em desuso, pois os próprios widgets da view passaram a ter meios melhores de lidar com os eventos realizados pelos usuários. Além disso, em alguns casos, era conveniente o Modelo, ou o AM, poderem alterar diretamente a view, sem a necessidade de mecanismos indiretos.

Neste cenário, a IBM propôs o MVP, uma variação do MVC que seria mais adequada. Os adeptos do Smalltalk simplificaram o modelo da IBM e utilizaram no Smalltalk/Dolphin. No MVP, as responsabilidades que eram do Controller são redistribuídas entre a View e o Presenter. A View se encarrega de lidar com as entradas dos usuários, enquanto o Presenter se encarrega de interagir com o Modelo. Além disso, o Application Model do AM-MVC é substituído pelo Presenter, a quem é dado o direito de interagir diretamente com a View.

Martin Fowler catalogou o MVP na forma de dois padrões distintos:

  • Supervising Presenter: nesta forma, o presenter interpreta os eventos da View e comanda o modelo, alterando seu estado. Para atualizar sua exibição na tela de modo a refletir o novo estado do modelo, a view pode interagir diretamente com objetos do modelo, e obter os dados necessários. O modelo até pode se comunicar com a view, porém indiretamente, através de eventos, segundo o padrão Observer Synchronization.
  • Passive View: nesta forma, a view e o modelo são totalmente desacoplados, e todas as interações são realizadas por meio do Presenter. É uma abordagem que favorece o teste da aplicação, pois facilita o uso de mock views.

No novo milênio, o MVC ressurgiu com força total, quase um dogma em aplicações Web. De tão usado, seus conceitos acabaram se tornando difusos, às vezes mal interpretados, e confundidos com Arquitetura em Camadas. Leia MVC versus Camadas para entender melhor a diferença.

Uma limitação do MVC em aplicações web é a dificuldade de testar a camada de apresentação (view e controllers). O MVP parece mais adequado nestes casos, e seu uso tem sido encorajado em aplicações GWT (Google Web Toolkit).

Para concluir, a grande diferença entre MVC e MVP é a forma como o Modelo, a View e o Controller interagem, de modo a prover uma solução mais adequada, de acordo com a tecnologia utilizada e os objetivos desejados. Uma comparação mais detalhada entre o AM-MVC e o MVP/Passive view pode ser encontrada no artigo Do MVC para o MVP.

terça-feira, 15 de dezembro de 2009

Seu objeto está na sessão? Entenda o conceito Attached e Dettached Objects!

Esta manhã respondi no RioJUG uma dúvida sobre JPA que achei interessante registrar aqui no blog:

Em JPA, é interessante jogar uma luz sobre um importante conceito: attached/dettached objects. Quando um objeto persistente participa de uma sessão que ainda está ativa, ele se encontra no estado "attached". Depois que a sessão é encerrada (session.close() ou entitymanager.close()), o objeto passa para o estado "detached".

Basicamente, no estado detached, não há mais conexão com o banco, portanto nenhuma alteração realizada no objeto será efetivada no BD, e o objeto não pode mais tentar carregar informações adicionais do banco (por exemplo associações lazy que não tenham sido acessadas durante a sessão).

Estou falando isso, pois o conceito está diretamente vinculado ao papel da chamada merge:

o método recebe um objeto dettached, e retorna o objeto correspondente, vinculado à sessão. Quando o objeto dettached for uma nova entidade, o ORM em algum momento (não necessariamente no momento da chamada) irá realizar um insert no banco de dados. Caso seja uma entidade existente, é interessante obtê-lo através de uma chamada find:

Note que, pelo find, o objeto já é retornado participando da sessão. Uma vez que o objeto já participe da sessão, você pode continuar populando as propriedades desse objeto (attached.setXXX, attached.setYYY), todas as alterações feitas o objeto, DURANTE A SESSÃO serão efetivadas no banco. Resumindo: as alterações realizadas no objeto enquanto ele participa da sessão serão efetivadas no BD.

É legal fazer o seguinte teste: carregue um objeto que já esteja no BD, e realize as alterações nesse objeto. Ao final, apenas feche a sessão, sem a chamada em.merge (ela não é necessária, pois o objeto já estava attached). Logicamente dê commit na transação, caso aplicável. Depois verifique se as alterações foram efetivadas.

Por fim, o JPA fica muito mais divertido e produtivo quando entendemos melhor seu mecanismo de funcionamento, o ciclo de vida dos objetos, pois mapeamento objeto-relacional é seu conceito-base, mas seu poder é com certeza mais amplo.

Padrão MVP para Aplicações Web Cliente-Servidor

Este post é o quarto e último de uma série de traduções de artigos do blog Design Codes, que falam um pouco sobre a evolução do padrão MVC desde o final dos anos 70, até chegar aos dias de hoje, momento em que uma de suas variações - o MVP - está se popularizando, tendo inclusive sido citado como uma best practice no Google I/O 2009 para o desenvolvimento de aplicações GWT.


Clique Aqui Clique Aqui Clique Aqui


O padrão Model-View-Presenter foi publicado em 1996 por Mike Potel, para se tornar o modelo de programação da próxima geração de aplicações Java e C++ da época. A arquitetura MVP era baseada no modelo de programação MVC clássico do Smalltalk, que era o mais comum naquele tempo, e que inspirou muitas outras bibliotecas e frameworks. Potel descreveu o MVP como um modelo unificado que era adaptável a aplicações multi-camadas e a múltiplas arquiteturas cliente-servidor.

Este artigo revisa o padrão MVP proposto por Potel, focando em sua implementação em aplicações Cliente-Servidor. Use os links acima para conhecer mais sobre a evolução do MVC. Ao final do artigo, existem links para blogs que dão exemplos de implementações do padrão MVP.

MVP em aplicações Desktop

O MVP de Potel inclui seis abstrações: View, Interactor, Presenter, Command, Selection, and Model.

É possível criar uma aplicação usando todas as seis abstrações:

  • Model: serve para interagir com o banco de dados, e armazenar informações.
  • Selection: usado para definir um subconjunto de dados selecionados.
  • Command: executar operações em uma Selection.
  • View: Desenhar a aplicação na tela.
  • Interactor: lidar com os eventos do usuário e invocar o Command apropriado.
  • Presenter: criar o Model, a View, as Selections, os Commands e Interactors, e gerenciar as interações entre eles para prover as funcionalidades da aplicação.

O modelo mais simples possível é ter apenas entidades Presenter, acumulando as responsabilidades do Model, da View e do Interactor. A View pode ser adicionada em seguida, para blindar o Presenter das responsabilidades de desenhar a aplicação na tela; Um próximo passo seria adicionar Interactors para tratar os eventos do usuário; e assim por diante, os elementos podem ser incrementalmente adicionados, sob demanda, à arquitetura.

Os desenvolvedores do Smalltalk adotaram uma variação MVP/Dolphin, que contém apenas os elementos Model, View e Presenter, com o Presenter acumulando as responsabilidades do Interactor, Command e Selection, ficando encarregado de responder aos eventos dos usuários, manter uma seleção de dados, e executar operações sobre esta seleção. Mais detalhes em Do MVC para o MVP.

Colaboração

Vejamos como o MVP pode ser usado como modelo base para uma GUI simples de uma aplicação chamada "Impressão de Registros". Há apenas um caso de uso: o usuário clica no botão imprimir, e o registro selecionado é impresso.

A modelagem MVP para esta GUI terá apenas uma View para desenhar a lista de registros, e um botão Imprimir; dois Interactors - um para capturar o clique do botão, e um para capturar a seleção de um registro na lista; um Command, para enviar o registro selecionado para a impressora (Model); um Selection, para armazenar o registro selecionado, e um Presenter, para criar a View, o Command, os Interactors, e realizar as interações entre cada entidade.

  • O usuário seleciona "Record-1"
  • o Interactor da lista captura o clique e faz a Selection apontar para Record-1
  • A view é atualizada para destacar o registro selecionado
  • O usuário clica no Botão Imprimir
  • O Interactor do Botão intercepta o clique e executa o PrintCommand
  • O Command verifica a Selection atual e envia para a impressora

MVP em aplicações cliente/servidor

No artigo descrevendo MVP, Potel sentencia que uma vasta gama de arquiteturas cliente-servidor podem ser modeladas através de variações simples do modelo MVP: em aplicações "fat-client", onde consultas SQL geradas no próprio cliente são mandadas através da rede; e em aplicações "thin-client", onde comandos são mandados para o servidor, e este atua sobre os comandos, para gerar o SQL apropriado.

Fatorando o Modelo

Uma forma de implementar a arquitetura MVP em aplicações Cliente-Servidor é dividir o modelo entre ao lado cliente e o lado servidor. Ou seja, haverão classes, em ambos os lados, representando um mesmo modelo conceitual. As entidades MVP residiriam todas no lado cliente, e o Modelo em si no servidor. Os modelos no lado cliente agem como proxies para os modelos reais que residem no lado servidor, nos quais ocorre a interação com os bancos de dados, e outras interações da lógica de negócio.

Nas aplicações atuais, o Interactor não mais é necessário, desde que os próprios Widgets sejam capazes de lidar com os eventos dos usuários (leia mais a respeito no tópico "problema da aplicabilidade" neste artigo). Já o Command e o Selection podem ser encapsulados no Presenter. Esta abordagem MVP simplificada é comum em arquiteturas de aplicações rich-client.

Em muitas aplicações de TI, o modelo reside inteiramente no lado cliente, enquanto o servidor é meramente um Sistema Gerenciador de Banco de Dados (SGBD), ou um web-service que encapsula o banco de dados.


Fatorando o Presenter

Outra forma de implementar uma arquitetura MVP em aplicações cliente-servidor é dividir o Presenter entre o cliente e o servidor: em arquiteturas fat-client, a lógica de negócios reside na parte do presenter que reside no lado cliente, com um presenter mais simples rodando no servidor; Em arquiteturas thin-client, a parte simples do presenter fica no lado cliente, enquanto o presenter no lado servidor lida com a lógica de negócios.

Em aplicações cliente-servidor onde o presenter onde o Interactor reside no lado cliente e o Command no lado servidor, o Interactor não tem como se comunicar diretamente com o Command. Neste caso, o interactor captura os eventos do usuário, e delega à contraparte cliente do presenter para lidar com eles. O presenter no lado cliente interpreta o evento e envia uma mensagem apropriada para sua contraparte no servidor, e esta sim executa o Command.

Exemplos de Aplicação MVP

Links Relacionados

domingo, 13 de dezembro de 2009

MVC em Aplicações Web

Este post é o terceiro de uma série de traduções de artigos do blog Design Codes, que falam um pouco sobre a evolução do padrão MVC desde o final dos anos 70, até chegar aos dias de hoje, momento em que uma de suas variações - o MVP - está se popularizando, tendo inclusive sido citado como uma best practice no Google I/O 2009 para o desenvolvimento de aplicações GWT.


Clique Aqui Clique Aqui Clique Aqui


Mesmo o MVC tendo sido projetado como uma framework para aplicações desktop, há 3 décadas, ele se encaixou bem em aplicações web, as quais requerem mapeamento de URLs e/ou direcionamento de mensagens HTTP (request/response). Este artigo resume o padrão MVC usado em aplicações Web.

MVC para aplicações Desktop

O padrão MVC foi introduzido no começo dos anos 80, como um framework para aplicações desktop. À época, os widgets não tinham ainda a habilidade de processar as entradas dos usuários (cliques de mouse e digitação no teclado). O MVC introduziu a entidade Controller, para agir como uma ponte entre o usuário e a aplicação. No final dos anos 80, os widgets passaram a ter suporte nativo para processamento de entradas dos usuários, tornando obsoleto o papel do controller, e o MVC deu lugar ao MVP. Mesmo que o Presenter seja comumente citado como Controller, o autêntico controller permaneceu nas sobras até o surgimento das aplicações Web baseadas em navegadores (browsers)

Web MVC

Em aplicações Web, no lado cliente, o usuário interage com o navegador, gerando requests HTTP (sejam POST ou GET) e enviam para o servidor. O servidor processa o request (extrai os dadoa da URL ou do corpo do request), executa alguma operação do negócio, e cria a resposta (uma página HTML), a qual é enviada de volta para o cliente para ser renderizada no navegador.

Colaboração

Componentes MVC residem no servidor: o modelo guarda os dados e lida com a lógica de negócios (como sempre), a view gera a resposta (página HTML) de acordo com os dados do modelo, e o controller processa as entradas dos usuários presentes nos requests HTTP, comanda o modelo - alterando o seu estado - e cria a view apropriada para ser reenviada para o cliente e renderizada na tela.

Interação

A UI mostrada abaixo tem um único use case. O usuário clica no botão "votar". No banco de dados, a coluna que salva a quantidade de votos é incrementada, e a caixa de texto é atualizada com o valor atual do número de votos.

A modelagem MVC para esta UI terá um objeto view, para gerar o HTML mostrado acima, um objeto de modelo, para incrementar e armazenar a contagem de votos, e um controller para processar o pedido "post" iniciado quando o usuário clica no botão "votar". O controller comanda o modelo, e cria a view para a geração da resposta, retornando-a para o cliente.

Nota do tradutor: Numa aplicação web escrita em Java, tipicamente é um servlet, que interage com a camada de negócios, e em seguida delega a renderização da página a um JSP (um tipo especial de servlet). A view, neste caso, tem acesso direto aos objetos do modelo que o servlet salvar como atributos da sessão do usuário. As frameworks web atuais costumam usar uma abordagem parecida com o AM-MVC, com uma camada de aplicação, que tem a finalidade de blindar os objetos de negócio dos detalhes da view, ainda que a view tenha acesso direto a esses objetos.

Do MVC para o MVP (Model-View-Presenter)

Este post é o segundo de uma série de traduções de artigos do blog Design Codes, que falam um pouco sobre a evolução do padrão MVC desde o final dos anos 70, até chegar aos dias de hoje, momento em que uma de suas variações - o MVP - está se popularizando, tendo inclusive sido citado como uma best practice no Google I/O 2009 para o desenvolvimento de aplicações GWT.


Clique Aqui Clique Aqui Clique Aqui


Neste artigo, iremos olhar mais de perto o design pattern MVP (Model-View-Presenter) e revisar a forma na qual ele evoluiu desde seus predecessores no Smalltalk ( "MVC Clássico" e "Application Model MVC"). Espero que, depois desta revisão, sejamos capazes de distinguir o MVP do MVC e endender o porquê de a discussão MVC x MVP deixar tanta gente confusa.

Abstract

O primeiro artigo sobre MVP foi pulicado em 1996 por 'Mike Potel' e declarado como a nova geração do modelo de programação C++ e Java da IBM. Era construído com base no modelo de programação do MVC Clássico do Smalltalk, com o intuito de se adaptar melhor aos novos sistemas que foram introduzidos na época e para suportar aplicações cliente-servidor (Leia mais em Web MVC).

O Smalltalk, que duas décadas antes inspirou muitas frameworks e bibliotecas com o MVC, buscava por uma nova arquitetura para a sua próxima geração de frameworks, chamada "Dolphin Smalltalk". Eles analisaram o 'Application Model MVC' (a partir de agora referenciado, neste artigo, por AM-MVC), que era a variação mais recente do MVC, e concluiram que ele não atenderia às suas necessidades. Continuaram procurando, até descobrirem o artigo da IBM descrevendo o MVP, muito similar ao AM-MVC, mas com a guinada certa que o tornava mais adequado ao que estavam procurando. Eles o lapidaram ainda mais, e chegaram a um MVP simplificado.

O modelo MVP do Dolphin Smalltalk ainda atende bem às frameworks atuais e tem sido usado como arquitetura base para aplicações Rich-Client e Thin-Client do .Net Framework (nota do tradutor: no Google I/O 2009, o MVP foi apresentado como uma best-practice para usar com o GWT). Leia mais em Web MVP.

Em 2006, Martin Fowler renomeou o "Dolphin MVP" para Supervising Controller, dando um passo atrás em direção ao AM-MVC, e apresentou uma outra variação do MVP chamada Passive View.

Este artigo foca no MVP/Dolphin e não no MVP/IBM por dois motivos:

  • Primeiro, por ser uma tendência atual. O MVP/IBM possui raizes extras como Interactor, Command e Selection desnecessários nos dias de hoje.
  • Segundo, este artigo tenta distinguir o MVP do MVC, apresentando-os lado a lado, e explicando a forma como o MVP evoluiu a partir do MVC.

Os adeptos do Dolphin apresentam o MVP como uma evolução do AM-MVC, onde o Presenter substitui o Application Model; A IBM, por sua vez, apresenta o MVP como uma evolução do MVC Clássico, onde o Presenter substitui o Controller. De fato, o MVP/IBM é o original, mas a abordagem do Dolphin é mais atraente e fácil de explicar, uma vez que o AM-MVC é muito mais próximo do MVP do que o Controller do MVC Clássico.

O Conceito MVC

A idéia por trás do MVC é fazer uma clara separação entre os objetos do domínio (model) e os objetos da apresentação (view/controller). Ele utiliza o mecanismo Observer-Synchronization para suportar múltiplas apresentações (observadores) de um mesmo objeto de domínio (observável), e para manter os objetos de domínio completamente desacoplados dos seus respectivos objetos de apresentação.

Este conceito foi largamente experimentado nas últimas três décadas, de forma que mesmo com as significativas melhorias que o MVP introduz, no final das contas o conceito continua o mesmo.

O MVC Clássico e o 'Application Model' MVC (AM-MVC)

Antes de continuar, é interessante ler sobre os predecessores do MVP, no primeiro artigo desta série, que fala sobre o Padrão MVC. Em resumo: Tanto no 'MVC Clássico' como no 'AM-MVC', a view é um widget da tela, e cada view tem como par um controller, que processa as entradas do usuário (por exemplo cliques de mouse e digitação no teclado), e comanda o modelo conforme apropriado. O AM-MVC introduz o 'Application Model', que age como uma classe intermediária entre o modelo e a apresentação (view/controllers), de forma a proteger o modelo de detalhes da apresentação.

O que há de errado com o AM-MVC?

Os adeptos do Dolphin Smalltalk esbarraram com o fato de que o AM-MVC não seria mais aplicável, e que ele tinha alguns problemas de usabilidade.

  • Não era mais aplicável, pois não mais havia necessidade por um controller para escutar aos eventos do mouse e teclado. Isso ocorreu após sistemas operacionais como o MS Windows diponibilizassem conjuntos nativos de widgets com os quais a UI podia ser construída. Os próprios widgets capturavam os eventos de mouse e teclado, tornando o controller desnecessário.
  • O maior problema de usabilidade era que, apesar do AM ser responsável por lógica da view, ele não podia acessá-la diretamente: Se por um lado, o AM deveria estar desacoplado da view através do mecanismo Observer Synchronization, ao mesmo tempo ele é responsável por funcionalidades da view, por exemplo alterar cores para destacar erros de validação, habilitar e desabilitar widgets, etc. Por isso, ele às vezes precisa ter acesso à view, o que rompe o padrão observer.

MVP - A guinada

Como mencionado acima, o MVC não é para ser meramente substituído. Podemos notar que o problema do MVC não está em seu conceito, e sim no papel que suas entidades (model-view-controller) desempenham. Ao invés de substituí-lo, o Dolphin deu uma guinada de 60° para o MVP.

Entidades

View é um conjunto de widgets, que respondem às ações dos usuários e deixando para o presenter o tratamento desses eventos. Seu propósito é exibir dados do modelo.

Presenter se encarrega da lógica de apresentação, comanda o modelo e altera a apresentação de acordo com as regras da aplicação. É altamente acoplada com a View

Model são os objetos de negócio que representam o domínio do problema, armazenando os dados, interagindo com o banco de dados e serviços externos, lidando com a lógica de negócio. É totalmente ignorante da UI (portanto desacoplada do view/presenter).

Colaboração

Como mencionado antes, o MVP/Dolphin se assemelha muito ao Supervising Controller.

  • User triggered action: a view delega as entradas do usuário para o presenter;
  • Update display: o presenter executa alguma operação relacionada a UI, alterando seu estado interno, e a atualizando diretamente
  • Command: o presenter comanda o modelo da forma apropriada
  • Event (State Changed): o modelo atualiza o seu estado e dispara um evento apropriado
  • A view trata o evento - e (Query) pega os dados relevantes diretamente do modelo, para poder atualizar sua exibição.

Dando um passo atrás em direção ao AM-MVC, temos o padrão Passive View

  • User triggered action: a view delega as entradas do usuário para o presenter;
  • Update display: o presenter executa alguma operação relacionada a UI, alterando seu estado interno, e a atualizando diretamente
  • Command: o presenter comanda o modelo da forma apropriada
  • Event (State Changed): o modelo atualiza o seu estado e dispara um evento apropriado
  • O presenter trata o evento - ele obtém os dados relevantes do modelo, e atualiza a exibição da view.

Revisitando os problemas do MVC

  • O Dolphin resolveu o problema da aplicabilidade do controller, movendo suas responsabilidades para a view e para o presenter. A view ficou com a responsabilidade de responder aos eventos de mouse e teclado (entradas do usuário), enquanto o presenter ficou com a responsabilidade de comandar o modelo quando apropriado.
  • O Dolphin resolveou o problema da usabilidade, substituindo o Application Model pelo Presenter, o qual tem permissão para se comunicar diretamente com a view quando necessário.

Note que a view no MVP/Dolphin (Supervising Controller) se comunica com o modelo e se registra com seus eventos, enquanto no AM-MVC toda interação entre a view e o model é intermediada pelo 'Application Model'. O padrão Passive View (Fowler) é descrito de forma semelhante, com a intermediação view/model sendo feita através do presenter.

Interação

Vamos colocar os conceitos em prática, revisando a GUI de controle de volume que usamos na primeira parte desta série.

Relembrando: o usuário clica no botão "aumentar volume", e em caso de sucesso, o volume é aumentado em uma unidade, e a caixa de texto exibe o valor do volume atualizado. Se o volume exceder 12, a cor da caixa de texto deve ser mudada para vermelho.

O diagrama abaixo mostra como o MVP (Passive View) lida com este caso de uso.

Vimos na primeira partedesta série que o Application Model (AM) lidava com as regras lógicas (alterar a cor) já que a lógica de apresentação fazia parte de suas responsabilidades, e vimos também que o fato de o AM não poder mudar a cor da caixa de texto diretamente levava a um código deselegante, com o AM tendo que disparar um evento para o caixa de texto.

No MVP, o presenter se encarrega da lógica de apresentação, sendo o único a ter que lidar com a regra: agora, o presenter pode mudara cor da caixa de texto diretamente, e nenhum código adicional é necessário.

Outra coisa a se notar é que o presenter responde ao evento "VolumeChanged" e delega este evento para a view, ao invés de alterar diretamente a caixa de texto para atualizar o valor do volume. A razão para mostrar isso é estressar o fato de que nem todas as atualizações precisam ser iniciadas diretamente pelo presenter. Este deve acessar a view diretammente apenas para comportamentos que não se encaixem no padrão Observer Synchronization (foi o caso da mudança de cor de um widget específico, como resposta a uma lógica da aplicação).

MVC x MVP

Há dois MVC's - MVC Clássico e Application Model MVC, assim como dois MVP's - Supervising Controller and Passive View.

Desta forma, há 4 tipos de combinações MVC x MVP:

1) MVC Clássico x MVP PassiveView

2) MVC Clássico x MVP SupervisingController

3) AM-MVC x MVP PassiveView

4) AM-MVC x MVP SupervisingController

Se entendermos as diferenças entre o MVC Clásico e o AM-MVC; bem como as diferenças entre o MVP-PV e o MVP-SC, uma comparação entre o AM-MVC e o MVP-PV deve ser suficiente para entender as quatro variações.

AM-MVC x MVP-PV

View

No AM-MVC (assim como no MVC Clássico), todo widget é uma view. No MVP, a view é a tela em si. A view do MVP é uma composição de widgets, agrupados em um container de widgets, onde a tela em si é um agrupamento de widgets ou de containers de widgets.

Presenter

O ApplicationModel no AM-MVC e o presenter no MVP-PV são ambos responsáveis pela lógica de apresentação, e ambos agem como classes intermediárias entre o modelo e a view. A grande diferença é que o presenter é altamente acoplado à view, podendo modificá-la diretamente, enquanto o AM é pouco acoplado à view, devendo se comunicar com ela somente através de eventos.

olhando de relance, pode parecer que o AM-MVC foi fracamente projetado. Se o AM está encarregado da lógica de apresentação, como pode estar pouco acoplado à view?? A razão disso é que o AM lidava com um número indeterminado de pares view-controller, então o comportamento padrão era agir como um observer - assim ele não tinha que ser alterado cada vez que um novo widget fosse adicionado à tela. Já o MVP lida com uma única view (a própria tela), de modo que o presenter pode ficar altamente acoplado à sua view.

Ainda que o padrão para o AM era o baixo acoplamento com relação à view, em aplicações reais frequentemente isto era burlado, e por comodidade se permitia o AM ganhar acesso às suas views, quebrando o padrão observer.

Model

O modelo desempenha o mesmo papel em ambas as abordagens, uma vez que ele está desacoplado das responsabilidades de apresentação.

Ainda confuso?

O principal motivo para tanta confusão é o uso exagerado e às vezes errôneo do termo controller.

Algums pessoas não levam em consideração o fato que o Controller MVC versa sobre processar a entrada do usuário e traduzí-la em comandos do modelo. Ele não versa sobre mediar a comunicação entre o modelo e a view. Este equívoco é a razão pela qual o controller é frequentemente referido como 'Application Model' (ou 'Application Layer'), da mesma forma como o MVP é usado, porém sob a denominação de MVC.

É importante entender que a principal função do controller é interceptar a entrada do usuário, outras funções como comandar o modelo e alterar a view são apenas subprodutos desta função principal; da mesma forma, a função do presenter é lidar com lógica da aplicação, e funções como interpretar eventos são subprodutos de sua função principal.

Com a introdução das aplicações web, a necessidade de processar entradas do usuário ressurgiu. Agora, as entradas são enviadas para o servidor na forma de pedidos HTTP, e o controller ressurge na forma de novos padrões:

  • Application Controller: lida com aspectos relacionados ao fluxo entre páginas (page flow);
  • Page Controller lida com pedidos para uma página específica; e
  • Front Controller lida com todos os pedidos de uma aplicação Web.

MVP ou não MVP? Eis a questão!

Em sistemas que possuiem um domain model complexo, um application model e muitas views que espelham os dados do modelo, pode fazer sentido pagar o preço do MVP (aprender o padrão, analisar o design, etc). Ao escolher o MVP, devemos nos responder se é possível separar o presenter da view. Algumas perguntas podem ajudar na decisão:

  1. Espera-se que o sistema tenha suporte tanto para aplicações desktop como web? Neste caso, faz todo sentido usar um Presenter para interagir com os dois tipos de view.
  2. O sistema tem muitas views requerindo mais de um presenter? Neste caso, é mais racional criar uma única view, com vários presenters, que podem ser substituídos "on-the-fly".
  3. Quando não for suficiente testar somente o domain model e quisermos testar também o Application Model.

Caso as considerações acima não sejam aplicáveis, o presenter pode ser embutido na própria View. Também depende do ponto de vista do desenvolvedor.

Para alguns, o MVP melhora a usabilidade, a leitura e a facilidade de busca / navegação no código da aplicação. É mais fácil entender métodos "PlayButtonClicked" no presenter do que onPlayButtonClicked na view. Também é cômodo fazer a view lidar apenas com questões de visualização, deixando o presenter lidar com a maior parte da lógica de reagir aos eventos do modelo.

sábado, 12 de dezembro de 2009

Considerações Históricas sobre o Padrão MVC

Este post é o primeiro de uma série de traduções de artigos do blog Design Codes, que falam um pouco sobre a evolução do padrão MVC desde o final dos anos 70, até chegar aos dias de hoje, momento em que uma de suas variações - o MVP - está se popularizando, tendo inclusive sido citado como uma best practice no Google I/O 2009 para o desenvolvimento de aplicações GWT

A História do Padrão MVC (Model View Controller)

Neste artigo, caminharemos nas duas primeiras fases da evolução do MVC. Iremos revisar o padrão MVC Clássico desenvolvido por Trygve Reenskaug para o Smalltalk no final dos anos 70, e ver como ele evoluiu para o Application Model MVC (a partir de agora referenciado, neste artigo, por AM-MVC; uma tradução conveniente seria MVC com Modelo de Aplicação).

Clique Aqui Clique Aqui Clique Aqui


O Conceito MVC

A idéia por trás do MVC é fazer uma clara separação entre os objetos do domínio (model) e os objetos da apresentação (view/controller). Ele introduz o mecanismo "Observer-Synchronization" para
  • suportar múltiplas apresentações (observadores) de um mesmo conjunto de objetos de domínio (observáveis); e
  • para manter os objetos de domínio completamente desacoplados dos seus respectivos objetos de apresentação.
Este conceito foi largamente experimentado nas últimas três décadas, e mesmo com algumas significativas melhorias que foram introduzidas com o MVP, no final das contas o conceito permanece inalterado.

MVC Clássico

View

Os componentes view são os widgets da tela, podem ser botões, labels, etc. Seu propósito é exibir dados do modelo (usualmente um de seus campos). No MVC Clássico, componentes view se comunicam diretamente com o modelo.
Tenha em mente que estamos falando de widgets dos anos 80, não importando se eles eram clicados por mouse ou pressionados através do teclado.

Controller

Um controller é a entidade lógica que sempre tem uma view (widget) como par. Ele processa a entrada vinda dos dispositivos como teclado e mouse, e comanda o modelo conforme apropriado. O controller também se comunica diretamente com o modelo.

Model

O modelo diz respeito aos objetos de negócio que representam o domínio do problema. Geralmente, estes objetos armazenam dados, interagem com bancos de dados e serviços, e lidam com a lógica do negócio. Eles são completamente ignorantes com relação à UI, portanto eles não sabem absolutamente nada sobre a view nem sobre o controller.

Colaboração

O diagrama abaixo mostra como os objetos MVC colaboram entre si. Os objetos no diagrama representam uma UI bem simples, que possui apenas dois widgets (representados pelos pares "view widget"/controller) e um objeto de negócio (representado pelo modelo).
  • O controller recebe as entradas do usuário,
  • Se a entrada for relevante para o seu par widget (view), ele comanda o modelo,
  • O modelo executa alguma operação, altera seu estado, e dispara um evento.
  • O widget (view) responde ao evento, pega os dados relevantes do modelo, e altera a sua exibição.
image_thumb9
O que não é mostrado na figura:
  • A view está acoplada a um método do modelo, de forma que ela possa obter os dados toda vez que houver alguma alteração.
  • O controller e a view podem se comunicar diretamente um com o outro.

    • Nota do tradutor: A view, portanto, está acoplada ao controller, e o controller à view.

Interação

A GUI controle de volume mostrada abaixo tem um único caso de uso.
  • Gatilho: O usuário clica no botão "Aumentar Volume"
  • Cenário Normal: O volume é aumentado em uma unidade e a caixa de texto é atualizada de modo a exibir o volume atual.
A modelagem para esta GUI terá um par view-controller para cada elemento visual (botão, caixa de texto, label) e um objeto modelo para memorizar o volume. O diagrama abaixo mostra como os objetos MVC interagem.

Application Model MVC (AM-MVC ou MVC com Modelo de Aplicação)

À medida que o Smalltalk evoluia, uma nova entidade denominada 'Application Model' (AM ou modelo da aplicação) foi adicionada de modo a proteger o modelo e a view das responsabilidades da lógica de apresentação. O AM age como uma classe intermediária entre o modelo e os objetos da apresentação (view/controller), que deixam de acessar diretamente o modelo (domínio). Ao invés disso, eles acessam o modelo da aplicação (AM), e se registram para os eventos do AM.

Colaboração

  • O controller recebe as entradas do usuário,
  • Se a entrada for relevante para o seu widget, ele comanda o AM,
  • O AM por sua vez, executa alguma operação relacionada à UI, alterando o estado interno da mesma,
  • O AM comanda o modelo, o qual executa alguma operação de negócio, altera seu estado e dispara algum evento
  • O AM responde ao evento do modelo, propagando-o para o widget.
  • O widget responde ao evento do AM, peg os dados relevantes no AM e alterando a sua vizualização

Pra que a mudança?

Vimos que o MVC clássico funciona bem com a GUI do "Controle de Volume". O problema é que na vida real, a UI provavelmete terá casos de uso mais complexos. Para ilustrar, adicionemos um caso de uso a esta GUI: se o volume exceder 12 unidades, então a cor da caixa de texto deve mudar para vermelho. A questão é: quem deve ser responsável por este pedaço da lógica de apresentação?
Podemos empurrar isto para o modelo, de modo que dele dispare algum evento especial quando o limite exceder 12 (mas isso parece inadequado). Podemos extender o widget da caixa de texto, de modo que ela verifique o seu valor e, quando aplicável, altere sua própria cor. Soa melhor, porém ainda é preferível manter os widgets genéricos e ignorantes de qualquer tipo de lógica.
Exatamente por este tipo de situações que o AM foi inventado.
O diagrama abaixo mostra como o AM lida com a nova regra.

  • O usuário clica no botão "Aumentar Volume",
  • O controller comanda o AM para aumentar o volume,
  • O AM delega o comando para o modelo,
  • O modelo comanda o auto-falante e dispara o evento "DataChanged",
  • AM respond and delegate the event to the widget,
  • O widget verifica com o AM qual o volume atual,
  • O AM checa o volume com o modelo, retornando o valor para o widget, que atualiza seu estado.
  • O AM checa se o valor é maior que 12, neste caso dispara um evento ColorChanged, ao qual o widget responde alterando sua cor.
Um benefício de usar o AM é que ele pode armazenar dados da view, como a cor atual do widget, qual item em uma lista está atualmente selecionado, quais widgets estão habilitados ou desabilitados, e assim por diante.

Qual o problema com o AM?

O AM possui uma limitação: ele não tem permissão para manipular sua view diretamente, NÃO pode simplesmente mudar a cor da caixa de texto, ao invés disso, precisa disparar o evento "ColorChanged", e deixar que o widget responda ao evento.
O próximo post, 'Do MVC para o MVP', será mostrado como o Padrão MVP soluciona este problema.

quinta-feira, 10 de dezembro de 2009

Anunciado o lançamento oficial do GWT 2.0

Google anuncia em 08/dez/2009 o aguardado lançamento do GWT 2.0: http://googlewebtoolkit.blogspot.com/2009/12/introducing-google-web-toolkit-20-now.html
Tradução para o português, por Fábio Miranda:

Introduzindo o SpeedTracer

Speed Tracer é uma nova e poderosa ferramenta de análise de performance para o Chrome que dá a você insights sem precedentes sobre o funcionamento interno de qualquer aplicação web - não apenas aquelas criadas com o GWT. Quer descobrir porque sua aplicação parece uma lesma? SpeedTracer pode ajudá-lo a encontrar a resposta. Veja uma demonstração no video:

(Ah, o SpeedTracer foi desenvolvido com o GWT!)

O que há de novo?

Este video destaca algumas das novas funcionalidades bacanas trazidas pelo GWT 2.0:

Abaixo são descritos em detalhes algumas das novidades do novo GWT SDK (kit de desenvolvimento GWT) e do novo GPE (Google Plugin para o Eclipse).

Mais eficiência com o novo Modo de Desenvolvimento e Compilações Intermediárias

  • GWT 2.0 melhora enormemente o cliclo de edição/atualização ao introduzir um novo método de depuração chamado modo de desenvolvimento, que permite que um projeto seja debugado com o navegador de sua escolha - sim, o seu navegador, e não um imposto pelo GWT! O modo de desenvolvimento conta com um novo plugin para o navegador, chamado Plugin de Desenvolvimento GWT, que torna possível ao projeto Java criar uma ponte de debug para qualquer navegador que tenha o plugin instalado. Detalhes técnicos à parte, esta nova forma de debugar simplesmente parece melhor.
  • Ao colocar a experiência de debug diretamente no navegador, o modo de desenvolvimento torna possível usar as ferramentas de desenvolvimento próprias de cada navegador, em combinação com o debug do Java. Ou seja, agora você pode debugar código Java no Firefox, enquanto simultaneamente usa grandes ferramentas como o Firebug, para visualizar as estruturas DOM e experimentos com CSS. O mesmo vale para o Ditto/Safari e assim por diante com os demais navegadores.
  • O novo GPE inclui suporte aperfeiçoado para iniciar e controlar o modo de desenvolvimento, incluindo uma nova View que registra as mensagens do logging diretamente numa Eclipse View (nota do tradutor: em versões anteriores, o log de mensagens do GWT era feito através do Hosted Mode). 
  • É possível iniciar o modo de desenvolvimento manualmente, através de uma interface Swing, que pode rodar completamente fora do contexto do eclipse, incluindo outras IDEs.
  • O modo de desenvolvimento também suporta debug em múltiplos navegadores, significando que é possível conectar a mais de um tipo de navegador em uma única sessão de debug. É uma ótima forma de rapidamente assegurar que o projeto funciona corretamente com a maioria dos navegadores, sem a necessidade de reiniciar o depurador Java.
  • E finalmente, o modo de desenvolvimento funciona através de uma rede. Por que isto é legal? Porque você pode facilmente testar os navegadores em sistemas operacionais diferentes daquele que você está desenvolvendo. Por exemplo, suponha que você esteja desenvolvendo em uma IDE no Linux. É possível, em uma simples sessão de debug, conectar através da rede para visualizar a aplicação que você está executando - e depurá-la - no Internet Explorer em um Windows, bem como no Safari em um Mac OS X, ou Chrome no Windows, ou Firefox no OS X... O ponto é que você pode desenvolver usando a plataforma de sua preferência enquanto pode debugar em cada combinação de navegador / sistema operacional que seus usuários possam ter.
  • Caso você necessite compilar frequentemente o código para Javascript - apesar de que esperamos que o modo de desenvolvimento irá reduzir dramaticamente que você tenha essa necessidade - você agora pode contar com a flag -draftCompile, a qual acelera o tempo de compilação, pois deixa de lado as otimizações de código. Para ser claro, você definitivamente não deverá mais fazer deploy de Javascript compilado, o que pode poupar tempo durante builds contínuos intermediários (que não sejam builds de produção).

Interfaces Declarativas com o UIBinder

  • GWT 2.0 introduz numa nova forma mais produtiva e poderosa de construir codigo de interfaces de usuário, chamada UIBinder. Ela facilita a separação dos aspectos visuais da UI da lógica da aplicação. Para construir a UI, simplesmente junte HTML e elementos de widgets GWT de forma declarativa em um template XML UIBinder (um arquivo na forma *.ui.xml). Você pode então colocar a lógica da aplicação em um arquivo *.java correspondente.
  • UIBinder torna muito mais fácil envolver designers web mais diretamente no workflow de desenvolvimento. Por exemplo, desenvolvedores podem copiar e colar mocks HTML fornecidos por um web designer. 
  • Nós também temos notado que alguns designers web gostam de editar templates UIBinder diretamente e usar o ciclo de edição/atualização rápido do modo de desenvolvimento à medida que eles fazem experiências com a UI. É bem mais divertido modelar usando widgets interativos do que mocks HTML passivos.
  • Incorporar seus próprios widgets customizados em um template UIBinder é exatamente igual a usar quaisquer um dos widgets nativos do GWT.
  • UIBinder também ajuda a prevenir bugs subtos, como erros de digitação dos IDs, de colocar em produção código com bugs, ao validar em tempo de compilação todas as referências cruzadas entre os pares de arquivo .ui.xml e o .java.
  • O GPE agora vem com wizards UIBinder, auto-completar de código, e refactoring para ajudar a usar o UIBinder de forma eficiente.

Visual perfeito ("pixel-perfect") com painéis de layout

  • Alcançar o visual (look and feel) exatamente do jeito que você deseja é algo que tradicionalmente requer artemanhas e truques com HTML e CSS. Antes de GWT 2.0, mesmo os widgets GWT não eram capazes de abstrair totalmente algumas das dores de cabeça relacionadas a layout que poderiam ocorrer. Porém, GWT 2.0 introduz novos painéis de layout que realmente tornam possível criar o layout que você deseja. A aplicação de exemplo "Mail" no GWT SDK foi atualizada de modo a mostrar como isto funciona.
  • Painéis de layout criam um sistema de layout no topo do CSS padrão. Devido ao fato que eles trabalham sobre o CSS (e não ignorando o CSS), painéis de layout continuam a funcionar de forma previsível mesmo na presença de estilos CSS customizados que você necessite incluir. E devido ao fato que layout baseado em CSS é tratado nativamente dentro da engine de renderização do navegador, nenhum JavaScript precisa ser executado durante o layout. Como resultado, o layout é mais rápido e fluido: você poderá notar especialmente a velocidade ao redimensionar a janela do navegador.
  • Como é de se esperar, painéis de layout funcionam especialmente bem com o UIBinder. Com apenas algumas linhas de XML, você consegue criar layouts realmente sofisticados, incluindo transições animadas, divisórias, e assim por diante.

Melhorias de Código

  • Uma nova funcionalidade chave no GWT 2.0 é o code-splitting guiado pelo desenvolvedor. É muito mais fácil explicar com uma analogia: quando você está assitindo a um filme online, você definitivamente não deseja baixar o filme inteiro antes de começar a assitílo, certo? Você quer que o filme inicie instantaneamente, e o resto pode ser baixado aos poucos. O mesmo é válido para aplicações web. Iniciar aplicações não deve fazer você sentir como se estivesse instalando alguma coisa. Você apenas quer ver as coisas acontecerem ao clicar em um hyperlink. Cod Splitting é uma ferramenta que pode ajudá-lo a obter exatamente isso.
  • Pode soar complicado, mas code splitting é na verdade fácil. Apenas encontre um local em sua aplicação onde você gostaria de retalhar parte do código e usar o novo e mágico método GWT.runAsync() para criar um split point. Ao ir continuamente adicionando split points toda vez que isto fizer sentido, você pode de forma fácil e segura retalhar a sua aplicação de forma a assegurar que o download inicial inclua somente a quantidade mínima de código necessária para iniciar a aplicação.  O compilador automaticamente organiza os fragmentos de código adicional para serem baixados mais tarde. E, ao invés de manualmente dividir o código "na unha" através de declarações <script> o GWT automaticamente faz o trabalho pesado de assegurar que todas as dependências necessárias sejam baixadas na ordem correta. Como exemplo, a aplicação Showcase incluída no GWT SDK foi atualizada para mostrar o funcionamento do code splitting.
  • Além de code splitting, há também melhorias fundamentais à habilidade do compilador em produzir JavaScript. Adicionamos otimizações para tornar o código compilado menor e mais rápido. se você já possui projetos GWT existentes, pode experimentar grandes benefícios simplesmente fazendo um upgrade, recompilando, e provando que suas aplicações iniciam e executam mais rápido. GWT 2.0 inclui otimizações de grande impacto, e já vimos reduções no tamanho do JavaScript compactado variando entre 3% e mais de 20%.

Otimização de recursos com o ClientBundle

  • ClientBundle extende e generaliza o padrão de agrupamento de recursos que foi criado com o ImageBundle no GWT 1.4. Com uma simples interface Java, você pode em tempo de compilação otimizar e embutir recursos diretamente em código JavaScript compilado. Por exemplo, ao embutir imagens, você consegue evitar ineficientes round-trips de HTTP. ClientBundle prov~e suporte a uma ampla variedade de recursos, incluindo imagens, texto e até mesmo CSS>
  • Você ouviu bem , CSS. CssResource é uma nova e poderosa framework para gerenciar estilos. Suporta uma nova sintaxe extendida de CSS para definir sprites, named constraints, escopos, seletores condicionais e assim por diante. Ela também otimiza agressivamente seu CSS ao remover comentários e espaços em branco, e ao obfuscar nomes de classes.
  • ClientBundle é também extensível. Você pode criar seus próprios tipos de recursos e enganchá-los para dentro da framework ClientBundle para manter suas interfaces de recursos coerentes enquando agrupa quaisquer tipos de recurso que você considerar conveniente.

Agradecimentos Especiais

GWT 2.0 é o ápice de muitos meses de desenvolvimento colaborativo, e em nome de todo o time GWT, nós gostaríamos de agradecer ao time do WebKit, com os quais trabalhamos de perto e adicionamos a instrumentação de baixo nível necessária para tornar possível o Speed Tracer.
E, como sempre, profundos agradecimentos à comunidade de desenvolvedores GWT, muitos dos quais bravamente baixaram o GWT diretamente do trunk SVN, testando as novas funcionalidades ainda em progresso, e auxiliando a encontrar bugs, e até mesmo submetendo correções para os bugs que encontravam

Get Started!

Acredite ou não, nós nem chegamos perto da superfície de todos os novos recursos que o GWT oferece. Como sempre, GWT é Open Source e gratuito para utilizar. Esperamos que você dê ao menos uma olhada no GWT 2.0.
Happy Coding! Nos vemos online!

sexta-feira, 27 de novembro de 2009

Guicifique seus Testes - Parte 6

Esta é sexta parte da tradução para português do interessante artigo Guicing Up Your Testing, June 21, 2007, por Dick Wall. Trata-se de uma leitura interessante, que junta dois tópicos que estou estudando atualmente: Teste Unitário e Google Guice, além de falar um pouco sobre Injeção de Dependências e os Padrões de Projeto Singleton e Factory.

Índice

Uma rápida reflexão sobre Design Patterns

A afirmação sobre a bala de prata se aplica, em minha opinião, tanto para o uso de bibliotecas como o Guice quanto para Design Patterns. Padrões de Projeto podem encapsular conceitos importantes, mas usá-los indiscriminadamente pode levar a problemas similares aos que tivemos ao jogar todas as responsabilidades para cima do Guice.

Lembremos que a modelagem inicial do teste estava particularmente difícil de testar justamente devido a uma aplicação pouco flexível do padrão Singleton, que era difícil de ser substituída por outra mais adequada. Uando o Guice, acabamos por aplicar o mesmo padrão Singleton, mas de forma diferente, deixando claro que a culpa não era propriamente do padrão Singleton, mas sim da forma como ele havia sido implementado.

Ainda assim, não se pode tentar pensar sempre em termos de padrões de projeto, na medida que agindo dessa forma pode-se limitar bastante as possíveis abordagens de desenvolvimento. Padrões de Projeto possuem seu valor, assim como os Anti-Patterns, e ainda mais importante, a Criatividade e o Pensamento Original e Independente.

Modelagem usando Guice e o Padrão Factory

Neste exemplo, encontrei um forte exemplar de como combinar a Injeção de Dependências oferecida pelo Guice e o Padrão de Projeto Factory. Iremos usar o Guice para criar objetos FabricaPedidos, que serão injetadas como o objeto GerenciadorTaxaJuros cofigurado nos bindings do Guice. Iremos então usar esta Fábrica para criar os objetos Pedido. Ao tirar do Guice a responsabilidade de criar os objetos da classe Pedido, ela pode voltar a ter ambos os atributos Cliente e GerenciadorTaxaJuros populados via construtor, com ambos os atributos podendo ser marcados como imutáveis.

Desta forma, corrigimos os problemas que introduzimos no código ao adaptá-lo para fazer uso da Injeção de Dependências. Vamos ao código. Na classe Pedido, reescrevemos o construtor, e retiramos a anotação @Inject (já que ela não vai mais ser diretamente gerenciada pelo Guice!).

Note que restauramos a palavra reservada final ao atributo cliente, e que o construtor recebe como argumentos tanto o GerenciadorTaxaJuros como o cliente. Como o Guice não fará mais a injeção através desta classe, criamos uma FabricaPedidos, que irá fazer este trabalho:

Não devem haver surpresas até aqui: o Guice irá injetar um GerenciadorTaxaJuros via construtor da classe FabricaPedidos. Agora, para criar um pedido, iremos chamar o método criaPedidoParaCliente, passando como parâmetro o cliente deste pedido. O método criará o Pedido, usando o GerenciadorTaxaJuros injetado pelo Guice, e o Cliente informado através da chamada da API FabricaPedidos. Vejamos o que ocorre com os testes:

Ao invés de criar um Pedido no construtor, agora criamos uma Fábrica de Pedidos. No teste, esta fábrica é usada para criar objetos Pedido, para os clientes aplicáveis. Esta implementação é muito mais limpa do que a anterior, e recuperamos a imutabilidade dos atributos da classe Pedido.

Melhorias Adicionais

Outra coisa a ser notada é que, ao invés de a busca em `GerenciadorTaxaJuros` receber como parâmetro um ID de um `Cliente`, faz mais sentido receber um objeto `Cliente`, fazendo a busca baseada neste objeto. Isto torna a API mais limpa e flexível, uma vez que permitiria que a busca possa ser parametrizada também por outros atributos da classe `Cliente`, caso conveniente. Desta forma, atualizamos a interface `GerenciadorTaxaJuros`:

E sua implementação fake:

O método `calculaTotalComTaxas` da classe `Pedido` também precisa ser refatorado para receber um objeto `Cliente` ao invés de seu ID:

A Biblioteca AssistedInject

Utilizar o padrão Factory em conjunto com Injeção de Dependências solucionou as deficiências introduzidas no final do primeiro artigo, então podemos nos perguntar se o próprio Guice não poderia oferecer alguma forma de contornar estas limitações, uma vez que elas parecem ser bem comuns.

Talvez o Guice possa, em breve. Jesse Wilson e Jerome Mourits, engenheiros da Google, criaram uma extensão do Guice chamada AssistedInject, que formaliza o uso do padrão `Factory` para ser mais facilmente utilizado com o Guice. Uma descrição do que ela faz pode ser encontrada em:

  • http://groups.google.com/group/google-guice/browse_thread/thread/9a7e2c57583d21d6
  • http://code.google.com/p/google-guice/wiki/AssistedInject

Conclusão

Vimos, portanto, que a Injeção de Dependências, por mais útil que seja, é apenas mais uma ferramenta na caixa de ferramentas. Ela pode ser utilizada com outras boas práticas e padrões de projeto quando apropriado, e quando fizer sentido. Usado corretamente, ele pode tornar mais bonitas a arquitetura e a implementação de uma aplicação.

Guicifique seus Testes - Parte 5

Esta é quinta parte da tradução para português do interessante artigo Guicing Up Your Testing, June 21, 2007, por Dick Wall. Trata-se de uma leitura interessante, que junta dois tópicos que estou estudando atualmente: Teste Unitário e Google Guice, além de falar um pouco sobre Injeção de Dependências e os Padrões de Projeto Singleton e Factory.

Índice

Não há bala de Prata

Na quarta parte desta série, precisamos refatorar a classe Pedido para que ela recebesse via construtor um objeto GerenciadorTaxaJuros. Esta medida foi necessária para que o Guice pudesse realizar a injeção de dependências via construtor. Porém, como criação de objetos Pedido agora seriam controlados pelo Guice, perdemos a liberdade de fornecer ao Pedido, via construtor, o objeto Cliente com quem ele se relaciona.

Podemos ponderar se o próprio objeto cliente poderia ter sido injetado pelo Guice, mas isso não se aplica neste caso: precisamos ter a liberdade de criar pedidos para diferentes clientes (ainda neste teste, poderíamos também incluir a checagem da taxa para um segundo cliente!), portanto a informação encapsulada pela classe Cliente está fora dos limites da classe Pedido.

Usar um método setter é a forma mais prática de contornar o problema, mas venhamos e convenhamos, ela é sub-ótima. Talvez o maior problema esteja na perda de mutabilidade: um objeto pedido existente pode ter seu cliente alterado, o que não soa particularmente seguro. Olhando por este prisma, não é desejável permitirmos que o cliente mude após ele ser atribuído ao pedido.

Poderíamos incluir código condicional dentro do setter, por exemplo lançando uma exceção em caso de tentativa de alteração, mas vamos encarar: isto começa a cheirar novamente a bacalhau.

Outro risco de permitir a mutabilidade é que alguém pode agora criar um pedido e esquecer de informar o cliente. O código (deliberadamente) não está mais protegido desta circunstância, podendo falhar com uma exceção do tipo NullPointerException ao se tentar calcular o total do pedido sem ter especificado o cliente deste pedido.

Ao me deparar com o problema da mutabilidade, isso me bloqueou por algum tempo. Certamente isso degradou aquilo que era um exemplo simples e claro em algo não tão limpo. À medida que refletia sobre isso, porém, percebi que este problema tinha valor em si!

Ele trouxe à tona um ponto importante: apesar de que a injeção de dependências seja uma ferramenta fantástica para melhorar o estilo e a modelagem de código, ela não é uma bala de prata. Da mesma forma como muitas outras ferramentas que o precederam, o Guice é surpreendentemente útil e hábil, mas não é a resposta para todos os problemas (nem mesmo para a maioria dos problemas).

Eu realmente gosto de bibliotecas que não tentam ser a solução definitiva, ao invés disso elas fazem algo específico, se propondo a fazer este algo realmente bem feito. Neste ponto, o Guice é muito bem sucedido: é a solução perfeita para Injeção de Dependências em Java: é leve, simples e rápido.

Por outro lado, mesmo um exemplo de teste bem simples evidencia algumas de suas limitações. Tão logo perdemos o controle da criação do Pedido, ao delegar este controle para o Guice, perdemos também o controle sobre a imutabilidade do atributo Cliente da classe Pedido.

Deve estar evidente que delegar ao Guice a criação direta de objetos Pedido foi uma decisão incorreta de modelagem. O problema de setar o cliente para um pedido merece uma boa solução, porém esta solução virá de um ângulo diferente daquele que nos é oferecido pela injeção de dependências. Ao invés disso, podemos mesclar esta abordagem com um outro Padrão de Projeto: Fábrica (Factory Design Pattern).

Guicifique seus Testes - Parte 4

Este artigo é quarta parte da tradução para português do interessante artigo Guicing Up Your Testing, June 21, 2007, por Dick Wall. Trata-se de uma leitura interessante, que junta dois tópicos que estou estudando atualmente: Teste Unitário e Google Guice, além de falar um pouco sobre Injeção de Dependências e os Padrões de Projeto Singleton e Factory.

Índice

Colocando o Guice em Ação

A última coisa que ainda falta fazer é fazer o setup do Guice na aplicação que ele irá rodar. Todas as peças estão no lugar agora, mas o Guice ainda não está sendo invocado em lugar algum.

Aqui está uma grande regra trazida por Zorzella e Sasha, da Google, e que é boa para se ter sempre em mente: Guice is the new "new" - em outras palavras, Guice é o nova forma de se usar o operador "new".

Numa aplicação Guice, não veremos mais tantas chamadas de construtores (new()). Ao criar um objeto usando new, não tem como envolver o Guice no processo de resolver as dependências para este objeto. Para fazer proveito do poder de autowiring, é necessário usar o Guice para criar os objetos. Vamos dar uma olhada no setup da classe de testes:

Note que pedimos ao Guice para nos retornar um gerenciadorTaxas baseado apenas na interface TaxRateManager?. Nosso código de testes, portanto, não possui mais nenhuma dependência direta a uma implementação concreta, as dependências são todas encapsuladas nas regras de binding dentro da classe ModuloTestarTaxaJuros?.

Mas espera lá, na classe ModuloTestarTaxaJuros? não definimos nenhum binding para a classe Pedido, então como é que o Guice sabe o que fazer na chamada abaixo?

O Guice possui uma regra especial para difinição de classes concretas. Como Pedido é uma classe (e não uma interface), o Guice consegue adivinhar que precisamos que ele crie um objeto desta classe para nós. Porém ele vai além, ele injeta no objeto as dependências explicitadas em seu construtor marcado com a anotação @Inject.

Agora podemos enfim chegar ao teste propriamente dito, que sofre uma ligeira alteração. Lembre que tiramos do construtor da classe Pedido o parâmetro Cliente e o colocamos em um setter. Portanto, precisamos agora fornecero cliente através deste método e não mais através do construtor:

Resultados

Depois de todo este trabalho, analisemos os resultados. Executando o teste original, simulando 5 segundos de latência para cada operação realizada no banco de dados, o JUnit mostra a seguinte saída:

Ao usar o Guice para injetar a implementação fake baseada em um HashMap?, obtemos um resultado bem mais razoável:

É uma melhoria de duas ordens de grandeza! Tudo bem, este é um exemplo inventado, mas a experiência real me diz que ele não é tão desconectado da realidade. eliminar dependências com operações de I/O, bancos de dados, serviços remotos, e assim por diante, causa uma melhoria dramática na velocidade dos testes unitários.

Isto significa que é possível rodar mais testes em menos tempo, e que estamos testando uma coisa de cada vez, sem se distrair com coisas que não têm relação direta e relevante com o teste propriamente dito (ao testar a lógica de cálculo de taxas, não há razão nenhuma de precisarmos obter informações necessariamente de um banco de dados). Ao invés disso, podemos simplesmente simular o comportamento destes componentes de baixo nível.

Guicifique seus Testes - Parte 3

Este artigo é terceira parte da tradução para português do interessante artigo Guicing Up Your Testing, June 21, 2007, por Dick Wall. Trata-se de uma leitura interessante, que junta dois tópicos que estou estudando atualmente: Teste Unitário e Google Guice, além de falar um pouco sobre Injeção de Dependências e os Padrões de Projeto Singleton e Factory.

Índice

Introdução ao Guice

Neste contexto é onde o Guice se encaixa perfeitamente. Ele nos dá o poder de definir uma configuração que mapeia as implementações de objetos a uma forma simples de localizá-los e obtê-los. Quando se pede um objeto para o Guice, ele não só irá procurar por aquele que foi solicitado, mas também irá percorrer o objeto procurando pelas suas dependências, e injetando os tipos corretos de dependência para o contexto aplicável. Ou seja, ele fará todo o trabalho de auto-conectar as dependências entre os objetos, eliminando a necessidade de injetá-las manualmente usando passagem de parâmetros pela aplicação.

Vejamos como é fácil começar a usar o Guice: baixe a versão mais atual desta biblioteca em http://code.google.com/p/google-guice, descompacte o arquivo, procure pelo arquivo guice-VERSION.jar, e adicione-o ao classpath de seu projeto. Feito isso, o Guice está pronto para usar. Ele nada mais é do que um conjunto de anotações para classes e uma simples API para configurar os módulos que irão conectar os objetos da maneira adequada. NA verdade ele é um pouco mais que isso, mas neste momento focarei apenas nas funcionalidades principais.

Voltando ao Exemplo

Vejamos o que o Guice pode fazer por nós. Primeiro, vamos deixar para o Guice a responsabilidade de preparar a classe Pedido toda vez que precisarmos utilizá-la. Sendo mais específico, queremos que o Guice forneça o GerenciadorTaxasJuros adequado para o contexto que a classe Pedido está sendo usada, seja em testes ou em produção:

As mudanças neste ponto incluem:

  • importar a anotação Inject; e
  • adicionar a anotação @Inject antes do construtor da classe Pedido.

Pode-se notar também que fizemos algumas regressões na modelagem, pois já não mais passamos um objeto Cliente através do Construtor - ao invés disso, agora ele deve ser injetado via um método setter. Desta forma, também é necessário retirar do atributo cliente o modificador final, pois para poder usar o setter, o atributo precisa ser mutável. Demos alguns passos para trás, mas voltaremos a falar sobre isso no final deste artigo.

Por agora, a anotação @Inject é a coisa mais importante a se notar. Ela indica para o Guice que ele é responsável pela construção de objetos dessa classe. Por mais que também seja possível criar objetos utilizando a classe da forma tradicional, iremos a partir de agora usar o Guice para fazer isso por nós, pois ele:

  1. Verifica que há uma anotação @Inject no nível do construtor.
  2. Verifica a assinatura dos tipos de parâmetros do construtor.
  3. Para cada um dos parâmetros, ele tenta encontrar um objeto adequado e injetar no objeto, durante a sua criação.
  4. Não há passo 4 (bom, na verdade há: se por algum motivo o Guice tiver problemas em resolver as dependências concretas que deve passar para algum parãmetro do construtor, ele automaticamente lança um erro que nos ajudará a rastrear e corrigir o problema).

Tudo isso parece mágica, mas não é. Para o Guice funcionar, informaremos a ele tudo o que ele precisa saber sobre como criar os objetos que irá injetar em cada parâmetro do construtor. Vamos juntar as peças.

Configuração dos Bindings (conexões entre objetos)

Primeramente, explique para o Guice o que você pretende fazer quando ele for solicitado para injetar um GerenciadorTaxasJuros em outro objeto (lembre que GerenciadorTaxasJuros é uma interface, então é necessário criar alguma de suas implementações concretas para que possa ser utilizado).

Para ensinar o Guice, criamos um módulo de configuração, com um binding dentro dele:

Pronto! Isto cria uma subclasse de Module explicando ao Guice que, quando você precisar de um GerenciadorTaxasJuros, o que você realmente quer, para propósitos de teste, é um objeto de GerenciadorTaxasJurosFake. Em produção, você pode ter um outro módulo que instrui o Guice a usar a implementação GerenciadorTaxasJurosBd.

É aqui que o "pulo do gato" de auto-conexão entre os objetos (auto-wiring) ocorre. Logicamente, em um sistema real, provavelmente haverão muito mais definições de binding, onde tudo o que é necessário é simplesmente adicionar mais chamadas binder.bind. Muitos erros encontrados em grandes sistemas que usam o Guice podem ocorrer devido à ausência de bindings, mas estes tipos de erro são reportados de uma maneira que se torna óbvio percebr quando algo está faltando.

Tem mais uma coisa: mencionamos antes que usar um único objeto GerenciadorTaxasJuros em toda a JVM seria uma boa idéia, porém fazer isso na forma de singleton estáticos é algo muito limitador (o alto acoplamento dificulta, entre outras coisas, escrevermos bons testes). Guice vem nos salvar deste embróglio provendo um "escopo" Singleton para podemos implementar os bindings. Uma forma de se fazer isso, é adicionar na configuração uma chamada ao método in(Scopes), onde Scopes é uma classe da API do Guice:

A outra forma, ainda mais prática, é utilizar a anotação @Singleton diretamente na definição da classe GerenciadorTaxasJurosFake.

Ambos possuem vantagens e desvantagens, porém é muito mais uma questão de preferência qual delas se deve escolher. Prefiro usar a anotação, pois ela expressa diretamente na classe GerenciadorTaxasJurosFake a intenção semântica de que um único objeto dela será criado para atender a todos as dependências a esta classe dentro da JVM.

Guicifique seus Testes - Parte 2

Este artigo é a segunda parte da tradução para português do interessante artigo Guicing Up Your Testing, June 21, 2007, por Dick Wall. Trata-se de uma leitura interessante, que junta dois tópicos que estou estudando atualmente: Teste Unitário e Google Guice, além de falar um pouco sobre Injeção de Dependências e os Padrões de Projeto Singleton e Factory.

Índice

Introdução à Injeção de Dependências

A solução mais simples é eliminar a dependência com o singleton. Para testar a classe Pedido, não há necessidade de realizar uma chamada real a um banco de dados, mesmo que o banco de dados seja rápido o suficiente. O que precisamos na classe GerenciadorTaxasJurosBD são dois métodos: atribuiTaxaParaCliente e verificaTaxaParaCliente. Este último usa o ID do cliente e retorna um double. Esta funcionalidade pode ser fornecida facilmente por um HashMap ao invés de ter que consultar o banco de dados, sendo a implementação com HashMap? mais rápida e leve.

A técnica de criar uma implementação de uma classe como esta, que imita o comportamento desejado para a implementação real, é conhecida como Fake Objects. É diferente da técnica Mock Objects, que veremos adiante (a técnica Mock Objects tem mais a ver com prover ao objeto um roteiro de passos que se espera que ele execute, e verificar se o roteiro foi realmente obedecido).

Voltando ao assunto, o que importa aqui é que para testar a classe Pedido e o seu cálculo para taxar uma determinada venda, não importa quem faz o trabalho de verificar qual o valor da taxa aplicável ao Cliente. Assumindo que o único trabalho de um GerenciadorTaxasJuros seja armazenar e recuperar a taxa usando o ID do cliente, podemos trabalhar com a seguinte implementação:



Há algumas coisas para observar aqui. Primeiro, esta implementação é bem simples. Mais importante, ela armazena tudo em memória, nunca chegando nem perto de um banco de dados, então ela tende a ser realmente eficaz para se utilizar em um teste unitário. Todos os dados necessários podem ser configurados no próprio teste, sem necessidade de armazenar nada no banco de dados.

A segunda coisa é que agora a classe agora implementa a interface GerenciadorTaxasJuros. Para a injeção de dependências funcionar, é necessário criar uma interface que a classe Pedido possa usar, ao invés de expor dentro dela uma implementação específica. A interface deve se parecer com o código abaixo:


Agora, na classe Pedido, podemos substituir todas as referências à classe GerenciadorTaxasJurosBD pela interface GerenciadorTaxasJuros. Isto nos traz toda sorte de benefícios além da testabilidade, que irei cobrir em futuros artigos?.

Em poucas palavras, tudo o que fizemos até aqui foi para eliminar uma dependência que o código tinha com uma implementação. Pedido agora não está mais amarrado a uma classe que o obriga a acessar o banco de dados. Ele agora se comunica através de uma interface, não importando para ele qual a implementação real que vai utilizar, desde que a implementação cumpra o contrato que é estabelecido pela interface.

É claro, agora é necessário, alguma forma, prover para o pedido uma implementação de GerenciadorTaxasJuros. Em produção, iremos fornecer para o pedido a implementação GerenciadorTaxasJurosBD, mas em ambiente de teste, podemos oferecer GerenciadorTaxasJurosFake, que é mais eficaz nexte contexto.

A forma mais fácil de fazer isso (sem ainda utilizar uma framework como o Guice), é simplesmente injetar a implementação desejada da interface GerenciadorTaxasJuros através do construtor da classe Pedido:


A partir de agora, todas as vezes que um pedido for criado

  • em ambiente de produção, podemos injetar um objeto GerenciadorTaxasJurosBD
  • em ambiente de testes, podemos injetar um objeto GerenciadorTaxasJurosFake, que é mais eficaz.

Se você conseguiu entender o conceito acima, você agora compreende tudo o que precisa saber sobre Injeção de Dependências. É apenas uma maneira especial de dizer para o pedido "Eu vou dizer onde você vai buscar os dados quando eu te configurar". Se isso é tudo o que tenho que falar sobre injeção de dependências, então onde é que entra o Guice?

Pense no seguinte: se você tiver que usar o construtor em toda parte em sua aplicação, o que isto acarretará ao seu código? Por um lado, você agora terá bastante trabalho adicional, tendo que passar pra lá e pra cá os objetos que deseja injetar através da aplicação. Toda vez que precisar de um Pedido, onde quer que seja na aplicação, terá de injetar manualmente uma implementação da interface GerenciadorTaxasJuros.

Posso até conseguir alguma forma mais prática de fazer isso, mas provavelmente ela provocará o mesmo tipo de inflexibilidade / acoplamento que existia quando usávamos meramente um Singleton. Logo logo o código inteiro vai estar infestado de parâmetros sendo passados via construtores ou algum outro bacalhau que vai deixar o código cada vez mais sujo.

Além disso, apesar de que um Singleton que tantas pessoas usam em aplicações Java seja um estorvo, o fato de se reutilizar um único objeto GerenciadorTaxasJuros ao invés de ter que criar um novo cada vez que precisar passá-lo ao construtor da classe Pedido, este conceito ainda é bom e desejado.

Sem falar nos problemas relacionados a Thread Safety? ou gargalos de desempenho, instanciar mais de um objeto GerenciadorTaxasJuros - um para cada Pedido - em uma única JVM seria um desperdício de memória, uma vez que todos eles realizam exatamente o mesmo trabalho (e nem mencionamos o quão caro é construir novas conexões no caso da implementação que acessa o banco de dados).

Portanto, enquanto a Injeção de Dependências resolveu o problema de alto Acomplamento, ela introduziu muito mais problemas, e não é nada prática em aplicações de grande porte. É por este motivo que outros Padrões de Projeto?, como Fábricas? e Dicionários?, entre outros, se tornaram tão populares. Eles fornecem uma forma mais organizada de localizar utilizar objetos através da aplicação.

O problema com os dicionários e fábricas é que eles são pesados e normalmente são difíceis de configurar. Eles desempenham um importante papel em grandes sistemas coporativos, mas podem ser inviáveis de se utilizar em pequenos projetos. A maioria utiliza busca de objetos por nome através de Strings, o que carrega seus próprios riscos - Strings incorretas não podem ser capturadas pelo compilador e só vão manifestar problemas em tempo de execução.

O que precisamos, portanto, é de alguma forma de "conectar" (wiring) todas essas dependências entre objetos, e esta conexão deve ser feita em tempo de execução, para evitar alto acoplamento no código. Devemos ser capazes de configurar em algum lugar qual implementação precisamos utilizar em cada circunstância, e deixar que alguma biblioteca faça o trabalho de conectar tudo, seja para testes ou para produção.