design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

303

Upload: rodrigo-hiemer

Post on 10-Aug-2015

206 views

Category:

Software


47 download

TRANSCRIPT

Page 1: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

CORTESIA_PREMIUM_PLUS

Page 2: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

© Casa do Código

Todos os direitos reservados e protegidos pela Lei nº9.610, de

10/02/1998.

Nenhuma parte deste livro poderá ser reproduzida, nem transmitida, sem

autorização prévia por escrito da editora, sejam quais forem os meios:

fotográficos, eletrônicos, mecânicos, gravação ou quaisquer outros.

Casa do Código

Livros para o programador

Rua Vergueiro, 3185 - 8º andar

04101-300 – Vila Mariana – São Paulo – SP – Brasil

Design Patterns com Java: Projeto orientado a objetos guiado por

padrões / Eduardo Guerra

São Paulo: Casa do Código, 2013

ISBN 978-85-66250-11-4

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 3: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código

Agradecimentos pessoais

Meu primeiro agradecimento é a Deus, por Ele ter me proporcionado tudopara que trilhasse o caminho que trilhei até chegar onde estou agora. Porter colocado as pessoas certas para me ajudar em minhas di�culdades, porter me colocado obstáculos nas horas adequadas que me permitiram amadu-recer para superar obstáculos maiores no futuro e por ter me iluminado nomomento das decisões mais críticas que acabaram direcionando minha vidano futuro.

Em seguida envio meu “muito obrigado” para meus pais, José Maria eMaria da Graça, que sempre me incentivaram e me apoiaram em minhas es-colhas. Mesmo estando distantes �sicamente hoje, sei que todos os dias elesrezam e torcem por mim. Imagino direitinho eles mostrando esse livro paratodo mundo com o maior orgulho! Aproveito essa oportunidade para esten-der esse agradecimento para toda minha família, em especial para minha TiaDorinha que sempre esteve ao meu lado quando precisei.

Agradeço também aMaria Eduarda, a Duda, minha �lha mais velha, portrazer só alegria desde que chegou nesse mundo. Seu jeito amigo e carinhosome conforta sempre que por algum motivo estou para baixo. Não sei comotanta bondade e paciência pode caber em apenas uma pessoa. Sou muitograto pelo seu companheirismo, como quando ela senta do meu lado bemagarradinha para assistir um �lme ou jogar um videogame.

Seguindo para a mais nova da família, agradeço a Ana Beatriz, a Bia, porconter a maior concentração de energia e alegria de todo mundo. Apesar deser bem bagunceira e gostar de um alvoroço, ela sempre faz alguma coisa queconsegue tirar uma gargalhada de nossa boca nas horas mais inesperadas. Éaté difícil de descrever a alegria que sinto quando eu chego e essa baixinha

i

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 4: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código

vem gritando o meu nome e dá um abraço na minha perna.Deixei para o �m os agradecimentos para minha companheira de toda

vida, minha esposa Roberta, ou, como eu a chamo, Lindinha. Acho que elanão tem noção do amor que sinto por ela e como sua presença é central emminha vida. Eu agradeço a ela por através de suas ações ter se tornado a pes-soa mais importante da minha vida. Sem ela eu seria incompleto e não teriaestrutura para ter trilhado o caminho que segui em minha vida até esse mo-mento. Muito obrigado por todo seu apoio, por todo seu amor e por todo seucarinho!

ii

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 5: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código

Agradecimentos pro�ssionais

Antes demencionar alguémde forma especí�ca, gostaria de agradecer a todosmeus professores, colegas de trabalho, amigos e companheiros que de algumaforma contribuíram para meu conhecimento e me incentivaram a seguir emfrente. Nossa vida é feita de pessoas e de experiências, e é a partir disso queseguimos nossos caminhos e realizamos nossas escolhas.

Primeiramente deixo um agradecimento institucional ao ITA, um insti-tuto que é referência nacional e internacional em engenharia, em onde meformei emEngenharia daComputação e �zmeumestrado e doutorado. Alémdisso, também foi um local onde tive a oportunidade de atuar como profes-sor por mais de � anos. Foram experiências que me marcaram e são umaimportante parte da minha história. Eu saí do ITA, mas com certeza o ITAnão saiu de mim! Agradeço em especial ao professor Clovis Fernandes, meuorientador na graduação, no mestrado e no doutorado. Um grande amigo,que sempre me incentivou e que foi grande mentor, cujos ensinamentos fo-ram muito além de questões técnicas. Espero ainda fazermos muitas coisasjuntos!

Agradeço à revista MundoJ, principalmente ao Marco Guapo, inicial-mente por ter aberto espaço para que escrevesse sobre minhas ideias e meusconhecimentos. Em seguida, Guapo con�ou a mim o conteúdo da revistacomo editor-chefe. Através da minha atuação na revista eu pude aprendermuita coisa e me manter sempre atualizado durante os últimos anos. Atra-vés dos artigos que escrevi ganhei experiência e tomei gosto pela escrita deconteúdo técnico. Posso a�rmar com certeza que a semente desse livro foiplantada em alguns de meus artigos durante os últimos anos. Espero que essaparceria ainda dure bastante tempo!

iii

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 6: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código

Agradeço tambémà comunidade de padrões, tanto nacional quanto inter-nacional, por terme in�uenciado com toda sua cultura e ideias. Sou grato porterem me recebido de braços abertos e por terem fornecido um fórum ondefoi possível debater as minhas ideias e obter feedback dos trabalhos que estourealizando. Agradeço em especial pelo apoio e incentivo de Fabio Kon, FábioSilveira, Je�erson Souza, Uirá Kulezsa, Ademar Aguiar e Filipe Correia. Tam-bém agradeço pelos meus gurus Joseph Yoder e Rebecca Wirfs-Brock, comquem aprendi demais e tive discussões muito interessantes sobre modelageme arquitetura de so�ware.

Agradeço ao INPE, instituição onde acabei de ingressar, por ter me rece-bido de braços abertos e pela con�ança que tem depositado emmeu trabalho.Espero poder dar muitas contribuições e desenvolver diversos trabalhos in-teressantes durante os anos que estão por vir!

Finalmente agradeço à Casa do Código pelo convite para escrita desse li-vro. Apesar de a vontade sempre ter existido, o convite de vocês foi o estopimpara que esse projeto se tornasse algo concreto. Deixo um agradecimento emespecial ao editor desse livro, Paulo Silveira, pelos questionamentos e suges-tões que contribuíram de forma de�nitiva para o conteúdo desse livro.

iv

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 7: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código

Minibiogra�a do autor

Eduardo Martins Guerra nasceu em Juiz de Fora em ���� e cursou o ensinobásico e médio em escola pública, o Colégio de Aplicação João XXIII. Desdeessa época ele já despertou seu interesse pela programação, começando com adigitação de código Basic que vinha em revistas no seu defasado computadorExato Pro. A diversão era mais ver o efeito de pequenas modi�cações nocódigo do que o jogo que saia como resultado. Nessa época, pouco depois,também fez um curso de Clipper, onde desenvolveu seu primeiro so�ware:um gerenciador de canil!

Incentivado por seus professores, pela facilidade com disciplinas na áreade exatas, prestou o vestibular para o ITA em ����, logo após o término deseus estudos, e foi aprovado. Durante o curso, ele optou pela carreira militare pelo curso de computação, além de participar de atividades extracurricula-res como projetos para a empresa júnior e aulas no curso pré-vestibular parapessoas carentes, CASD Vestibulares. Nesses cinco anos, Eduardo se apai-xonou pela área de desenvolvimento de so�ware e pela possibilidade de usarconstantemente sua criatividade para propor sempre soluções inteligentes einovadoras. Dois dias depois de se formar, casou-se com sua mais forte pai-xão, sua esposa atual, Roberta.

Após formado, em ����, foi alocado no Centro de Computação da Ae-ronáutica de São José dos Campos (CCA-SJ), onde �caria pelos próximos �anos. Durante esse período, trabalhou com o desenvolvimento de so�wareoperacional para atender as necessidades da Força Aérea Brasileira, adqui-rindo uma valiosa experiência na área.

No �nal de ����, com dedicação em tempo parcial, Eduardo conseguiuseu título de mestre em Engenharia da Computação e Eletrônica apresen-

v

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 8: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código

tando a dissertação “UmEstudo sobre Refatoração de Código de Teste”. Nessetrabalho, Eduardo desenvolve sua paixão pelo design de so�ware, realizandoum estudo inovador sobre boas práticas ao se codi�car testes automatizados.Essa dissertação talvez tenha sido um dos primeiros trabalhos acadêmicosno Brasil no contexto de desenvolvimento ágil. No ano de conclusão de seumestrado, veio sua primeira �lha, Maria Eduarda.

Devido sua sede de conhecimento, ainda nesse período, estudou porconta própria e tirou sete certi�cações referentes a tecnologia Java, sendo naépoca um dos pro�ssionais brasileiros com maior número de certi�caçõesna área. Além disso, ocupa o cargo de editor-chefe da revista MundoJ desde����, na qual já publicou dezenas de artigos técnicos de reconhecida quali-dade. Esses fatos lhe deram uma visão da indústria de desenvolvimento deso�ware que foi muito importante em sua trajetória, direcionando sua pes-quisa e seus trabalhos para problemas reais e relevantes.

NoCCA-SJ, Eduardo atuou de forma decisiva no desenvolvimento de fra-meworks. Seus frameworks simpli�caram a criação das aplicações e derammaior produtividade para a equipe de implementação. Um deles, o Swing-Bean, foi transformado em um projeto open-source e já possui mais de ����downloads. A grande inovação e diferencial de seus frameworks estava nofato de serem baseados em metadados. A partir desse caso de sucesso, eledecidiu estudar mais sobre como a utilização de metadados poderia ser feitaem outros contextos. Foi uma surpresa descobrir que, apesar de outros fra-meworks líderes de mercado utilizarem essas técnicas, ainda não havia ne-nhum estudo sobre o assunto. Foi, então, que Eduardo começou sua jornadapara estudar e tornar acessível o uso dessa técnica por outros desenvolvedo-res, pois ele sabia do potencial que os frameworks baseados em metadadostem para agilizar o desenvolvimento de so�ware e o tornar mais �exível.

Então, em ����, ele ingressou no curso de doutorado do ITA pelo Pro-grama de Pós-Graduação em Aplicações Operacionais – PPGAO. A pesquisasobre frameworks baseados emmetadados se encaixava perfeitamente no ob-jetivo do programa. A criação de arquiteturas �exíveis, nas quais se podeacrescentar funcionalidade de forma mais fácil, é algo crítico para aplicaçõesde comando e controle, foco de uma das áreas do programa. Orientado peloprofessor Clovis Torres Fernandes, começou uma pesquisa que envolveu a

vi

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 9: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código

análise de frameworks existentes, a abstração de técnicas e práticas, a criaçãode novos frameworks de código aberto e a execução de experimentos paraavaliar os conceitos propostos.

Foi nessa época queEduardo começou a participar e se envolveuna comu-nidade de padrões. Em ���� submeteu para o SugarLoafPLoP em Fortalezaos primeiros padrões que identi�cou em frameworks que utilizavam meta-dados. Nesse momento ele se empolgou com o espírito de colaboração dacomunidade de padrões, onde recebeu um imenso feedback do trabalho queestava realizando. Desde então se tornou um membro ativo da comunidade,publicando artigos com novos padrões em eventos no Brasil e no exterior.Além disso, em ���� participou da organização do MiniPLoP Brasil, em ����foi o primeiro brasileiro a ser chair da principal conferência internacional depadrões, o PLoP, e em ���� está tambémparticipando tambémda organizaçãodo próximo MiniPLoP.

Enquanto realizava seu doutorado, Eduardo foi incorporado, ainda comomilitar, no corpo docente do Departamento de Ciência da Computação doITA, onde pôde desenvolver umanova paixão: ensinar! Durante esse período,ministrou aulas na graduação e em cursos de especialização, e orientou umasérie de trabalhos que, de certa forma, complementavam e exploravam ou-tros aspectos do que estava desenvolvendo em sua tese. Em consequência dobom trabalho realizado, foi convidado por três vezes consecutivas para ser oprofessor homenageado da turma de graduação Engenharia da Computaçãoe duas vezes para a turma de especialização em Engenharia de So�ware.

Em ����, apresentou seu doutorado chamado “A Conceptual Model forMetadata-based Frameworks e concluiu com sucesso essa jornada que deixoudiversas contribuições. Devido a relevância do tema, foi incentivado pelosmembros da banca a continuar os estudos que vem realizando nessa área.Apesar da tese ter trazido grandes avanços, ele tem consciência que ainda hámuito a ser feito. Uma de suas iniciativas nessa área foi o projeto Es�nge (http://es�nge.sf.net) , que é um projeto guarda-chuva para a criação de diversosframeworks com essa abordagem baseada em metadados. Até o momentojá existem três frameworks disponíveis e vários artigos cientí�cos em cimade inovações realizadas nesses projetos. No mesmo ano que terminou seudoutorado, veio sua segunda �lhinha, a Ana Beatriz.

vii

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 10: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código

Em ����, Eduardo prestou concurso para o Instituto Nacional de Pesqui-sas Espaciais, onde assumiu o cargo de pesquisador no início ����. Hoje elesegue com sua pesquisa na área de arquitetura, testes e design de so�ware,buscando aplicar as técnicas que desenvolve para projetos na área espacial.Adicionalmente, atua como docente na pós-graduação de Computação Apli-cada desse instituto, onde busca passar o conhecimento que adquiriu e orien-tar alunos para contribuírem com essas áreas.

viii

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 11: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código

Por que mais um livro depadrões?

O primeiro livro de padrões “Design Patterns: Elements of Reusable Object-Oriented So�ware”, conhecido como Gang of Four (GoF), já foi lançado hámais de �� anos e de forma alguma seu conteúdo está ultrapassado. Em mi-nha opinião, esse livro foi algo muito a frente de seu tempo, sendo quemuitasde suas práticas só foram ser utilizadas de forma efetiva pela indústria algunsanos depois. Durante esses anos, diversos livros sobre esses padrões foram es-critos, apresentando sua implementação em outras linguagens ou ensinando-os de formamais didática. Desse contexto podem surgir as seguintes pergun-tas: será que é preciso mais um livro sobre padrões? O que esse livro trás dediferente?

Durante esses últimos anos tive diversos tipos de experiência que me �-zerem ter diferentes visões sobre os padrões. Dentre essas experiências euposso citar o ensino de modelagem de so�ware e padrões, a utilização de pa-drões para o desenvolvimento de frameworks e aplicações, a identi�cação denovos padrões a partir do estudo de implementações existentes e discussõesna comunidade sobre padrões e sua aplicabilidade. A partir disso acreditoque tive a oportunidade de ter uma visão diferenciada sobre esses padrões,e é essa visão que procuro passar nesse livro. As seções a seguir descrevemalguns diferenciais desse livro em relação a outros existentes.

Uma sequência didática para aprendizado

Percebi que muitos cursos sobre padrões, sendo eles dados por univer-

ix

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 12: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código

sidades ou empresas de treinamento, vão ensinando os padrões um por um.Mas qual é a melhor ordem para o ensino e a assimilação dos padrões? Issoera algo que era decidido por cada professor. Uma ordem inadequada nesseaprendizado pode impedir que o aluno compreenda os princípios por trás dasolução do padrão, o que di�culta sua compreensão a respeito de sua aplica-bilidade e suas consequências. Isso também pode causar uma visão limitadada solução do padrão, o que impede sua adaptação para outros contextos si-milares.

Esse livro procura organizar os padrões em uma sequência didática parao aprendizado. Cada capítulo agrupa os padrões pelo tipo de técnica que uti-lizam em sua solução ou pelo tipo de problema que resolvem. Dessa forma,ao ver umamesma técnica ser utilizada em padrões diferentes é possível com-preender melhor a sua dinâmica e de que formas diferentes ela pode ser uti-lizada. Em capítulos que focam em tipos de problema, a visão das diferentesalternativas de solução e das suas diferenças, cria um senso crítico a respeitodas possíveis alternativas de modelagem e quais os critérios que devem serconsiderados para sua escolha.

Isso faz com que esse livro vá além da apresentação dos padrões e sejana verdade a respeito de técnicas para modelagem orientada a objetos. Ospadrões são utilizados como uma referência para a discussão de cada umadessas técnicas. Dessa forma, esse livro é recomendado para utilização comomaterial base em cursos de graduação ou pós-graduação a respeito de técni-cas de programação orientada a objetos ou programação orientada a objetosavançada.

Relação entre padrões

Outro problema que percebo é que os padrões tendem a ser olhados deforma individual. Isso é uma consequência do formato que os padrões pos-suem e do fato de serem autocontidos, ou seja, toda informação sobre o pa-drão estar contida em sua descrição. Isso por um lado é bom pois permiteque alguém obtenha todas as informações sobre um determinado padrão emapenas um local. Porém, por outro lado, perde-se um pouco a ideia a respeitodo relacionamento entre os padrões. Apesar de haver uma seção que descreveos padrões relacionados, ainda é uma descrição pequena para uma questão

x

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 13: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código

de tamanha importância.Apresentar o relacionamento entre os padrões foi um dos objetivos desse

livro. Apesar de cada padrão apresentar uma solução independente, é da com-binação entre eles que é possível criar uma sinergia para criação de soluçõesainda melhores. Dessa forma, a medida que o livro vai seguindo, e novos pa-drões vão sendo apresentados, existe uma preocupação emmostrar como elespodem ser combinados com os padrões anteriores ou como eles podem serutilizados como uma alternativa a um outro padrão. Esse tipo de discussão éimportante, pois além de saber a solução do padrão, também é essencial saberquando aplicá-la.

Refatoração para padrões

Amodelagem de uma aplicação é uma questão dinâmica que evolui ame-dida que o desenvolvimento do so�ware vai seguindo seu curso. Apesar deainda ser comum amodelagem das classes da aplicação a partir de diagramasantes do inicio das implementações, muitas vezes os problemas aparecem so-mente depois. Sendo assim, tão importante quanto saber como utilizar ospadrões na modelagem inicial, é saber como refatorar um código existentepara uma solução que utilize um determinado padrão. A refatoração cons-tante do código é hoje uma das bases para a manutenção de sua qualidade aolongo do projeto.

Dessa forma, além de apresentar os padrões, esse livro também se pre-ocupa em mostrar quais os passos que precisariam ser feitos para refatorarum código existente em direção a um determinado padrão. Este assunto, quejá foi tema de um livro dedicado somente a ele, aqui é apresentado de forma�uida juntamente com os padrões. Isso é importante para que o leitor possater uma visão, não apenas estática em relação a utilização de um padrão, masde como um código existente pode evoluir e ser melhorado a partir de suaintrodução a partir de pequenos passos.

Exemplos realistas e do dia a dia

Por mais que diagramas sejam úteis para que se tenha uma visão geral deuma solução, os desenvolvedores ainda têm muito a cultura do “show me thecode”, querendo ver na prática como um padrão pode ser implementado. Por

xi

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 14: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código

mais que oGoF tenha sido um livro a frente de seu tempo, ele utiliza exemplosque hoje estão distantes de umdesenvolvedor comum, como a criação de umasuíte de componentes grá�cos ou um editor de texto. Alguns outros livrosacabam trazendo “toy examples”, que seriam exemplos simples e didáticos,mas que são fora do contexto de aplicações reais.

Achomuito importante que o leitor se identi�que com os exemplos e façauma ligação com o tipo de so�ware que desenvolve. Ao se ver próximo aosexemplos e possuir uma experiência anterior com aquele tipo de problema,é muito mais fácil compreender as questões que estão envolvidas e as alter-nativas de solução com suas respectivas consequências. Nesse livro, busqueitrabalhar com exemplos recorrentes em aplicações desenvolvidas por grandeparte dos programadores, como geração de relatórios, carrinho de comprasem aplicações de e-commerce e geração de arquivos para integração entreaplicações. Acredito que assim �cará bem mais claro como cada um poderáutilizar esses padrões em seu dia a dia.

Dicas e referências para implementações em Java

Muitos padrões apresentados por esse livro são amplamente utilizadosem frameworks e APIs Java. Muitas vezes, apenas por seguir as regras de umadeterminada API, sem saber você já está utilizando um determinado padrão.A �exibilidade que aquele framework consegue para ser instanciado na suaaplicação, muitas vezes é conseguida justamente pelo uso do padrão! O co-nhecimento de uma situação em que aquele padrão foi utilizado e, muitasvezes sem saber, você acabou o utilizando, também ajuda muito em sua com-preensão.

Dessa forma, esse livro também cita diversas classes de APIs padrão dalinguagem Java e de frameworks amplamente utilizados pela comunidade dedesenvolvimento. Isso vai permitir que o desenvolvedor possa compreen-der melhor aquela solução inteligente utilizada naquele contexto, e trazer amesma ideia para questões do seu próprio código.

Além disso, como o próprio nome do livro diz, os padrões de projeto sãoapresentados na linguagem Java e, dessa forma, acabam trazendo algumasdicas mais especí�cas da linguagem para sua implementação. Essas dicas vãodesde a utilização dos próprios recursos da linguagem, como a utilização de

xii

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 15: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código

sua biblioteca de classes e, até mesmo, de frameworks externos.

xiii

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 16: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 17: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código

Lista de discussão

O design de so�ware é um tema pelo qual me apaixonei justamente por seralgo em que o uso da criatividade é essencial, e cada aplicação tem suas parti-cularidades, o que sempre traz novas questões a serem discutidas. O mesmoocorre com padrões! Eles já estão aí há muitos anos e até hoje ainda existemuita discussão sobre eles! Sendo assim, eu gostaria que esse livro não fosseapenas uma comunicação de mão única, onde eu escrevo e vocês leem, maso início de um canal de comunicação para fomentar discussões e conversassobre padrões e design de so�ware em geral.

Sendo assim, criei uma lista de discussão com o nome “Design Patternsem Java: Projeto orientado a objetos guiado por padrões

no endereço [email protected] você quer discutir, colocar suas dúvidas e saber eventos e novidades no

mundo dos padrões, deixo aqui o meu convite para a participação no grupo!

xv

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 18: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 19: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código

Prefácio

Por Joseph YoderJá faz cerca de �� anos que o Gang of Four escreveu o livro inicial de pa-

drões chamado “Design Patterns: Elements of Reusable Object-OrientedSo�ware

. Desde aquela época, os padrões de projeto se tornaram muito conhe-cidos e uma parte essencial de uma boa modelagem em sistemas orientadosa objetos. Adicionalmente, os padrões ganharam uma grande aceitação nacomunidade de desenvolvimento de so�ware, o que pode ser observado pelaexistência de diversas publicações e casos de sucesso. Existem também diver-sas conferências ao redor do mundo criadas aos moldes da primeira confe-rência sobre padrões, a Pattern Languages of Programs (PLoP).

Mesmo nos anos iniciais do Java, é possível observar como os padrõesin�uenciaram a linguagem. Algumas dessas in�uências podem ser vistasclaramente nas primeiras APIs do Java, como as interfaces Iterator eObserver, enquanto outras foram implementadas como parte da evoluçãodessas APIs, como a implementação dos listeners, que são uma implementa-ção mais atual do padrão Observer.

Porém, é importante notar que umbomdesign não acontece por acidente,pois ele exige trabalho duro e evolução. De forma complementar, tambémnão quer dizer que usando um padrão necessariamente se tem um bom de-sign. Por exemplo, a linguagem Java foi lançada apenas alguns anos depoisda publicação do primeiro livro de design patterns. Por causa disso, mui-tas das primeiras bibliotecas da linguagem foram in�uenciadas pelo livro emuitos padrões foram incorporados no design de suas principais bibliotecase frameworks. No entanto, isso nem sempre guiou as soluções para o me-

xvii

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 20: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código

lhor design e foram necessárias várias evoluções em suas classes depois quediversos problemas foram encontrados nas primeiras versões.

Um bom exemplo pode ser visto no tratamento de eventos do JavaAWT. No AWT �.�, o tratamento de eventos utilizava o padrão Chain of

Responsibility. A princípio essa parecia uma solução adequada ao olharpara os requisitos. O problema com essa implementação era que precisava-seda utilização de um Mediator ou da criação de diversas subclasses. Isso po-deria causar problemas de desempenho, visto que para o evento ser tratadoera necessário percorrer toda a cadeia para descobrir qual era a classe quedeveria processá-lo. Isso muitas vezes direcionava o desenvolvedor a criaçãode uma solução muito complexa devido ao grande número de subclasses queprecisava criar.

A partir disso, o AWT �.� passou a tratar os eventos de forma mais e�ci-ente utilizando os padrões Observer e Adapter. Essa modelagem evoluiudepois para a utilização de listeners. Essa acabou sendo uma solução muitomais limpa e e�ciente, visto que apenas as classes interessadas no evento eramnoti�cadas quando ele acontecia. Essa história claramente mostra que o de-sign de um so�ware deve levar em consideração as consequências positivase negativas de cada decisão, e só porque ele utiliza padrões não quer dizerque aquela é a melhor alternativa de design. Veja que isso não quer dizer queutilizar padrões leva a decisões erradas de design, mas que, pelo contrário, autilização de um outro padrão mais adequado se mostrou uma solução me-lhor.

Apenas saber como aplicar os padrões não é o su�ciente para um bomdesign. Entender quando e onde aplicá-los é tão importante quanto, senãomais importante. De fato, no decorrer do tempo, mesmo um bom designinicial pode ser comprometido por sucessivas revisões arquiteturais. Em ����,foi feita a a�rmação que a arquitetura que realmente predominava na práticaera a Big Ball of Mud (traduzindoGrande Bola de Lama). Emesmo commuito trabalho e dedicação ainda é possível acabar com um Big Ball of

Mud se você não está comprometido a manter o código sempre limpo.Existemmuitas forças e bons motivos que podem levar a uma arquitetura

extremamente complexa. De fato, arquitetos e times de desenvolvimento ex-perientes estão constantemente fazendo exatamente o que deveria ser feito

xviii

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 21: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código

quando se deparam com “lama” e complexidade desnecessária em seu sis-tema.

Quando se tenta manter uma arquitetura, é importante tentar protegercertas partes do design. Grandes sistemas possuem partes que mudam emdiferentes velocidades. Existem certas ações e padrões que você pode utilizarpara isolar e de�nir divisões em volta dessas diferentes partes, tanto para tor-nar a arquitetura estável, quanto para possibilitar as mudanças onde elas sãonecessárias. De fato, essa é uma premissa importante dos padrões de projeto.Isso se torna uma consideração ainda mais importante quando evoluímos aestrutura das aplicações para um mundo onde parte delas está localizada nanuvem.

Eu conheço o autor desse livro (Eduardo) a muitos anos. Eduardo temgrande experiência em padrões, design orientado a objetos e Java. Eu já co-laborei e trabalhei com ele em projetos orientados a objetos e publicações,incluindo a implementação de diversos padrões de projeto e a construção deframeworks. Eduardo tem muita experiência na construção de frameworks,atividade que exige um conhecimento profundo de padrões, além de quandoe onde utilizá-los. Ele tem mais do que uma compreensão de “como” imple-mentar um padrão em Java. Eduardo claramente compreende o que está emjogo em um bom design orientado a objetos e como utilizar os padrões paraum design mais limpo, mais reutilizável e mais fácil de mudar.

Qualquer desenvolvedor para se tornar pro�ciente em um design orien-tado a objetos precisará compreender não apenas o básico sobre padrões, mastambém princípios mais avançados a respeito de suas consequências e o con-texto em que devem ser utilizados. Este livro é mais do que um livro de re-ceitas sobre como aplicar os padrões, pois, pelo contrário, ele traz tambémimportantes princípios e se aprofunda nessas técnicas avançadas de design.O leitor irá descobrir nesse livro um guia e�ciente a respeito de como aplicaros padrões, como escolher o padrãomais adequado e como saber quando umdesign deve ser refatorado para a introdução de um novo padrão.

Após ler esse livro, é possível continuar essa jornada sobre o conheci-mento de padrões através de conferências sobre o assunto, como o SugarLo-afPLoP no Brasil. Este assunto tem se tornado de extrema importância paratodas as facetas dos sistemas de so�ware, principalmente aqueles que estão

xix

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 22: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código

em constante evolução para se manterem atualizados frente a novos requisi-tos.

xx

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 23: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Sumário

Sumário

� Entendendo padrões de projeto ��.� Conceitos da orientação a objetos . . . . . . . . . . . . . . . . ��.� Mas esses conceitos não são su�cientes? . . . . . . . . . . . . ��.� O primeiro problema: cálculo do valor do estacionamento . ��.� Strategy: o primeiro padrão! . . . . . . . . . . . . . . . . . . . ���.� O que são padrões? . . . . . . . . . . . . . . . . . . . . . . . . ���.� Como o livro está organizado . . . . . . . . . . . . . . . . . . ��

� Reúso através de herança ���.� Exemplo de padrão que utiliza herança - Null Object . . . . . ���.� Hook Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . ���.� Revisando modi�cadores de métodos . . . . . . . . . . . . . . ���.� Passos diferentes na mesma ordem - Template Method . . . ���.� Refatorando na direção da herança . . . . . . . . . . . . . . . ���.� Criando objetos na subclasse - Factory Method . . . . . . . . ���.� Considerações �nais do capítulo . . . . . . . . . . . . . . . . . ��

� Delegando comportamento com composição ���.� Tentando combinar opções do gerador de arquivos . . . . . . ���.� Bridge - uma ponte entre duas variabilidades . . . . . . . . . ���.� Hook Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . ���.� State - variando o comportamento com o estado da classe . . ���.� Substituindo condicionais por polimor�smo . . . . . . . . . . ���.� Compondo com múltiplos objetos - Observer . . . . . . . . . ���.� Considerações �nais do capítulo . . . . . . . . . . . . . . . . . ��

xxi

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 24: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Sumário Casa do Código

� Composição recursiva ���.� Compondo um objeto com sua abstração . . . . . . . . . . . ���.� Composite - quando um conjunto é um indivíduo . . . . . . ���.� Encadeando execuções com Chain of Responsibility . . . . . ���.� Refatorando para permitir a execução de múltiplas classes . . ����.� Considerações �nais do capítulo . . . . . . . . . . . . . . . . . ���

� Envolvendo objetos ����.� Proxies e decorators . . . . . . . . . . . . . . . . . . . . . . . . ����.� Exemplos de Proxies . . . . . . . . . . . . . . . . . . . . . . . . ����.� Extraindo um Proxy . . . . . . . . . . . . . . . . . . . . . . . . ����.� Adaptando interfaces . . . . . . . . . . . . . . . . . . . . . . . ����.� Considerações �nais do capítulo . . . . . . . . . . . . . . . . . ���

� Estratégias de criação de objetos ����.� Limitações dos construtores . . . . . . . . . . . . . . . . . . . ����.� Criando objetos com métodos estáticos . . . . . . . . . . . . . ����.� Um único objeto da classe com Singleton . . . . . . . . . . . ����.� Encapsulando lógica complexa de criação com Builder . . . . ����.� Relacionando famílias de objetos com Abstract Factory . . . ����.� Considerações �nais do capítulo . . . . . . . . . . . . . . . . . ���

� Modularidade ����.� Fábrica dinâmica de objetos . . . . . . . . . . . . . . . . . . . ����.� Injeção de dependências . . . . . . . . . . . . . . . . . . . . . ����.� Service Locator . . . . . . . . . . . . . . . . . . . . . . . . . . . ����.� Service Locator versus Dependency Injection . . . . . . . . . ����.� Considerações �nais do capítulo . . . . . . . . . . . . . . . . . ���

� Adicionando operações ����.� Classes que representam comandos . . . . . . . . . . . . . . . ����.� Cenários de aplicação do Command . . . . . . . . . . . . . . ����.� Double Dispatch - Me chama, que eu te chamo! . . . . . . . . ����.� Padrão Visitor . . . . . . . . . . . . . . . . . . . . . . . . . . . ����.� Considerações �nais do capítulo . . . . . . . . . . . . . . . . . ���

xxii

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 25: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Sumário

� Gerenciando muitos objetos ����.� Criando uma fachada para suas classes . . . . . . . . . . . . . ����.� Separando código novo de código legado . . . . . . . . . . . ����.� Mediando a interação entre objetos . . . . . . . . . . . . . . . ����.� Reaproveitando instâncias com Flyweight . . . . . . . . . . . ����.� Considerações �nais do capítulo . . . . . . . . . . . . . . . . . ���

�� Indo além do básico �����.� Frameworks . . . . . . . . . . . . . . . . . . . . . . . . . . . . �����.� Utilizando tipos genéricos com os padrões . . . . . . . . . . . �����.� Padrões com Test-driven Development . . . . . . . . . . . . . �����.� Posso aplicar esses padrões na arquitetura? . . . . . . . . . . . �����.� Comunidade de padrões . . . . . . . . . . . . . . . . . . . . . �����.� E agora? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ���

Índice Remissivo ���

Bibliogra�a ���Versão: ��.�.��

xxiii

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 26: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 27: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

C������� �

Entendendo padrões de projeto

“Eu diria algo como ‘So�ware está gerenciando o mundo’. Nosso trabalho éapenas polinizá-lo ...”– Brian Foote

Imagine que uma pessoa tenha aprendido diversas técnicas de pintura. Apartir desse conhecimento ela saberá como pegar um pincel, como misturaras cores e como trabalhar com diferentes tipos de tinta. Será que somentecom esse conhecimento ela conseguirá pintar um quadro? Note que ela sabetudo que se precisa para realizar a pintura, porém todo esse conhecimentonão é válido se a pessoa não souber como utilizá-lo. Para realizar essa tarefa, énecessário, alémde conhecimento, ter habilidade, que é algo que só se aprendecom muita prática e treino. Saber as técnicas é apenas o primeiro passo...

Com programação acontece um fenômeno similar. Quando se aprendeuma linguagem orientada a objetos e seus recursos, isso é equivalente a se

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 28: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Conceitos da orientação a objetos Casa do Código

aprender a pegar em um pincel. Saber, por exemplo, como utilizar herança epolimor�smo não é o su�ciente para distinguir em quais situações eles devemser empregados de forma apropriada. É preciso conhecer os problemas quepodem aparecer durante a modelagem de um sistema e saber quais as solu-ções que podem ser implementadas para equilibrar requisitos, muitas vezescontraditórios, com os quais se está lidando.

Felizmente existe uma forma de conseguir esse conhecimento sem pre-cisar passar pelo caminho tortuoso de errar várias vezes antes de aprender aforma correta. É um prazer poder apresentar nesse livro os Design Patterns,padrões de projeto na tradução mais utilizada, uma forma de se documentaruma solução para umproblema demodelagem. Mais do que isso, padrões nãosão novas soluções, mas soluções que foram implementadas com sucesso deforma recorrente em diferentes contextos. A partir deles, é possível aprendercom a experiência de outros desenvolvedores e absorver esse conhecimentoque eles levaram diversos anos para consolidar.

O objetivo desse capítulo é fazer uma revisão dos principais conceitos daorientação a objetos e apresentar como os padrões de projeto podem ser utili-zados para adquirir habilidade emmodelagem de so�ware. Ao seu �nal, seráapresentada uma visão geral de como esses padrões serão apresentados nodecorrer do livro.

�.� C�������� �� ���������� � �������Em linguagens mais antigas, não havia nenhuma separação dos elementos decódigo. Ele era criado em único bloco, no qual para se executar desvios no�uxo de execução, como para a criação de loops e condicionais, era precisorealizar saltos através do temido comando goto. Isso tornava o códigomuitodifícil de ser gerenciado e reutilizado, complicando seu desenvolvimento emanutenção. Foi dessa época que surgiu o termo “códigomacarrônico” , que sereferia à di�culdade de compreensão do �uxo de execução desses programas.

Em seguida, para resolver esse problema, foi criada aprogramação estru-turada. Nesse novo paradigma surgiram estruturas de controle que permi-tiam a criação de comando condicionais, comoo if e o switch, e comandositerativos, como o for e o while. Uma outra adição importante foram as

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 29: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Entendendo padrões de projeto

funções. A partir delas era possível dividir a lógica do programa, permitindosua modularização, e trazendo ainda a facilidade de reutilização em outrasaplicações. Amaior di�culdade nesse paradigma era como lidar comos dadose o comportamento associado a eles. Era comum a criação de variáveis glo-bais, que facilmente poderiam acabar causando problemas por estarem comvalores inconsistentes. Outro problema ocorria com dados relacionados quepodiam ser livremente modi�cados e facilmente �carem inconsistentes.

Como uma evolução da programação estruturada surgiu a programaçãoorientada a objetos. Além da estruturação da lógica, com esse paradigmaera possível a estruturação dos dados e conceitos relacionados ao negócio doso�ware. Sendo assim, pode-se colocar toda lógica relacionada a umconjuntode dados junto com ele. Além disso, a partir da possibilidade de abstração deconceitos consegue-se desacoplar componentes, obtendo um maior reúso euma maior �exibilidade.

Os tópicos a seguir exploram com maior detalhe os principais conceitosda programação orientada a objetos.

Classes e objetos

Na programação orientada a objetos são de�nidos novos tipos através dacriação de classes, e esses tipos podem ser instanciados criando objetos. Aideia é que um objeto represente uma entidade concreta enquanto sua classerepresenta uma abstração dos seus conceitos. Se, por exemplo, tivermos umaclasse Gato, o Gar�eld e o Frajola seriam objetos dessa classe. Adicional-mente, uma classe CarteiraDeIdentidade, que representa uma abstra-ção, teria como instâncias a minha e a sua carteira de identidade.

Eu vejo muitas pessoas utilizarem a metáfora “a classe seria a forma e oobjeto seria o pão” para explicar a relação entre classes e objetos. Pessoal-mente, não gosto dessa analogia pois ela leva a entender que a classe é o queproduz o objeto, o que seria mais próximo do conceito de fábrica (que serávisto no capítulo � desse livro). É importante perceber que os objetos repre-sentam entidades concretas do seu sistema, enquanto as classes abstraem suascaracterísticas.

A classe possui estado e comportamento, que são representados respec-tivamente pelos atributos e métodos de�nidos. Enquanto uma classe pos-

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 30: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Conceitos da orientação a objetos Casa do Código

sui uma característica, um objeto possui um valor para aquela característica.Por exemplo, um Gato possui a cor do pelo, enquanto o Gar�eld possui seupelo alaranjado. A classe possui um comportamento e o objeto pode realizaraquele comportamento. Seguindo o mesmo exemplo, enquanto um Gato

pode correr, o Frajola realmente corre para tentar pegar o Piu-piu.Projetar um sistema orientado a objetos consiste em de�nir suas classes e

como elas colaboram para conseguir implementar os requisitos de uma apli-cação. É importante ressaltar que não é só porque uma linguagem orientadaa objetos está sendo utilizada que se estará modelando de forma orientadaa objetos. Se suas classes não representam abstrações do sistema e são ape-nas repositórios de funções, você na verdade está programando segundo oparadigma estruturado.

Herança

Se uma aplicação precisa de um conjunto de objetos, deve haver classesque abstraiam esses conceitos. Porém, esses mesmos objetos podem precisarser tratados em partes diferentes do so�ware a partir de diferentes níveis deabstração. O Gar�eld e o Frajola podem ser tratados como gatos em umaparte do sistema, porém outra, que precisar também lidar com os objetosPica-pau eMickeyMouse, pode precisar de um conceito mais abstrato, comoanimal ou personagem.

Aherança é uma característica do paradigma orientado a objetos que per-mite que abstrações possam ser de�nidas em diversos níveis. Considerandoque você tem uma classe, ela pode ser especializada por uma outra que iráde�nir um conceito mais concreto. Por outro lado, um conjunto de classespode ser generalizado por uma classe que representa um conceito mais abs-trato. Quando uma classe estende outra, ela não só herda a estrutura de dadose o comportamento da superclasse, mas também o contrato que ela mantémcom os seus clientes.

Um dos grandes desa�os da modelagem orientada objetos é identi�caros pontos em que essa abstração deve ser utilizada e que será aproveitada deforma adequada pelo sistema. Pelo fato de um sistema de so�ware ser umarepresentação de algum processo que acontece nomundo real, isso não signi-�ca que todas as abstrações existentes precisam ser representadas no so�ware.

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 31: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Entendendo padrões de projeto

Umavião, por exemplo, seria representado de forma completamente diferenteem um simulador de voo, em um sistema de controle de tráfego aéreo e nosistema de vendas de uma companhia aérea. Saber qual a representação e asabstrações mais adequadas é um grande desa�o.

Encapsulamento

Sempre que falo sobre encapsulamento, inicio com a frase “A ignorânciaé uma benção!” . Não me entendammal, mas com a complexidade das coisascom que lidamos nos dias de hoje, é realmente muito bom não precisar sabercomo elas funcionam para utilizá-las. Fico feliz em poder assistir minha te-levisão, mudar de canal, aumentar o volume e não ter a menor ideia de comoessas coisas estão funcionando. Imagine como seria complicado poder uti-lizar novas tecnologias se elas exigissem uma compreensão profunda de seufuncionamento interno.

Essa mesma questão acontece com so�ware. Antes da programação es-truturada, o desenvolvedor precisava conhecer os detalhes de implementaçãode cada parte do código. Com a criação das funções, houve um certo avançoem relação à divisão do código, porém a utilização de algumas funções aindademandava um conhecimento sobre seu funcionamento interno, como quevariáveis elas estão acessando e atualizando.

O encapsulamento é um conceito importante da orientação a objetos quediz que deve haver uma separação entre o comportamento interno de umaclasse com a interface que ela disponibiliza para os seus clientes. Isso permiteque o desenvolvedor que utilizar uma classe precise saber somente como in-teragir com ela, abstraindo seu comportamento interno. Este é um conceitomuito poderoso que possibilita o desacoplamento entre as classes, podendoser utilizado para uma melhor divisão do so�ware em módulos.

Osmétodos getters e setters, que são utilizados respectivamente para aces-sar e modi�car propriedades em um objeto, são um exemplo do uso do en-capsulamento. Para os clientes da classe, apenas uma informação está sendorecuperada ou modi�cada, porém a implementação por trás pode realizaroutras coisas. Ao recuperar uma informação, ela pode ser calculada a partirde outros dados, e ao modi�car uma informação, pode haver uma validaçãodos dados. É importante lembrar que o uso dessesmétodos de acesso é apenas

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 32: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Conceitos da orientação a objetos Casa do Código

um começo para o encapsulamento de uma classe. A estrutura de dados podeser utilizada e manipulada também por outros métodos de forma totalmentetransparente à classe cliente.

A linguagem Java provê as interfaces como um recurso de linguagem, quepermite que a de�nição do contrato externo da classe possa ser feita de formaseparada da implementação. Quando uma classe implementa uma interfaceé como se estivesse assinando um documento, no qual ela se compromete aimplementar seus métodos. Dessa forma, os clientes dela não precisam co-nhecer a classe onde está a implementação, mas apenas a interface. Adicio-nalmente, diferentes implementações podem ser utilizadas pelo cliente sem amodi�cação de seu código.

I�������� �� ������ ���������

Várias vezes já me �zeram a pergunta: “quando devo utilizar umaclasse abstrata e quando devo utilizar uma interface?” . Tanto as classesabstratas quanto as interfaces podem de�nir métodos abstratos que pre-cisam ser implementados pelas classes que respectivamente a estende ouimplementa. Porém apenas as classes abstratas podem possuir métodosconcretos e atributos. Apesar dessa diferença, a resposta para pergunta émais conceitual do que relacionada com questões de linguagem.

Quando a abstração que precisar ser criada for um conceito, ou seja,algo que possa ser re�nado e especializado, deve-se utilizar uma classeabstrata. Quando a abstração é um comportamento, ou seja, algo queuma classe deve saber fazer, então a melhor solução é a criação de umainterface. Imagine um jogo no qual existem naves que se movem. Sesua abstração representa uma nave, então você está representando umconceito e deve utilizar uma classe abstrata. Por outro lado, se sua abs-tração representa algo que se move, então o que está sendo abstraído éum comportamento e a melhor solução é usar uma interface.

Polimor�smo

O polimor�smo é na verdade uma consequência da utilização de herança

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 33: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Entendendo padrões de projeto

e da utilização de interfaces. Não faria sentido utilizar herança para criar no-vas abstrações se o objeto não pudesse ser visto como uma dessas abstrações.Equivalentemente, não faria sentido haver uma interface se a classe não pu-desse ser tratada como algo que possui aquele comportamento. É através dopolimor�smo que um objeto pode ser visto como qualquer uma de suas abs-trações.

A palavra polimor�smo signi�ca “múltiplas formas” , o que indica queum objeto pode assumir a forma de uma de suas abstrações. Em outras pala-vras, qualquer objeto pode ser atribuído para uma variável do tipo de uma desuas superclasses ou para o tipo de suas interfaces. Isso é um recurso extrema-mente poderoso, pois um código pode utilizar uma classe que não conhece,se souber trabalhar com uma de suas abstrações. Isso pode ter um impactotremendo na reutilização e desacoplamento das classes.

Um bom exemplo para entender polimor�smo é a interfaceComparable que é provida pela API padrão da linguagem Java. Osalgoritmos de ordenação presentes na classe Collections sabem ordenarlistas de qualquer objeto que implementa essa interface, o que inclui classescomo Number, String e Date. Dessa forma, se uma classe da suaaplicação implementar essa interface, então os algoritmos saberão ordenaruma lista de suas instâncias. O mais interessante é que esse algoritmo utilizasua classe, mesmo tendo sido desenvolvido muito antes de ela existir.

�.� M�� ����� ��������� ��� ��� ������������Sem mais delongas, já irei responder que não são su�cientes. Na verdade,compreender bem esses conceitos é apenas o primeiro passo para a realizaçãode um projeto orientado a objetos. Entender o signi�cado da herança e sa-ber como implementá-la na linguagem de programação não é su�ciente parasaber em que situações o seu emprego é adequado. Saber como uma classeimplementa uma interface não basta para saber quando seu uso irá ajudar aresolver um problema.

Realizar a modelagem de um so�ware é um imenso jogo de raciocínio,como em um tabuleiro de xadrez. Da mesma forma que muitas vezes é pre-ciso entregar uma peça para se atingir um objetivo, no projeto de um so�ware

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 34: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. O primeiro problema: cálculo do valor do estacionamento Casa do Código

é comum abrir mão de certas características para se obter outras. Por exem-plo, ao se adicionar uma veri�cação de segurança, o desempenho pode serdegradado. Ou mesmo, ao se adicionar �exibilidade, a complexidade de usoda solução pode aumentar.

São raros os benefícios que vêm sem nenhuma consequência negativa.Durante o projeto de um so�ware o desenvolvedor está sentado emumamesade negociação, e do outro lado estão sentados os requisitos que precisam seratendidos. Ao colocar na mesa uma solução, devem ser avaliados as perdas eos ganhos obtidos para tentar equilibrar os atributos de qualidade do sistema.Muitas vezes é preciso combinar mais de uma solução para que o benefíciode uma compense a desvantagem da outra.

Nesse jogo, as soluções que podemser empregadas são as principais armasdo desenvolvedor. O desenvolvedor com menor experiência conhece umaquantidade menor de soluções, o que muitas vezes o leva a adotar um pro-jeto inadequado às necessidades do so�ware. A elaboração de uma soluçãopartindo do zero traz de forma intrínseca o risco da inovação, e muitas vezesacaba-se deixando de considerar outras alternativas.

Mas como conhecer diversas soluções sem precisar passar por vários anosalternando entre escolhas certas e erradas? Como saber o contexto em queessas soluções são adequadas e quais são as contrapartidas dos benefícios dasolução? Felizmente existe uma forma na qual desenvolvedores mais experi-entes expressam seu conhecimento em um determinado domínio. E então euapresento os padrões de projeto, os Design Patterns!

�.� O �������� ��������: ������� �� ����� ����������������

Para ilustrar como o livro vai proceder, essa seção vai apresentar um primeiroproblema de projeto, a partir do qual serão exploradas as alternativas de solu-ção. Apesar de grande parte dos problemas ser de sistemas �ctícios e criadospara ilustrar a aplicação dos padrões, as situações apresentadas por eles sãorealistas e aplicáveis em so�wares reais.

Considere o sistema de um estacionamento que precisa utilizar diversoscritérios para calcular o valor que deve ser cobrado de seus clientes. Para um

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 35: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Entendendo padrões de projeto

veículo de passeio, o valor deve ser calculado como R��,�� por hora, porém,caso o tempo sejamaior do que ��horas, será cobrada uma taxa diária, e caso onúmero de dias formaior que �� dias, será cobrada umamensalidade. Existemtambémregras diferentes para caminhões, que dependemdonúmero de eixose do valor da carga carregada, e para veículos para muitos passageiros, comoônibus e vans. O código a seguir apresenta um exemplo de como isto estavadesenvolvido.

public class ContaEstacionamento {

private Veiculo veiculo;private long inicio, fim;

public double valorConta() {long atual = (fim==0) ? System.currentTimeMillis():fim;long periodo = inicio - atual;if (veiculo instanceof Passeio) {

if (periodo < 12 * HORA) {return 2.0 * Math.ceil(periodo / HORA);

}else if (periodo > 12 * HORA && periodo < 15*DIA) {return 26.0 * Math.ceil(periodo / DIA);

} else {return 300.0 * Math.ceil(periodo / MES);

}} else if (veiculo instanceof Carga) {

// outras regras para veículos de carga}// outras regras para outros tipos de veículo

}

}

Como é possível observar, o código utilizado para calcular os diversoscondicionais é complicado de se entender. Apesar de apenas parte da im-plementação do método ter sido apresentada, pode-se perceber que a solu-ção utilizada faz com que o método valorConta() seja bem grande. Asinstâncias da classe ContaEstacionamento relacionadas com os veículosque estão no momento estacionados são exibidas para o operador e têm seu

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 36: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. O primeiro problema: cálculo do valor do estacionamento Casa do Código

tempo atualizado periodicamente.Até então, o próprio desenvolvedor sabia que o código estava ruim, mas

como o código estava funcionando, preferiu deixar da forma que está. Porém,o so�ware começou a ser vendido para outras empresas e outras regras preci-sariam ser incluídas. Algunsmunicípios possuem leis especí�cas a respeito dointervalo de tempo para o qual um estacionamento de�nir sua tarifa. Alémdisso, diferentes empresas podem possuir diferentes critérios para cobrar oserviço de seus clientes.

A solução da forma como está não irá escalar para um número grande deregras! O código que já não estava bom poderia crescer de uma forma des-controlada e se tornar não gerenciável. O desenvolvedor sabia que precisariarefatorar esse código para uma solução diferente. O que ele poderia fazer?

Usando herança

A primeira ideia que o desenvolvedor pensou foi na utilização de herançapara tentar dividir a lógica. Sua ideia era criar uma superclasse em que ocálculo do valor da conta seria representado por um método abstrato, o qualseria implementado pelas subclasses com cada uma das regras. A �gura �.�apresenta como seria essa solução.

Fig. �.�: Utilização de herança para dividir a lógica

Um dos problemas dessa solução é a explosão de subclasses que vai acon-tecer, devido às várias possibilidades de implementação. Outra questão é queutilizando herança, depois não é possível alterar o comportamento uma vez

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 37: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Entendendo padrões de projeto

que classe foi instanciada. Por exemplo, depois de �� horas que um veículo es-tiver estacionado, o comportamento do cálculo da tarifa deve ser alterado daabordagempor hora para a abordagempor dia. Quando se cria o objeto comosendo de uma classe, para mudar o comportamento que ela implementa, épreciso criar uma nova instância de outra classe. Isso é algo indesejável, poisa mudança deveria acontecer dentro da própria classe!

A segunda solução que o desenvolvedor pensou foi utilizar a herança, mascom uma granularidade diferente. Sua ideia era que cada subclasse possuísseo código relacionado a uma abordagem de cobrança para um tipo de veí-culo. Por exemplo, no caso acima, seria apenas uma subclasse para veículosde passeio e ela conteria os condicionais de tempo necessários. Dessa forma,a mesma instância seria capaz de fazer o cálculo.

Ao começar a seguir essa ideia, o desenvolvedor percebeu que nas sub-classes criadas ocorria bastante duplicação de código. Um dos motivos é quea cada pequena diferença na abordagem, uma nova subclasse precisaria sercriada. Se a mudança fosse pequena, o resto do código comum precisaria serduplicado. A �gura �.� ilustra essa questão. Uma das classes considera a tarifade veículo de passeio como apresentado anteriormente e a outra consideraque a primeira hora precisa ser dividida em períodos de ��minutos (uma leique existe nomunicípio de Juiz de Fora) e em seguida faz a cobrança por horacomo mostrada anteriormente. Observe que se houvesse uma classe com osperíodos de ��minutos mais a cobrança por dia e por mês, mais código aindaseria duplicado.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 38: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. O primeiro problema: cálculo do valor do estacionamento Casa do Código

Fig. �.�: Duplicação de código com a implementação menos granular

Mas será que é possível ter uma forma de manter a mesma instância deContaEstacionamento sem precisar duplicar código?

Pensando em composição

Ao pensar melhor, o desenvolvedor decide que a herança não é a melhorabordagem para resolver o problema. Ele precisa de uma solução que per-mita que diferentes algoritmos de cálculo de tarifa possam ser utilizados pelaclasse. Adicionalmente, é desejável que não haja duplicação de código e queo mesmo algoritmo de cálculo possa ser utilizado para diferentes empresas.Além disso, uma classe deve poder iniciar a execução com um algoritmo e omesmo ser trocado posteriormente.

Uma solução que se encaixa nos requisitos descritos é a classeContaEstacionamento delegar a lógica de cálculo para a instância de uma

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 39: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Entendendo padrões de projeto

classe que a compõe. Dessa forma, para trocar o comportamento do cálculodo valor do estacionamento, basta inserir outra instância dessa classe, comoutra estratégia de calculo, em ContaEstacionamento. Essa classe tambémpoderia ser parametrizada e ser reutilizada no contexto de diversas empresas.A �gura �.�mostra o diagrama que representa essa solução.

ContaEstacionamento delegar a lógica de cálculo para a instância de umaclasse que a compõe. Desse forma, para trocar o comportamento do cálculodo valor do estacionamento, basta inserir/atribuir outra instância(com outraestratégia de calculo) dessa classe em ContaEstacionamento.

Fig. �.�: Utilizando a composição para permitir a troca do algoritmo

Agora apresentamos a classe ContaEstacionamento delegando parauma classe que a compõe o calculo do valor do estacionamento. A interfaceCalculoValor abstrai o algoritmo de cálculo da tarifa. Observe que existeum método que permite que o atributo calculo seja alterado, permitindoa mudança desse algoritmo depois que o objeto foi criado.

public class ContaEstacionamento {

private CalculoValor calculo;

private Veiculo veiculo;private long inicio;private long fim;

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 40: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. O primeiro problema: cálculo do valor do estacionamento Casa do Código

public double valorConta() {return calculo.calcular(fim-inicio, veiculo);

}

public void setCalculo(CalculoValor calculo){this.calculo = calculo;

}}

A seguir, a classe CalculoDiaria mostra um exemplo de uma classeque faz o cálculo da tarifa por dia. Observe que essa classe possui um atributoque pode ser utilizado para parametrizar partes do algoritmo. Dessa forma,quando a estratégia for alterada para o calculo do valor por dia, basta inserira instância dessa classe em ContaEstacionamento. Vale também ressaltarque essa mesma classe pode ser reaproveitada para diferentes empresas emdiferentes momentos, evitando assim a duplicação de código.

Listagem �.� - Exemplo de classe que faz o cálculo da tarifa:

public class CalculoDiaria implements CalculoValor {

private double valorDiaria;

public CalculoDiaria(double valorDiaria){this.valorDiaria = valorDiaria;

}

public double calcular(long periodo, Veiculo veiculo) {return valorDiaria * Math.ceil(periodo / HORA);

}}

Reconhecendo a recorrência da solução

Após implementar a solução, o desenvolvedor, orgulhoso de sua capaci-dade de modelagem, começa a pensar que essa mesma solução pode ser utili-zada em outras situações. Quando houver um algoritmo que pode variar, essaé uma forma de permitir que novas implementações do algoritmo possam ser

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 41: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Entendendo padrões de projeto

plugadas no so�ware. Por exemplo, o cálculo de impostos, como pode variarde acordo com a cidade, poderia utilizar uma solução parecida.

Na verdade, ele se lembrou de que já tinha visto uma solução parecidaem um sistema com que havia trabalhado. Como o algoritmo de criptogra�aque era utilizado para enviar um arquivo pela rede poderia ser trocado, exis-tia uma abstração comum a todos eles que era utilizada para representá-los.Dessa forma, a classe que enviava o arquivo, era composta pela classe como algoritmo. Sendo assim, o desenvolvedor decidiu conversar com o colegaa respeito da coincidência da solução utilizada. Sua surpresa foi quando eledisse que não era uma coincidência, pois ambos haviam utilizado o padrãode projeto Strategy.

E� �� ���� ���� � ��� ����� ��� ��� �� �������

Uma reação comum quando certas pessoas têm seu primeiro contatocom padrões é ver que já utilizou aquela solução anteriormente. Isso énormal, pois o próprio nome “padrão” signi�ca que aquela é uma so-lução que já foi utilizada com sucesso em diversos contextos. Nesse mo-mento, algumas pessoas pensam: Para que eu preciso saber os padrões seeu posso chegar a essas soluções sozinho?.

Observe que para o desenvolvedor chegar a essa solução ele demo-rou um certo tempo. Mesmo assim, pode haver outras soluções que eledeixou de explorar e considerar, e pode haver consequências que muitasvezes não são levadas em conta. Ao aprender padrões, o desenvolvedoradquire o conhecimento não apenas da estrutura empregada, mas sobreo contexto em que ele é utilizado e as trocas feitas para se equilibrar osrequisitos. Dessa forma, não se engane achando que os padrões não irãolhe acrescentar conhecimento, pois mesmo os desenvolvedores mais ex-perientes aprendem muito com eles .

�.� S�������: � �������� �������“Por mais bonita que seja a estratégia, ocasionalmente deve-se olhar osresultados.”

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 42: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Strategy: o primeiro padrão! Casa do Código

– - Sir Winston Churchill

O Strategy é umpadrão que deve ser utilizado quando uma classe pos-suir diversos algoritmos que possam ser utilizados de forma intercambiável.A solução proposta pelo padrão consiste em delegar a execução do algoritmopara uma instância que compõe a classe principal. Dessa forma, quando afuncionalidade for invocada, nomomento de execução do algoritmo, será in-vocado um método da instância que a compõe. A �gura �.� apresenta umdiagrama que mostra a estrutura básica do padrão.

Fig. �.�: Estrutura do padrão Strategy

Uma das partes principais de um padrão diz respeito às suas consequên-cias, pois é a partir delas que o desenvolvedor vai avaliar se essa é uma boaescolha ou não para seus requisitos. No caso do Strategy, a principal con-sequência positiva é justamente o fato de o algoritmo poder ser alterado sema modi�cação da classe. A partir dessa estrutura, novas implementações delepodem ser criadas e introduzidas posteriormente.

Outro ponto positivo do padrão está no fato da lógica condicional naclasse principal ser reduzida. Como a escolha do algoritmo está na imple-mentação do objeto que está compondo a classe, isso elimina a necessidadede ter condicionais para selecionar a lógica a ser executada. Outra consequên-cia positiva está no fato de a implementação poder ser trocada em tempo deexecução, fazendo que o comportamento da classe possa ser trocado dinami-camente.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 43: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Entendendo padrões de projeto

Nomundo dos padrões, vale a expressão “nem tudo são �ores” , e é impor-tante conhecer as consequências negativas do padrão que está sendoutilizado.No caso do Strategy, isso acontece no aumento da complexidade na cri-ação do objeto, pois a instância da dependência precisa ser criada e con�gu-rada. Caso o atributo seja nulo, a classe pode apresentar um comportamentoinesperado. Outro problema dessa solução está no aumento do número declasses: há uma para cada algoritmo, criando uma maior di�culdade em seugerenciamento.

�.� O ��� ��� ��������Um padrão descreve um conjunto composto por um contexto, um problemae uma solução. Em outras palavras, pode-se descrever um padrão como umasolução para um determinado problema em um contexto. Porém um padrãonão descreve qualquer solução, mas uma solução que já tenha sido utilizadacom sucesso emmais de um contexto. Exatamente por esse motivo que a des-crição dos padrões normalmente sempre indica alguns de seus usos conheci-dos. Um padrão não descreve soluções novas, mas soluções consolidadas!

A ideia dos padrões vem do trabalho de Christopher Alexander na áreade arquitetura de cidades e construções [�]. Neste livro, cada padrão traziauma solução adequada a umproblema, que poderia ser reutilizada e adaptadapara diversas situações. A disseminação dos padrões na comunidade de de-senvolvimento de so�ware iniciou-se com o conhecido livroDesign Patterns:Elements of Reusable Object-Oriented So�ware [��]. Ele descrevia soluções deprojeto orientado a objetos que são utilizadas até hoje por desenvolvedoresde todo mundo. Esse livro também é conhecido comoGoF, um acrônimo deGang of Four, uma referência aos seus quatro autores.

Para ser um padrão, uma solução não basta ser recorrente, mas precisaser uma boa solução (caso contrário é um anti-padrão!). Além disso, é co-mum que ela traga outras partes, como a estrutura da solução, como funcionasua dinâmica e as consequências positivas e negativas de sua aplicação. Ou-tra parte importante é o relacionamento com os outros padrões, pois mostraoutros que oferecem uma outra alternativa, ou padrões que podem comple-mentar essa solução para compensar suas desvantagens.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 44: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. O que são padrões? Casa do Código

Muitos desenvolvedores acham que o padrão se resume à sua estrutura,normalmente representada através de um diagrama de classes, e esquecem detodo o resto de sua descrição. Isso é um uso inadequado dos padrões, pois sea solução for utilizada no contexto errado, ela pode atrapalhar mais que aju-dar. Se observarmos todos os padrões, é possível observar que as estruturasde alguns são bastante similares. Porém os problemas que essas estruturasvão resolver podem ser completamente diferentes. Por outro lado, a estru-tura apresentada no padrão é apenas uma referência, pois é possível aplicá-locom diversas adaptações estruturais de acordo com as necessidades da apli-cação. É por esse motivo que existem fortes críticas a ferramentas possuemuma abordagem de gerar a estrutura de padrões de forma automática.

Padrões não se re�etem em pedaços de código ou componentes que sãoreutilizados de forma igual em diversas aplicações, eles são um conhecimentoque deve estar na cabeça dos desenvolvedores. A partir desse conhecimento,os desenvolvedores devem avaliar o contexto e o problema que querem resol-ver e adaptar, e combinar padrões de forma a equilibrar as forças envolvidas.É a partir desse conhecimento que os padrões ajudam os desenvolvedores amelhorarem sua habilidade em modelagem orientada a objetos.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 45: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Entendendo padrões de projeto

Q����� ���� ������� �� ��������, ������ ��� ����� ���� �������

Claro que não! Como o padrão é uma solução para um problema,aplicá-lo onde o problema não existe irá apenas complicar as coisas. Issoseria como para uma pessoa que acabou a aprender a usar um martelo,ver todos os problemas como se fossem um prego. Por mais que umparafuso possa parecer com um prego, a solução é bem diferente.

O mesmo vale para os padrões. Não é só porque você aprendeu umnovo padrão, que precisa ir utilizando-o em todas as soluções. Lembre-seque eles também possuem consequências negativas que podem se sobre-por às vantagens em alguns casos. Procure possuir diversos padrões emsua caixa de ferramentas e utilizar o mais adequado para a cada situa-ção. Sua utilização desnecessária pode ser desastrosa e gerar uma sobre-engenharia do sistema, o que, no mínimo, di�culta a manutenção do có-digo.

Os padrões também adicionam à equipe uma terminologia para se refe-rir a soluções de modelagem, criando um vocabulário compartilhado. Dessaforma, quando alguém mencionar o nome de um padrão, todos saberão doque se trata. Esse nome não se refere apenas à estrutura, mas a todas as infor-mações agregadas a ele, como sua dinâmica e suas consequências.

Este livro não irá descrevê-los em um formato tradicional, mas irá utilizá-los para exempli�car técnicas que podem ser utilizadas para projetar um so�-ware. Cada capítulo irá focar em uma técnica ou em um tipo de problema,e então serão apresentados os padrões relacionados com exemplos que irãoilustrar sua implementação. Também serão discutidas as diferentes alternati-vas de solução e as consequências positivas e negativas trazidas por cada umadelas. O objetivo é apresentar importantes técnicas de projeto orientado aobjetos a partir da aplicação de padrões.

Todos os padrões apresentados no livro utilizarão este estilo, entãotoda vez que vir um nome nesse estilo, saiba que está fazendo referência a umpadrão. Seus nomes serão mantidos em inglês devido ao fato de serem refe-renciados dessa forma pela comunidade de desenvolvimento de so�ware no

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 46: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Como o livro está organizado Casa do Código

Brasil, sendo que paramuitos deles não existe uma tradução adequada. Os ca-pítulos do livro podem ser lidos de forma independente, porém eles podemfazer referência a padrões explicados em capítulos anteriores. Para desen-volvedores inexperientes, recomenda-se a leitura dos capítulos na ordem deapresentação.

Grande parte dos padrões utilizados nesse livro são os contidos no [��],porém outros importantes de outras fontes também estão incluídos. Quandonenhuma referência for provida com a primeira citação ao padrão, isso signi-�cará que é um padrão desse livro.

Chegando aos padrões pela refatoração

Existem basicamente duas formas para se implementar os padrões no seucódigo. Aprimeira delas é a sua introdução namodelagemantes do código serdesenvolvido. Nesse caso, é feita uma análise preliminar do problema, em queé identi�cado o contexto no qual é adequada a aplicação do padrão. A outraabordagem é através da refatoração [��], uma técnica no qual o código é re-estruturado de forma a não haver modi�cação de seu comportamento. Nessecontexto, o código já foi desenvolvido, porém apresenta algum tipo de de�-ciência em termos de projeto, situação conhecida como mau cheiro. Sendoassim, o código vai sendo alterado em pequenos passos até que se chegue aopadrão alvo.

Saber como utilizar refatoração para se chegar a padrões é uma técnicaimportante [��], pois nem sempre a solução mais adequada é a que é empre-gada na primeira vez. Esse livro irá apresentar, juntamente com os padrões,como pode surgir a necessidade de sua aplicação em um código existente equais os passos de refatoração que podem ser feitos para sua implementação.No exemplo apresentado nesse capítulo, foi mostrado um código problemá-tico já existente que foi refatorado para implementar o padrão Strategy.Os passos da refatoração para ele serão mostrados no capítulo �.

�.� C��� � ����� ���� ����������Esta seção irá fechar esse capítulo inicial, falando um pouco sobre a organi-zação do livro. Aqui os padrões são apresentados de uma forma distinta de

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 47: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Entendendo padrões de projeto

outros livros. Os capítulos são organizados de acordo com o princípio de de-sign utilizado pelo padrão. Dessa forma, eles servirão para exempli�car tiposde problema que podem ser resolvidos com aquela técnica. Serão apresenta-dos exemplos, que além de mostrar a aplicação do padrão de forma práticatambém irão ilustrar como eles podem ser combinados.

O capítulo � começa explorando de forma mais profunda a utilização deherança e quais são os padrões que fazem uso desse princípio para permitir aextensão de comportamento. De forma complementar, o capítulo � contrastao uso da herança com o uso da composição, ressaltando as principais diferen-ças entre elas. Nesse ponto, diversos padrões que fazem uso da composiçãoserão apresentados, inclusive de forma combinada com a herança.

Seguindo a linha de raciocínio, o capítulo � apresenta a composição re-cursiva, e como ela pode ser utilizada para facilitar o reaproveitamento decódigo. A partir dos padrões que utilizam esse princípio é possível combi-nar o comportamento de diversas classes de formas diferentes, obtendo umagrande quantidade de possibilidades de comportamento �nal. Em seguida,o capítulo �mostra como a composição recursiva também pode ser utilizadapara o encapsulamento de objetos, de forma a permitir que funcionalidadessejam adicionadas na classe de forma transparente aos seus clientes.

O capítulo � trata da criação de objetos e dos diferentes padrões quepodem ser utilizados para resolver problemas, como uma lógica de criaçãocomplexa ou a criação de objetos de classes relacionadas. Ainda tratandodesse tema, o capítulo � aborda questões de modularidade dentro da aplica-ção, apresentando diferentes técnicas para permitir que uma classe obtenhaa instância de outra sem depender diretamente dela. Isso permite que no-vas implementações possam ser plugadas sem a necessidade de recompilar aaplicação.

Em seguida, enquanto os capítulos iniciais apresentaram técnicas para al-terar a implementação e, consequentemente, o comportamento de funciona-lidades existentes, o capítulo � mostra técnicas para que novas funcionali-dades e operações possam ser adicionadas em classes existentes. E, de formacomplementar, o capítulo �mostra como a complexidade de uma grande apli-cação pode ser gerenciada através da criação de subsistemas e do encapsula-mento da gerência da comunicação entre os objetos.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 48: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Como o livro está organizado Casa do Código

Finalizando o livro, o capítulo �� não apresenta nenhum novo padrão,mas alguns tópicos avançados e complementares ao que foi apresentado aolongo de todo livro. Esses tópicos incluem questões sobre o desenvolvimentode frameworks, utilização de tipos genéricos em padrões e da utilização depadrões em um processo TDD. Por �m, ele termina falando um pouco dacultura da comunidade que existe no Brasil e nomundo para o estudo e iden-ti�cação de novos padrões.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 49: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

C������� �

Reúso através de herança

“Entidades de so�ware devem ser abertas a extensão, mas fechadas amodi�cação.”– Bertrand Meyer

A herança é uma das principais funcionalidades de linguagens orientadasa objetos. É a partir dela que é possível grande parte do potencial de reúso.O problema é que muita gente que diz isso para por aí e ainda busca o reúsoatravés da herança de forma errada.

O primeiro pensamento que normalmente temos ao se estender umaclasse é reutilizar a lógica da superclasse na subclasse. Será que isso é mesmoverdade? O que uma subclasse pode reutilizar da superclasse? A estrutura dedados? Seus métodos? A reutilização da estrutura de dados na herança nãoé muito importante, pois simplesmente instanciando e utilizando uma classeisso é possível de ser feito. A utilização dos métodos da superclasse pela sub-

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 50: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Exemplo de padrão que utiliza herança - Null Object Casa do Código

classe é equivalente ao reúso de funções na programação estruturada, nãosendo também grande novidade.

O potencial de reúso possível com herança está em outro local! Ele podeestar simno reúso de código da superclasse, porémnão é com a subclasse cha-mando métodos da superclasse, mas com a superclasse chamando código dasubclasse. Quando um método da superclasse chama um método que podeou deve ser implementado na subclasse, isso permite que um mesmo algo-ritmo possa ser reutilizado com passos alterados. Essa �exibilidade aumentao potencial de reutilização pois permite a sua adaptação para necessidadesmais especí�cas.

Outro local onde o código pode ser reusado é nas classes que utilizamuma variável com o tipo da superclasse. Nesse caso, como as instâncias dassubclasses podem ser atribuídas a essa variável, é possível adaptar o compor-tamento segundo a instância utilizada. Resumindo em apenas uma palavra:polimor�smo! Por exemplo, o algoritmo de ordenação de listas pode ser reu-tilizado em diversas aplicações para ordenação de listas de diversas classes.Isso só é possível pois todas as classes compartilham a mesma abstração dotipo Comparable.

O objetivo desse capítulo é mostrar como a herança pode ser utilizada emum design orientado a objetos para permitir adaptação de comportamento econsequentemente ummaior reúso. Isso será feito apresentando padrões queutilizam os princípios da herança descritos e como eles podem ser utilizadospara criação de soluções.

�.� E������ �� ������ ��� ������� ������� -N���O�����

“O que é invisível para nós é também crucial para nosso bem estar.”– Jeanette Winterson

Para começar a falar de herança, essa seção apresenta um padrão bemsimples, porém bastante útil. Apesar de não ter sido incluído no GoF, emuma entrevista recente, um dos autores confessou que o colocaria em umahipotética segunda edição do livro [��]. Ele irá ilustrar como com o uso da

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 51: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Reúso através de herança

herança é possível “enganar” o código que utiliza a classe, introduzindo umnovo comportamento que irá eliminar a necessidade do uso de condicionais.Como para o código cliente o comportamento da classe é invisível, o dina-mismo desse comportamento pode ser a chave para se lidar com situaçõesdiferentes.

Vou começar citando uma situação que certamente muitos já viram emalgum código com que trabalharam. A classe ApresentacaoCarrinho

apresentada na listagem a seguir chama o método criarCarrinho() daclasse CookieFactory para recuperar um carrinho previamente criadopelo usuário a partir dos cookies armazenados em seu navegador. Uma veztendo os valores recuperados, são de�nidos atributos para a exibição dasinformações do carrinho na parte superior da página do usuário. O grandeproblema desse código é que, caso nenhum carrinho tenha sido criado, eleretorna um valor nulo. Daí é preciso adicionar condicionais para con�guraros valores adequados para quando o carrinho for nulo.

Listagem �.� - Condicionais para proteção de valores nulos:

public class ApresentacaoCarrinho{

public voidcolocarInformacoesCarrinho(HTTPServletRequest request) {

Carrinho c = CookieFactory.criarCarrinho(request);if(c != null) {

request.setAttribute("valor", c.getValor());request.setAttribute("qtd", c.getTamanho());

} else {request.setAttribute("valor", 0.0);request.setAttribute("qtd", 0);

}if(request.getAttribute("username") == null) {

if (c != null) {request.setAttribute("userCarrinho",

c.getNomeUsuario());} else {

request.setAttribute("userCarrinho","<a href=login.jsp>Login</a>");

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 52: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Exemplo de padrão que utiliza herança - Null Object Casa do Código

}} else {

request.setAttribute("userCarrinho",request.getAttribute("username"));

}}

}

Se esse problema se tornar recorrente, ele pode ser uma verdadeira catás-trofe na legibilidade do código de uma aplicação. É comum ver condicionaisrepetidos para tratar em vários pontos a possibilidade de uma determinadavariável ser nula. Esses condicionais garantem a segurança do código apenasnaquele ponto, pois nada garante que esse valor será tratado adequadamenteem outros locais. Mas como então fazer para proteger minha aplicação de umNullPointerException sem precisar recorrer a esse tipo de condicional?

Criando uma classe para representar valores nulos

O padrão Null Object [��] propõe a criação de uma classe para re-presentar objetos nulos em uma aplicação. Essa classe deve estender a classeoriginal e implementar seus métodos de forma a executar o comportamentoesperado da aplicação quando um valor nulo for recebido. Dessa forma, emvez de se retornar um valor nulo, retorna-se uma instância dessa nova classe.

A classe CarrinhoNulo apresentada na listagem a seguir exempli�ca acriação de um Null Object para esse contexto. Observe que os métodosretornam exatamente os valores que eram con�gurados para o caso docarrinho ser nulo. Nesse exemplo, apenas valores são retornados, porémem outros casos é preciso ter uma lógica a ser executada dentro dosmétodos.

Listagem �.� - De�nição da classe para representar o carrinho nulo:

public class CarrinhoNulo extends Carrinho{public double getValor(){

return 0.0;}

public int getTamanho(){

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 53: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Reúso através de herança

return 0;}

public String getNomeUsuario(){return "<a href=login.jsp>Login</a>";

}}

Na listagem a seguir, é possível observar como o código �ca mais simplescom a eliminação da parte responsável pelo tratamento dos valores nulos.Outra coisa que precisaria ser alterada é a própria classe CookieFactory

que deve retornar uma instância de CarrinhoNulo em vez de null

quando nenhum carrinho do usuário for encontrado.

Listagem �.� - Código após a introdução do Null Object:

public class ApresentacaoCarrinho{public void

colocarInformacoesCarrinho(HTTPServletRequest request) {Carrinho c = CookieFactory.criarCarrinho(request);request.setAttribute("valor", c.getValor());request.setAttribute("qtd", c.getTamanho());

if(request.getAttribute("username") == null) {request.setAttribute("userCarrinho",

c.getNomeUsuario());} else {

request.setAttribute("userCarrinho",request.getAttribute("username"));

}}

}

Uma consequência interessante da aplicação desse padrão é que ele re-solve o problema do tratamento de valores nulos em qualquer ponto da apli-cação que utilize essa classe. Por mais que o método apresentado no exemplotratasse esses valores, a mesma situação poderia acontecer em partes do có-digo que não o fazem, gerando a possibilidade de erro. Apesar das vantagens,

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 54: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Exemplo de padrão que utiliza herança - Null Object Casa do Código

o tratamento dos valores nulos não �ca explícito e isso pode gerar uma certaconfusão na hora de ler e dar manutenção nesse código.

O uso da herança no Null Object

Uma das coisas interessantes desse exemplo é como o código que utili-zava o Null Object desconhecia completamente que ele estava ali. Ele co-nhecia apenas a interface da superclasse, mas utilizou as funcionalidades dasubclasse. Na verdade, esse é o principal motivo para a utilização da herançano projeto de um so�ware: o uso do polimor�smo. Com ele é possível reuti-lizar o código cliente para diversas implementações. Inclusive, um bom testepara veri�car se o uso de herança é adequado em uma determinada situação éver se faz sentido substituir a implementação por uma de suas subclasses emtodos os contextos.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 55: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Reúso através de herança

H����� �� ��� ������ �� JP����� E�� � ��������

Eu vejo essa questão da utilização ou não da herança é no desen-volvimento de aplicações desktop em Swing. Ao se criar uma nova telapara aplicação, uma dúvida comum é se essa classe deve estender a classeJPanel ou simplesmente utilizá-la. A extensão de JPanel pode fazersentido a princípio, visto que a nova classe será um painel (obedecendo afamosa regra é um ) e que pode ser adicionada em uma interface grá�ca.

Por outro lado, se pensarmos nos princípios da herança, uma sub-classe deve poder ser utilizada no lugar de sua superclasse. Raciocinandodessa forma, é muito fácil pensar em diversas situações em que a classecom a tela de uma aplicação não faria sentido de ser utilizada no lugarde um painel genérico. Isso também quebraria o contrato da superclasse,visto que diversos métodos, como para adição de componentes e con�-guração de layout deixariam de fazer sentido.

Somente veri�car se a subclasse é uma superclasse pode não ser su�ci-ente. Antes de utilizar herança veri�que se o polimor�smo faz sentido, ouseja, se qualquer subclasse pode ser utilizada no lugar da superclasse. Emcaso negativo, isso é um indício de que a herança está sendo utilizada deforma inadequada. Esse é conhecido como o Princípio de Substituiçãode Liskov, que defende que se uma classe é um subtipo de outra, entãoos objetos dessa classe podem ser substituídos pelos objetos do subtiposem que seja necessário alterar as propriedades do programa.

O NullObject é um padrão que demonstra bem essa características daherança, pois ele permite que o código cliente possa ser utilizado, mesmo parao caso de umobjeto nulo. Observe que outras implementações de Carrinho,como um que talvez acesse informações remotamente, também poderiam serretornadas pela fábrica e utilizadas livremente.

�.� H���M������Um importante uso que pode ser feito da herança é para permitir a especi-alização de comportamento. Dessa forma, a superclasse pode fornecer uma

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 56: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Hook Methods Casa do Código

base para uma determinada funcionalidade, a qual invoca um método quesomente é de�nido pela superclasse. Esse método funciona como um pontode extensão do sistema é chamado de método-gancho, ou em inglês, hookmethod.

A �gura �.� representa o conceito de hook method. A superclasse possuium método principal público que é invocado pelos seus clientes. Esse mé-todo delega parte de sua execução para o hook method, que é um métodoabstrato que deve ser implementado pela subclasse. Ele funciona como um“gancho” no qual uma nova lógica de execução para a classe pode ser “pen-durada”. Cada subclasse o implementa provendo uma lógica diferente. Comoessa lógica pode ser invocada a partir do mesmométodo público, de�nido nasuperclasse, os hook methods permitem que o objeto possua um comporta-mento diferente de acordo com a subclasse instanciada.

Fig. �.�: Representação de Hook Methods

Essa prática é muito utilizada em frameworks para permitir que as apli-cações possam especializar seu comportamento para seus requisitos. Nessecaso, o framework provê algumas classes com hook methods que precisam

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 57: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Reúso através de herança

ser especializadas pela aplicação. Sendo assim, a aplicação deve estender essaclasse e implementar o hook method de forma a inserir o comportamento es-pecí�co de seu domínio. Imagine, por exemplo, um framework que realiza oagendamento de tarefas. Usando essa estratégia ele poderia prover uma classepara ser estendida, na qual precisaria ser implementado um hookmethod paraa de�nição da tarefa da aplicação. Enquanto isso, na classe do framework es-taria a lógica de agendamento que chamaria ométodo de�nido pela aplicaçãono momento adequado.

Um exemplo de uso dessa técnica está na Servlets API [��], na qual umservlet precisa estender a classe HTTPServlet. Essa classe possui o métodoservice() que é invocado toda vez que ele precisa tratar uma requisiçãoHTTP. Esse método chama outros métodos, como doPost() ou doGet(),que precisam ser implementados pela subclasse. Neles ela deve inserir a lógicaa ser executada para tratar a requisição recebida. Nesse caso, esses métodospossuemuma implementação default vazia e precisam ser implementados so-mente se necessário.

�.� R�������� ������������� �� �������Quando aprendemos o básico da linguagem, aprendemos modi�cadores deacesso e outros tipos de modi�cadores de métodos e qual o seu efeito nasclasses. O que nem todos compreendem é quando cada um deles precisa serutilizado e qual o tipo de mensagem que se passa com cada um em termosde design. A seguir estão relacionados alguns modi�cadores de acesso e qualo recado que uma classe manda para suas subclasses quando utiliza cada umdeles:

• abstract: Um método abstrato precisa obrigatoriamente ser imple-mentado em uma subclasse concreta. Isso signi�ca que esse é um hookmethod que foi de�nido na superclasse e obrigatoriamente precisa serde�nido.

• final: De forma contrária, um método do tipo �nal não pode sersobrescrito pelas subclasses e por isso nunca irá representar um hookmethod. Essesmétodos representam funcionalidades da classe que pre-

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 58: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Passos diferentes na mesma ordem - Template Method Casa do Código

cisam ser imutáveis para seu bom funcionamento. Costumam ser osmétodos que invocam os hook methods.

• private: Os métodos privados só podem ser invocados dentro daclasse que são de�nidos. Dessa forma, eles representam métodos deapoio internos da classe e nunca poderão ser substituídos pela subclassecomo um hook method.

• protected e public: Todos osmétodos públicos e protegidos, desdeque não sejam final, são candidatos a hook method. É isso mesmo!Qualquer método que pode ser sobrescrito na subclasse pode ser uti-lizado para a inserção de comportamento. Algumas classes possuemimplementações vazias para esses métodos, o que faz com que sejamhook methods com implementação opcional pelas subclasses.

Ao criar uma estrutura de classes, é importante colocar os modi�cadorescorretos nos métodos de forma a passar a mensagem certa para as classesque forem estendê-la. Por exemplo, se um método que coordena a chamadade hook methods for sobrescrito, eles podem não ser chamados e algumaspremissas assumidas na superclasse podem não ser mais verdadeiras. Porisso é preciso muito cuidado ao prover uma classe para ser estendida comoparte da API de um componente ou framework.

�.� P����� ���������� �� ����� ����� - T��-�����M�����

“No meio do caminho tinha uma pedra / tinha uma pedra no meio docaminho / tinha uma pedra / no meio do caminho tinha uma pedra.”– Carlos Drummond de Andrade

O principal padrão que utiliza hookmethods como técnica é o Template

Method. Este padrão é aplicável quando se deseja de�nir um algoritmo geral,que estabelece uma série de passos para cumprir um requisito da aplicação.Porém, seus passos podem variar e é desejável que a estrutura da implemen-tação forneça uma forma para que eles sejam facilmente substituídos.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 59: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Reúso através de herança

Imagine, por exemplo, como seria se fôssemos de�nir um algoritmo paraas pessoas acordarem e irem ao trabalho. Esse algoritmo envolveria açõescomo acordar, ir ao banheiro, comer alguma coisa, trocar de roupa e se loco-mover até o trabalho. Dependendo da pessoa cada um dos passos pode serdiferente, como o tipo de roupa que colocam para ir ao trabalho ou o que co-mem pela manhã. Na locomoção ao trabalho, alguns podem ir de transportepúblico, outros podem ir em seu carro e até mesmo a pé ou de bicicleta. Pormais que cada um dos passos seja diferente, o algoritmo que os utiliza acabasendo o mesmo.

Estrutura do Template Method

Para entender a estrutura do Template Method vamos utilizar comometáfora algo familiar a muitas pessoas: modelos de documento. Conformeilustrado na �gura �.�, um modelo de documento de�ne algumas partes quesão �xas e outras partes que devem ser introduzidas quando o documentopropriamente dito for criado. São lacunas que precisam ser completadas e queirão variar de documento para documento. O modelo provê uma estruturaque pode ser facilmente reaproveitada, simpli�cando sua criação.

Fig. �.�: Uso de Templates para documentos

De forma similar, um Template Method é um modelo de algoritmoque possui algumas partes �xas e algumas partes variáveis. As partes variá-

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 60: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Passos diferentes na mesma ordem - Template Method Casa do Código

veis são lacunas que precisam ser completadas para que o algoritmo faça real-mente sentido. As lacunas são representadas como hook methods que podemser implementados nas subclasses. Caso seja uma lacuna obrigatória, o mé-todo deve ser de�nido como abstrato e caso a implementação seja opcional,o método pode ser concreto e normalmente possui uma implementação va-zia. O algoritmo é representado através de um método na superclasse quecoordena a execução dos hook methods.

A �gura �.� apresenta a estrutura do padrão Template Method.A ClasseAbstrata representa a superclasse que implementao TemplateMethod e que de�ne quais são os hook methods. AClasseConcreta representa a classe que herda o Template Method daClasseAbstrata e de�ne uma implementação concreta dos hook methods.A classe representada como Cliente invoca o metodoTemplate().Observe que apesar do tipo da variável ser do tipo da classe abstrata, otipo instanciado é o da subclasse que implementa os passos concretos doalgoritmo.

Fig. �.�: Estrutura do padrão Template Method

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 61: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Reúso através de herança

Q��� � ��������� ����� H��� M������ � � T�������M������

Muitas pessoas nesse ponto podem estar achando queHook Methodse o padrão Template Method são a mesma coisa. A grande diferençaé que osHookMethods são uma técnica para permitir a extensão de com-portamento e o Template Method, como um padrão, é uma soluçãopara um problema mais especí�co. Seria correto dizer que o Template

Method utiliza Hook Methods em sua solução. O que é importante per-ceber é que o conceito deHookMethod émais geral e inclusive é utilizadopor outros padrões que serão vistos mais à frente nesse livro.

Serializando propriedades em arquivos

Imagine que em uma aplicação precisamos pegar mapas de propriedadese serializar em arquivos. A questão é que os formatos em que as proprieda-des precisam ser estruturadas são diferentes. Por exemplo, pode ser possí-vel estruturar as informações em arquivos XML e em arquivos de proprieda-des. Além disso, os arquivos podem precisar receber um pós-processamento,como uma criptogra�a ou uma compactação. Para o exemplo que será apre-sentado iremos considerar que existem duas possibilidades: a criação do ar-quivo XML compactado e a criação do arquivo de propriedades criptogra-fado.

Como nesse caso temos um algoritmo base em que os passos po-dem ser modi�cados de acordo com a implementação, esse é um cená-rio adequado para implementação do padrão Template Method. Aclasse GeradorArquivo, apresentada na listagem a seguir, possui o métodogerarArquivo() que de�ne um algoritmo base para a criação do arquivo,invocando os dois métodos abstratos processar() e gerarConteudo().Esse método possui o modi�cador final para que ele não possa ser sobres-crito nas subclasses. Note que o método processar() fornece uma imple-mentação default que retorna o próprio array de bytes, representando o casoem que não existe processamento adicional após o arquivo ser gerado. Di-

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 62: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Passos diferentes na mesma ordem - Template Method Casa do Código

ferentemente, o método gerarConteudo() é abstrato e obrigatoriamenteprecisará ser implementado.

Listagem �.� - Classe que de�ne o Template Method:

public abstract class GeradorArquivo {public final void gerarArquivo(String nome,

Map<String, Object> propriedades)throws IOException {

String conteudo = gerarConteudo(propriedades);byte[] bytes = conteudo.getBytes();bytes = processar(bytes);FileOutputStream fileout = new FileOutputStream(nome);fileout.write(bytes);fileout.close();

}

protected byte[] processar(byte[] bytes) throws IOException{return bytes;

}

protected abstract StringgerarConteudo(Map<String, Object> propriedades);

}

As duas próximas listagens apresentam as classesGeradorXMLCompactado e GeradorPropriedadesCriptografado

que implementam a classe GeradorArquivo, fornecendo uma implemen-tação para os passos do algoritmos de�nidos na superclasse. A primeiraimplementação, no método gerarConteudo() cria um arquivo XMLno qual existe uma tag chamada <properties> e cada propriedade éum elemento dentro dessa tag. O método processar() usa a classeZipOutputStream para gerar um arquivo compactado. Já na segundalistagem, o método gerarConteudo() cria a estrutura de um arquivode propriedades em que cada linha possui o formato propriedade=valor. Ométodo de processamento criptografa os bytes do arquivo utilizando umalgoritmo simples chamado cifra de César, em que o valor de cada byte édeslocado de acordo com o parâmetro delay.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 63: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Reúso através de herança

Listagem �.� - Classe que gera o arquivo com XML compactado:

public class GeradorXMLCompactado extends GeradorArquivo {protected byte[] processar(byte[] bytes) throws IOException{

ByteArrayOutputStream byteOut =new ByteArrayOutputStream();

ZipOutputStream out = new ZipOutputStream(byteOut);out.putNextEntry(new ZipEntry("internal"));out.write(bytes);out.closeEntry();out.close();return byteOut.toByteArray();

}

protected String gerarConteudo(Map<String, Object> props) {StringBuilder propFileBuilder = new StringBuilder();propFileBuilder.append("<properties>");for(String prop : props.keySet()){

propFileBuilder.append("<"+prop+">"+props.get(prop)+"</"+prop+">");

}propFileBuilder.append("</properties>");return propFileBuilder.toString();

}}

Listagem �.� - Classe que gera arquivo de propriedades criptografado:

public class GeradorPropriedadesCriptografadoextends GeradorArquivo {

private int delay;

public GeradorPropriedadesCriptografado(int delay) {this.delay = delay;

}

protected byte[] processar(byte[] bytes) throws IOException{byte[] newBytes = new byte[bytes.length];for(int i=0;i<bytes.length;i++){

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 64: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Passos diferentes na mesma ordem - Template Method Casa do Código

newBytes[i]=(byte) ((bytes[i]+delay) % Byte.MAX_VALUE);

}return newBytes;

}

protected String gerarConteudo(Map<String, Object> props) {StringBuilder propFileBuilder = new StringBuilder();for(String prop : props.keySet()){

propFileBuilder.append(prop+"="+props.get(prop)+"\n");}return propFileBuilder.toString();

}}

Consequências do uso do Template Method

Pelo exemplo apresentado e pela descrição do padrão, é possível perce-ber que com o Template Method podemos reaproveitar o código relativoà parte comum de um algoritmo, permitindo que cada passo variável possaser de�nido na subclasse. Isso também é uma forma de permitir que a fun-cionalidade da classe que de�ne o algoritmo básico seja estendida. Assim épossível de�nir uma funcionalidade mais geral na qual pode ser facilmenteincorporada a parte especí�ca do domínio da aplicação. É importante lem-brar que os modi�cadores adequados dos métodos devem ser utilizados paraimpedir que o contrato da superclasse com os seus clientes seja quebrado.

Porém, como foi dito no primeiro capítulo, o uso da herança nesse padrãotambém traz algumas limitações. A primeira é que a herança “é uma cartaque só pode ser jogada uma vez” , isso signi�ca que uma classe que precise decomportamentos de duas outras classes só poderá fazer o uso da herança parauma delas. Essa questão serámais bem discutida no capítulo �. Outra questãoé que depois que uma implementação for instanciada não será mais possívelalterar os passos do algoritmo.

Ao utilizar um padrão, é preciso avaliar para os requisitos de sua aplica-ção quais consequências pesam mais ou menos. A partir dessas informaçõesé possível decidir se seu uso será ou não adequado. Ressalto que o maior pro-

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 65: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Reúso através de herança

blema de uma solução que possui limitações é quando elas são desconheci-das pelos desenvolvedores, pois quando se tem consciência de sua existência épossível gerenciar o risco ou tratá-las, muitas vezes a partir de outros padrões.

�.� R���������� �� ������� �� �������Muitas vezes, os requisitos que direcionam o desenvolvimento para o uso daherança não surgem todos ao mesmo tempo. Outras vezes, simplesmentenão se visualiza no momento que se está programando que aquele padrãopode ajudar na solução. Nesses casos, o padrão pode ser alcançado a partir derefatorações. A ideia é conseguir, a partir de pequenos passos, ir modi�candoaos poucos a estrutura da solução, semmodi�car o seu comportamento. Valeressaltar que é extremamente recomendado que a refatoração seja apoiada poruma suíte de testes automatizados, que devem ser executados a cada passo darefatoração para veri�car se o comportamento foi mantido.

D�������� ���� ��� ��� ������ � ������������� ������

Um grande erro ao se refatorar um código é destruir toda a soluçãoexistente e reconstruir do zero uma nova solução. Isso é ruim pois du-rante um longo período de tempo perde-se a referência dos testes auto-matizados, que dão o feedback se o comportamento está sendo mantidoinalterado. No decorrer desse livro, serão apresentados os passos que de-vem ser seguidos para se refatorar a solução na direção dos padrões apre-sentados. Com isso, pretende-se mostrar como que com uma sequênciade pequenas mudanças pode-se alcançar um grande impacto na quali-dade do código e na modelagem da aplicação.

No caso de necessidade de se refatorar para o uso da herança, existemdois cenários diferentes. No primeiro, toda a funcionalidade foi acumuladaem apenas uma classe, que possui um grande número de condicionais e umcódigo de difícil manutenção. O segundo cenário ocorre quando funciona-lidades com similaridades nos algoritmos são implementadas em diferentesclasses, gerando duplicação de código.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 66: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Refatorando na direção da herança Casa do Código

A �gura �.� apresenta os passos para a refatoração quando uma únicaclasse concentra em um método todas as possibilidades do algoritmo utili-zando condicionais. A primeira etapa é extrair os métodos de acordo com ospassos que devem ser realizados pelo algoritmo. Após a extração, cada mé-todo ainda possuirá a lógica condicional necessária para todos os possíveiscaminhos. Na etapa seguinte é criada uma subclasse para representar umaalternativa de execução do algoritmo e os passos seguintes consistem emmo-ver a funcionalidade para essa subclasse. Esse processo vai sendo repetidoaté que cada possível caminho seja movido para uma subclasse e somente alógica mais geral esteja concentrada na superclasse.

Para que isso seja feito, o primeiro passo é sobrescrever osmétodos extraí-dos na nova subclasse criada. A princípio, esses métodos ainda irão delegara execução da sua funcionalidade para a superclasse, fazendo uma chamadaaométodo original na forma super.original(). Caso exista algum parâ-metro setado na superclasse para a escolha dos passos do algoritmo, ele podeser con�gurado de forma �xa na subclasse, de acordo com o que será imple-mentado nela. A etapa seguinte consiste em mover para a subclasse a lógicarelativa aos métodos com os passos do algoritmo. Deve ser retirado o con-dicional na superclasse, juntamente com o código a ser executado, e inseridono método da subclasse, que não deve mais delegar sua execução para super-classe. Esse procedimento deve ser feito método por método. Em seguida, osmesmos passos devem ser repetidos para a criação de novas subclasses, atéque não exista mais lógica nesses métodos da superclasse. No caso de nãohaver uma implementação default, eles devem se tornar abstratos.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 67: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Reúso através de herança

Fig. �.�: Quebrando classe única com método grande e complexo

A outra possibilidade de refatoração deve ser realizada quando existiremduas classes que implementam algoritmos similares e possuam código dupli-cado. Conforme está apresentado na �gura �.�, a refatoração irá levar a refa-toração na direção da de�nição de uma superclasse comum que implementaum Template Method. A primeira coisa que precisa ser feita é a extraçãode métodos com as partes que diferenciam um algoritmo do outro. Depois

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 68: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Refatorando na direção da herança Casa do Código

dessa etapa, o código interno dos métodos principais deve ser igual e todas asdiferenças devem estar nosmétodos extraídos. Em seguida, para uniformizaras classes, é preciso renomear os métodos para que �quem commesmo nomee mesma assinatura em ambas as classes (incluindo tipos de parâmetros e ex-ceções). Devem-se buscar nomes que representem bem a parte do algoritmoabstraída por eles.

Fig. �.�: Criando superclasse para duas classes com código duplicado

Para seguir em direção a implementação do Template Method, o passoseguinte é criar uma superclasse que seja especializada por ambas as classes.A princípio essa classe estará vazia para que não con�ite com a implemen-tação de cada uma. Em seguida, deve-se subir para superclasse os métodos

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 69: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Reúso através de herança

extraídos, porém como métodos abstratos. Isso permitirá que na etapa se-guinte o método principal seja totalmente movido para superclasse e retiradode todas as subclasses.

Mau cheiro de código: Missing Template Method

Na terminologia de design de so�ware, ummau cheiro, ou bad smell, serefere a algum indício de problemas no código que podem signi�car de�ciên-cias em sua modelagem. Ummau cheiro é diferente de um bug, pois não ne-cessariamente ele causa um erro na aplicação, mas traz outras consequênciasnegativas como di�culdade de manutenção e falta de �exibilidade. Existemferramentas que fazem a detecção automática de maus cheiros, usando comobase suas métricas e características. O livro “Refatoração: aperfeiçoando oprojeto de código existente” [��] traz uma lista com alguns deles.

Um dos mau cheiros que são frequentemente detectados por essas ferra-mentas se chama “Missing Template Method” . Ele se refere a dois compo-nentes que possuem um número signi�cante de similaridades, mas não com-partilham de uma mesma abstração, como uma interface, ou de uma mesmaimplementação, como um Template Method. Ao se deparar com essa si-tuação, seja detectada por uma ferramenta ou não, é indicada a refatoraçãodescrita.

�.� C������ ������� �� ��������� - F������M�����

“Pequenas surpresas após cada esquina, mas nada perigoso.”– Willy Wonka, A Fantástica Fábrica de Chocolates

O conceito de hook method não se aplica somente ao padrão TemplateMethod. Existem outros padrões que fazem uso do mesmo princípio parasolucionar problemas diferentes. Eles ainda podem combinar isso com ou-tras técnicas para compor soluções mais elaboradas. Essa seção apresenta opadrão Factory Method, que é utilizado para resolver um problema relacio-nado a criação de objetos.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 70: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Criando objetos na subclasse - Factory Method Casa do Código

Quando estamos desenvolvendo uma aplicação, é comum termos classesde diferentes hierarquias se relacionando. Por exemplo, uma classe que repre-senta uma entidade do sistema pode se relacionar com a classe que faz a suavalidação, ou uma classe que representa um documento pode se relacionarcom a classe que gera sua representação em PDF. Quando isso ocorre, acaba-mos tendo que criar uma classe em função da outra que está sendo utilizada.No exemplo citado, sabemos que todo documento possui seu gerador de PDF,mas como relacionar as duas classes? Para a classe que estará trabalhando coma lógica de geração de arquivos, não interessa de qual classe é a instância queele está recuperando, mas apenas que essa instância seja relacionada com aclasse que está trabalhando.

Relacionando DAOs e classes de serviço

Para ir mais fundo nesse problema da criação de objetos, vamos conside-rar um exemplo bastante comum em todo tipo de aplicação. Quando temosuma arquitetura de camadas de�nida, é comum que objetos de uma camadaprecisem criar os objetos respectivos da camada seguinte. Nesse exemplo,consideraremos duas camadas da aplicação: a camada DAO (nome que vemdo padrão Data Access Object) que realiza o acesso ao banco de dadose a camada de serviço, onde estão implementadas as regras de negócio. Nessecaso, a camada de serviço relacionada com uma entidade deve criar o DAOpara a mesma entidade.

A interface DAO de�ne os métodos gerais de acesso a dados que serãodisponibilizados, como para recuperação, gravação e exclusão de entidades.Essa interface precisará ser implementada para cada entidade do sistema,para a criação dessas operações de cada uma. Note que a interface possui umparâmetro genérico que é utilizado para determinar a entidade associada aoDAO e para que essa entidade seja utilizada nos parâmetros e retornos dosmétodos, variando de acordo com o que for setado na implementação.

Listagem �.� - Interface de de�nição de um DAO:

public interface DAO<E> {public E recuperarPorId(Object id);public void salvar(E entidade);

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 71: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Reúso através de herança

public void excluir(Object id);public List<E> listarTodos();

}

A classe ServicoAbstrato representa uma classe da camada de negó-cios que contém serviços relacionados a uma entidade. Para prover sua funci-onalidade, ela precisa da colaboração doDAO relacionado amesma entidade.Essa classe possui métodos com serviços gerais que são providos a todas asentidades. Um exemplo é o método gravarEntidadeEmArquivo(), querecupera uma entidade a partir de seu identi�cador e chama a instância deGeradorArquivo (aquela classe criada anteriormente nesse capítulo) para cri-ação de um arquivo com as propriedades da classe. Essas propriedades sãoobtidas através do método getMapa() de�nido para o tipo Entidade. Valeressaltar que qualquer subclasse de GeradorArquivo pode ser utilizada, poisela é passada no construtor da classe.

A grande questão é que os métodos gerais dessa classe abstrata precisamacessar o DAO para executar sua funcionalidade, porém qual será a instânciaé algo que só será de�nido na subclasse, quando a entidade com a qual se tra-balha já terá sido de�nida. Dessa forma, a classe de�niu ummétodo abstratochamado getDAO(), que retorna a instância do DAO para ser utilizada.Assim, as subclasses devem implementar esse método de forma a retornara instância correta. Esse é um hookmethod comoobjetivo de criar umobjeto.

Listagem �.� - Serviço abstrato que de�ne um método para recupera-ção do DAO:

public abstract class ServicoAbstrato<E>{public GeradorArquivo gerador;

public ServicoAbstrato(GeradorArquivo gerador){this.gerador = gerador;

}

public abstract DAO<E> getDAO();

//Serviço geralpublic void

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 72: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Criando objetos na subclasse - Factory Method Casa do Código

gravarEntidadeEmArquivo(Object id, String nomeArquivo){E entidade = getDAO().recuperarPorId(id);gerador.gerarArquivo(nomeArquivo,

((Entidade)entidade).getMapa());}

}

A classe ServicoProduto, apresentada na próxima listagem, repre-senta um exemplo de uma subclasse da classe ServicoAbstrato para aentidade Produto. Observe que ela implementa o hook method de�nido ecria a instância do DAO relacionado com a classe Produto. No exemplo, ainstância é criada quando o primeiro acesso é feito ao método. Essa classepode possuir métodos especí�cos de produto, porém os métodos herdadosda superclasse que utilizam o método getDAO() também poderão serutilizados.

Listagem �.� - Serviço concreto que de�ne a implementação FactoryMethod:

public class ServicoProduto extends ServicoAbstrato<Produto>{private DAO<Produto> dao;

public DAO<Produto> getDAO(){if(dao == null){

dao = new ProdutoDAO();}return dao;

}

//Funcionalidade específicapublic double getPrecoProduto(Object produtoId){

Produto p = getDAO().recuperarPorId(id);return p.getPreco();

}}

Vale observar a utilização dos tipos genéricos nesse exemplo. Como aclasse ServicoProduto especi�cou de forma explícita o tipo genérico de

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 73: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Reúso através de herança

sua superclasse, todos os elementos da classe que usavam o parâmetro do tipogenérico serão �xados. Como o método getDAO() precisará agora retornarum DAO<Produto>, haverá umerro de compilação caso umDAOcomoutrotipo genérico for retornado.

Entendendo o Factory Method

O exemplo de criação do DAO nas classes de serviço ilustra bem o ce-nário onde a classe da instância utilizada depende da subclasse. O padrãoFactory Method utiliza um hook method para delegar a criação da instân-cia para a subclasse. Isso permite que métodos mais gerais na superclassepossam utilizar essa instância mesmo sem conhecer a classe concreta. Issopode ser feito invocando o método abstrato de criação que é implementadona subclasse.

A �gura �.� apresenta um diagrama com a estrutura do padrão Factory

Method. Nesse padrão, a classe principal possui uma dependência com umaabstração de uma hierarquia de classes. No diagrama, Dependencia estárepresentada como uma interface, porém não há nada que impede de ser umaclasse. O método metodoFabrica() possui a responsabilidade de criaruma instância de Dependencia, porém na classe principal ele é ummétodoabstrato. Dessa forma, nas subclasses esse método precisa ser implementadoe de deve ser criado um objeto de uma classe concreta que possua a abstraçãoda dependência.

Fig. �.�: Estrutura do padrão Factory Method

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 74: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Considerações �nais do capítulo Casa do Código

A partir desse padrão, é possível desacoplar a superclasse da criação deuma dependência. Com a criação das instâncias na subclasse, apenas elas�cam acopladas as classes concretas da hierarquia. Dessa forma, caso umanova instância da dependência precise ser utilizada pela superclasse, bastacriar uma nova subclasse que retorne aquela instância.

�.� C������������ ������ �� ��������Esse capítulo abordou a utilização da herança para a reutilização de código.O primeiro padrão apresentado foi Null Object, que mostra como o po-limor�smo pode ser utilizado para que o código dos clientes de uma classepossa ser adaptado com suas extensões. Nesse padrão foi visto como a exten-são de uma classe pode ser sua representação nula, fazendo com que o códigocliente consiga lidar com ela de forma transparente.

Em seguida foi mostrado como o uso da herança pode ser feito para per-mitir a especialização do código da superclasse. A partir de hook methodsé possível que as subclasses insiram comportamento em métodos que estãoimplementados na superclasse. Foram apresentados os padrões Template

Method e Factory Method que fazem uso desse princípio.Apesar de esses padrões possuírem o uso da herança como parte principal

de sua solução, o uso dos princípios apresentados não é de sua exclusividade.Em qualquer outro contexto em que os requisitos puderem ser solucionadosa partir da especialização da funcionalidade da superclasse, os mesmo prin-cípios apresentados podem ser utilizados. O próximo capítulo irá explorar ouso da composição e mostrar como ela pode ser utilizada em conjunto com aherança e quando seu uso é mais adequado.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 75: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

C������� �

Delegando comportamento comcomposição

“A primeira regra do gerenciamento é a delegação. Não tente fazer tudosozinho porque você não consegue.”– Anthea Turner

Ao pensar em objetos, uma coisa muito natural é imaginar em como sãocombinados. Na verdade, não existem grandes construções do homem quenão são feitas a partir da combinação de diversos objetos. Uma casa, porexemplo, é feita a partir da combinação de diversos blocos. Olhando outrosexemplos, um carro é composto de peças e um computador é feito de com-ponentes eletrônicos. O que é mais interessante é que à medida que vamosdescendo, muitas vezes o padrão vai se repetindo: uma peça pode ser com-posta por outras. Uma placa de computador é feita com capacitores, resis-

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 76: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Tentando combinar opções do gerador de arquivos Casa do Código

tências e chips. É a quantidade in�nita de combinações desses elementos quetorna possível a construção de diversas coisas diferentes a partir do mesmomaterial.

Quando a programação orientada a objetos foi criada, um dos objetivosera simpli�car a abstração dos conceitos domundo real para o so�ware. Dessaforma, um elemento existente no domínio da aplicação poderia ser represen-tado utilizando um objeto no so�ware. Damesma forma que as coisas intera-gem no mundo real para a realização de um objetivo, dentro de um so�warenão poderia ser diferente. Do mesmo jeito que diversas peças são combina-das em vários níveis para compor um componente físico, os componentes deso�ware também podem ser resultado da combinação de outros componen-tes mais granulares.

Apesar de não estar entre os quatro elementos principais de uma lin-guagem orientada a objetos, a composição é um conceito que vem implícitoquando se fala em objetos. O capítulo � já apresentou o uso da composiçãoatravés do padrão Strategy, e este capítulo irá explorar o conceito mais afundo e mostrar como ele pode ser utilizado para a extensão de comporta-mento. Será mostrado como a composição combinada com o uso de abstra-ções pode ser e�caz na solução de diferentes problemas. A primeira seçãocomeça revisitando o exemplo do gerador de arquivos do capítulo anteriorpara ilustrar cenários onde as limitações da herança são evidentes.

�.� T������� �������� ������ �� ������� ����������

No exemplo do capítulo anterior, a classe GeradorArquivo implemen-tava o padrão Template Method para permitir que as subclasses pudes-sem especializar parte do comportamento, como o formato da geração doconteúdo e um pós-processamento a ser feito nos bytes do arquivo. O mé-todo principal gerarArquivo() invocava os hookmethods processar()e gerarConteudo(), que eram implementados somente na subclasse.As duas subclasses de GeradorArquivo, GeradorXMLCompactado eGeradorPropriedadesCriptografado, respectivamente implementamos métodos para gerar em XML compactados e de propriedade criptografa-

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 77: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Delegando comportamento com composição

dos.O problema começa ao tentarmos ter uma implementação que combina

o comportamento dessas subclasses, como para a geração de arquivos XML ecriptografados. Se tentarmos utilizar a herança, acabamos com a duplicaçãode alguma parte do código. A �gura �.� mostra o que acontece quando cri-amos uma nova classe que de�ne o gerador de XML como uma superclassecom duas subclasses que especializam apenas a parte do pós-processamento.Apesar de ter sido criada uma abstração referente ao formato do arquivo, aausência de uma abstração para o pós-processamento faz com que haja du-plicação de código.

Fig. �.�: Duplicação de código ao se tentar criar um novo nível na hierarquia

A �gura �.�mostra que se tentarmos estruturar a hierarquia de uma outraforma para eliminar a duplicação anterior, continuaremos comduplicação emuma outra parte do código.

A limitação do uso da herança, que �ca evidente nesse problema, é o fatode ela poder ser utilizada apenas uma vez. Se fosse possível ter herança múl-tipla em Java, uma classe poderia obter a implementação do formato do ar-

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 78: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Tentando combinar opções do gerador de arquivos Casa do Código

quivo de uma superclasse e a do pós-processamento de outra. Como isso nãoé possível, quando precisamos de uma implementação de outro ramo da hie-rarquia, acaba havendo uma duplicação de código.

Fig. �.�: Manutenção da duplicação ao se tentar criar a hierarquia pelo outrolado

Quandomais de um hookmethod são de�nidos em uma superclasse e im-plementados por uma subclasse, essas implementações �cam ligadas uma aoutra devido ao fato de terem sido implementadas na mesma classe. Por maisque seja possível ir implementando cada hook method em um nível diferenteda hierarquia, a duplicação ainda pode acontecer, principalmente se esses fa-tores que podem variar - chamados de variabilidade - são independentes umdo outro. O problema vai se tornando ainda mais crítico quando se aumentao número de variabilidades e o número de possíveis implementações de cadauma delas. A quantidade de classes necessárias para implementar todas aspossibilidades tende a aumentar de forma exponencial!

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 79: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Delegando comportamento com composição

�.� B����� - ��� ����� ����� ���� �����������-���

“A ponte não é de concreto, não é de ferro / Não é de cimento / A ponte é atéonde vai o meu pensamento / A ponte não é para ir nem pra voltar / A ponte ésomente pra atravessar / Caminhar sobre as águas desse momento”– Lenini

Como todos já devemdescon�ar pelo foco do capítulo, a composição seráutilizada como forma de resolver o problema descrito. Como com a pura uti-lização de herança, a implementação de uma variabilidade �ca ligada a outra,a ideia da nova solução seria separar a implementação de uma delas em umaoutra classe. Da mesma formamostrada no padrão Strategy no capítulo �,essa classe irá compor a classe principal, no caso GeradorArquivo. Dessaforma, o método implementado nessa hierarquia de classes à parte será invo-cado como parte da implementação do algoritmo. A �gura �.� mostra como�caria a estrutura de classes com a utilização da composição para os algorit-mos de pós-processamento dos arquivos.

Fig. �.�: Utilização de composição no GeradorArquivo

A listagem a seguir apresenta o código do GeradorArquivo apósa aplicação dessa solução. O método processar() não está mais namesma classe e sim na instância de PosProcessador que compõeGeradorArquivo. Dessa forma, qualquer implementação dessa interfacepode ser utilizada para realizar o pós-processamento do arquivo. A grande

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 80: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Bridge - uma ponte entre duas variabilidades Casa do Código

motivação dessa solução no cenário apresentado é que o processador podeser con�gurado independente da subclasse que está sendo utilizada, permi-tindo que as duas variem de forma independente.

Listagem �.� - Classe GeradorArquivo utilizando composição:

public abstract class GeradorArquivo {private PosProcessador processador;

public void setProcessador(PosProcessador processador){this.processador = processador;

}

public final void gerarArquivo(String nome,Map<String, Object> propriedades)

throws IOException {String conteudo = gerarConteudo(propriedades);byte[] bytes = conteudo.getBytes();bytes = processador.processar(bytes);FileOutputStream fileout = new FileOutputStream(nome);fileout.write(bytes);fileout.close();

}

protected abstract StringgerarConteudo(Map<String, Object> propriedades);

}

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 81: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Delegando comportamento com composição

M�� � � ���� �� ��� ��� �� ���-��������������

Na implementação original apresentada no capítulo anterior, o mé-todo processar() possuía uma implementação default para o caso denão haver nenhum pós-processamento. Esse caso deve ser tratado aquinessa solução também! O que deve ser feito? Deve-se colocar um condi-cional para veri�car se o processador foi con�gurado corretamente? Essaé uma solução possível, porém podemos utilizar o Null Object parasolucionar esse problema.

Para fazer isso, uma implementação de PosProcessador cha-mada NullProcessador poderia ser criada com uma que retor-nasse o próprio array de bytes recebido como parâmetro sem nenhumpós-processamento. Sendo assim, para garantir que não ocorra umNullPointerException caso o conteúdo da variável processadorseja nulo, uma instância dessa classe poderia ser atribuída a ela em algummomento da inicialização do objeto.

Esse exemplo mostra como é importante combinar as soluções dospadrões para chegar a uma estrutura �nal. Enxergue um padrão comoum elemento que pode ser incluído para compor a sua solução e não asolução completa!

Entendendo o padrão Bridge

Essa solução caracteriza o padrão Bridge, que cria uma ponte entreduas hierarquias ligadas por uma relação de composição permitindo queambas variem de forma independente. Nesse caso, a ponte é caracterizadapela relação de composição entre a classe GeradorArquivo e a interfacePosProcessador. Um cenário em que esse tipo de solução é comum équando temos uma hierarquia de abstrações e outra com implementações,permitindo que cada uma possa variar independentemente. Neste caso, oGeradorArquivo e suas subclasses são uma abstração que caracteriza algo-ritmos de geração de arquivos em determinados formatos, porém ainda semde�nir a implementação do pós-processamento.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 82: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Bridge - uma ponte entre duas variabilidades Casa do Código

A �gura �.� apresenta uma estrutura mais geral para o padrão Bridge.A classe Abstracao representa um conceito que precisa ser representadoem um sistema e possui duas variabilidades independentes, ou seja, com-portamentos não dependentes que podem ser estendidos e/ou modi�ca-dos. A classe representada como AbstracaoRefinada especializa o com-portamento da abstração através da implementação de seus hook methods.Já a abstração representada pela interface Componente compõe a classeAbstracao e representa uma de suas variabilidades. Dessa forma, quandoessa operação precisar ser invocada em Abstracao, o método na classe quea compõe será chamado.

Fig. �.�: Estrutura geral do padrão Bridge

No exemplo apresentado, a classe Abstracao é representada pela classeGeradorArquivo, que representa uma abstração de como gerar arquivosa partir de mapas de propriedades. Seus re�namentos são classes que a es-pecializam para que a geração dos arquivos seja feita em um formato espe-cí�co, como XML e arquivos de propriedades. A interface Componente érepresentada pela interface PosProcessador e �ca claro a partir de suasimplementações que ela representa uma operação de pós-processamento doarquivo.

Os efeitos do Bridge

A principal motivação na implementação do padrão Bridge é tornar

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 83: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Delegando comportamento com composição

independentes os fatores que podem variar na solução. Qualquer subclassede Abstracao pode ser facilmente composta com qualquer implementaçãoda interface Componente, permitindo a combinação de ambas livrementesem duplicação de código. Caso, por exemplo, uma nova implementação dequalquer uma das duas hierarquias for criada, ela será facilmente combinadacom o que já existe.

Um efeito colateral muito interessante é que as classes que foram separa-das, também podem ser utilizadas em outro contexto. No exemplo, os algo-ritmos de pós-processamento poderiam ser utilizados na geração de outrostipos de arquivos ou mesmo para o envio de informações pela rede. Esse tipode questão nos faz re�etir se a princípio, pensando apenas nas responsabi-lidades, essa funcionalidade já não deveria ter sido atribuída a outra classe.Imagine acessar uma classe chamada GeradorArquivo para criptografardados para serem enviados em uma rede. Dessa forma, uma consequênciadesse padrão também é o desacoplamento de responsabilidades, permitindomais facilmente o seu reúso em outros contextos.

Um ponto que acho interessante no Bridge é o fato de sua solução utili-zar ao mesmo tempo herança e composição. Escuto muitas pessoas dizerempara utilizar sempre a composição no lugar da herança, porém acho que apalavra “sempre

é muito complicada no contexto de design de so�ware, pois não existebala de prata nem uma solução mágica que irá resolver todos os seus proble-mas. A composição tem sim suas vantagens que serão apresentadas no pre-sente capítulo, porém é importante sempre avaliar com cuidado o problemae os requisitos da solução para que se busque a mais apropriada.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 84: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Hook Classes Casa do Código

O ������ ��� � � ���������

Um erro comum ao se aprender os primeiros padrões é achar quea implementação deve ser sempre exatamente como é mostrada no di-agrama geral do padrão. O diagrama é apenas uma referência, pois amesma solução pode ser implementada de diversas outras formas. Porexemplo, dependendo do contexto, Implementacao poderia ser im-plementada como uma classe abstrata ou a classe Abstracao poderiapossuir mais de um hook method a ser implementado por suas subclas-ses. É por esse motivo, que ferramentas que geram código ou proveemuma estrutura de classes para a implementação de padrões são bastantecriticadas por especialistas da área. É mais importante se preocupar emter uma solução adequada ao seu problema, do que seguir cegamente aestrutura do padrão.

�.� H��� C������No capítulo anterior foi introduzido o conceito de Hook Methods, que sãométodos que podem ser sobrescritos pelas subclasses como forma de estendere especializar o comportamento da classe. Nesse capítulo estamos vendo umaoutra forma de alterar o comportamento das classes a partir da composição.Em vez de os pontos em que o comportamento pode variar serem de�nidosna mesma classe, eles são de�nidos em uma outra classe que compõe a classeprincipal. A essa classe que pode ser alterada damos o nome de Hook Class.A �gura �.� ilustra o conceito apresentado.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 85: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Delegando comportamento com composição

Fig. �.�: Representação do conceito de Hook Classes

Da mesma forma que os hook methods, as hook classes são uma técnicautilizada pelos padrões para chegar a uma solução para um problema maisespecí�co. O padrão Bridge, por exemplo, utiliza ao mesmo tempo umhookmethod e uma hook class como forma de separar dois pontos de extensãocujo comportamento pode variar de modo independente. Dessa maneira, éimportante não apenas conhecer os padrões, mas compreender os princípiospor trás de sua estrutura para entender melhor as consequências trazidas poruma decisão de design.

Diferenças entre os tipos de hooks

Quando entendemos a ideia por trás dos hook methods e das hook classes,eles até que são bem parecidos. Em ambos os casos, a classe principal chamaummétodo cuja implementação pode variar. No caso dos hook methods essemétodo está na mesma classe, podendo a implementação variar com a sub-classe, e no caso das hook classes o método está em um objeto que compõe aclasse, fazendo com que a implementação varie com a instância. Apesar de al-guma semelhança, a utilização de uma técnica ou outra possui consequências

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 86: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Hook Classes Casa do Código

bem diferentes.A primeira delas está nomomento emque a implementação que será utili-

zada é escolhida. Quandoumhookmethod é utilizado, a escolha é feita nomo-mento em que o objeto é instanciado. Isso ocorre pois ao criarmos um novoobjeto devemos escolher qual a classe concreta que será utilizada, e fazendoisso estamos escolhendo a sua implementação dos hook methods. Já no casoda utilização de hook classes, a implementação pode ser trocada a qualquermomento, bastando para isso trocar a instância que foi con�gurada. Observeque isso não é obrigatório, como no exemplo da classe GeradorArquivo

apresentada nesse capítulo, que permitia a con�guração da classe que a com-punha apenas no construtor.

Nesse ponto, vale a pena retomar o exemplo do primeiro capítulo, no qualera necessário poder trocar a lógica que calculava o valor do estacionamentode acordo com o tempo e com o tipo de veículo. No cenário apresentado eraextremamente importante poder trocar a implementação do cálculo após acriação do objeto ContaEstacionamento. Esse foi um dos motivos que�zeram a escolha do padrão Strategy, que utiliza uma hook class, ser ade-quado para a resolução do problema.

Outra questão que deve ser levada em consideração é o grau de depen-dência de diferentes pontos de extensão. Caso os hook methods sejam de�ni-dos na mesma classe, sendo eles na própria classe ou em uma hook class, asimplementações �cam ligadas. Foi esse o problema apresentado no exemplodo GeradorArquivo, no qual pontos de extensão eram independentes mas�cavam de�nidos na mesma classe. Quando usamos apenas hook methods,pelo fato de não haver herança múltipla em Java, eles sempre �cam inter-ligados. Ao utilizarmos hook classes, se colocarmos os métodos na mesmaclasse, o mesmo problema irá acontecer, porém se pusermos em classes dife-rentes, cada implementação poderá ser con�gurada de forma independente.O padrão Bridge aborda justamente essa questão, utilizando diferentes es-tratégias de extensão para que as implementações possam variar de formaindependente.

Evoluindo o modelo de classes

Em um artigo clássico sobre padrões para evolução de frameworks [�], é

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 87: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Delegando comportamento com composição

mostrado que normalmente os pontos de extensão são primeiramente de�-nidos utilizando herança, o que é conhecido como whitebox framework, parasó em seguida evoluir para o uso de composição, chamado de blackbox fra-mework. Por mais que a composição pareça ser mais vantajosa que a herançapor suas características estruturais, é muito mais fácil deixar sua classe abertaà extensão através de herança.

O termo whitebox framework se refere a frameworks cujas classes permi-tem sua extensão a partir da herança. Devido ao fato das subclasses teremacesso a detalhes internos da classe que está sendo estendida, sua estrutura�ca exposta. O termo whitebox, ou caixa branca, é uma metáfora a essa vi-sibilidade que o desenvolvedor precisa ter de como as coisas são na classeinternamente. Com a utilização desse paradigma, qualquer método públicoou protegido que não seja �nal, é um potencial hook method a ser especia-lizado pelas subclasses. Dessa forma, quando não se sabe exatamente o queserá necessário ser estendido, a herança é uma boa alternativa por possibilitarque vários pontos �quem em aberto. Uma desvantagem dessa abordagem éque quem desenvolver a classe que faz a extensão deve conhecer a estruturainterna da superclasse, tornando a tarefa mais complexa.

Por outro lado, o termo blackbox framework se refere a frameworks cujomecanismo de extensão se baseia em composição. Nesse caso, o desenvol-vedor deve criar implementações que obedecem a uma interface fornecida eutilizá-las para compor a classe principal. O termo blackbox, ou caixa preta,é utilizado porque o desenvolvedor não precisa conhecer a parte interna dasclasses do framework para estendê-las. Apesar de possuir as vantagens dacomposição, esse tipo de ponto de extensão é mais difícil de ser identi�cado,pois é preciso saber a porção exata da funcionalidade que será delegada. Emcompensação, depois que esses pontos forem identi�cados, é mais fácil paraoutros desenvolvedores alterarem o comportamento, pois eles só precisamentender as interfaces que estão implementando.

Na verdade, nada precisa ser completamente preto ou branco. O caminhonatural é, ao criar a primeira classe, deixar alguns possíveis hook methods emaberto para que seja possível realizar a extensão do comportamento. À me-dida que as extensões forem sendo criadas e os principais pontos onde o com-portamento é especializado com frequência forem identi�cados, a estrutura

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 88: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. State - variando o comportamento com o estado da classe Casa do Código

pode ser refatorada para o uso de hook classes.

�.� S���� - �������� � ������������� ���� ��-���� �� ������

“Eu costumava partir por semanas em um estado de confusão.”– Albert Einstein

Um cenário bastante comum ao desenvolvermos um so�ware é uma de-terminada entidade mudar de estado durante o decorrer de sua execução, al-terando seu comportamento de acordo com esse estado. Uma conta correnteem um banco, por exemplo, comporta-se diferente caso o saldo esteja ne-gativo. Em um jogo, as mesmas ações podem gerar diferentes efeitos em umpersonagem quando ele está em estados diferentes, como quando pega algumitem ou está dentro d’água.

A implementação comum nesses casos é criar uma série de condicionaisem que, em diversos pontos da classe, veri�ca-se o estado do objeto e executa-se a lógica mais adequada. Seguindo essa ideia, alguns estados são su�cientespara deixar o código bastante confuso. É comum que a mesma sequência decondicionais seja encontrada em diversos pontos do código onde o estado doobjeto possui in�uência. Alémdisso, essa estrutura torna complicada a adiçãode mais estados, pois isso demandará alterações no código da própria classee irá aumentar ainda mais a quantidade de condicionais. Como resolver isso?Conheça o padrão State!

A estrutura do State

Essa seção irá apresentar o padrão State, o qual utiliza composição parapermitir essa variação de comportamento de acordo como o estado de umaentidade do sistema. Nesse padrão, os estados são representados a partir declasses que obedecem a uma abstração comum, podendo ser uma interface ouuma superclasse. Dessa forma, a entidade é composta por um objeto do tipodessa abstração e, nos métodos de negócio, delega para ele o comportamentode toda parte que depende do seu estado. A �gura �.� representa a estruturadesse padrão.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 89: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Delegando comportamento com composição

Fig. �.�: Estrutura do padrão State

Sendo assim, apenas a lógica comum será mantida na entidade e o com-portamento do objeto especí�co de cada estado estará de�nido em cada sub-classe. Quando o estado for alterado, basta trocar a instância utilizada paracaracterizar o estado, que consequentemente o comportamento da entidadeserá alterado. Nesse caso, o estado é uma hook class para a qual está sendodelegado. Uma consequência desse padrão é a simplicidade em alterar esta-dos existentes, ou mesmo inserir novos estados no ciclo de vida da entidade.A desvantagem é que, devido à divisão da lógica dos estados, �ca mais difícilter uma visão global dos estados e transições que podem ser realizadas.

Busca em profundidade

Calma! Você não está em um livro de algoritmos! Acho muito interes-sante quando é possível resolver um problema clássico na área de projeto dealgoritmos e estrutura de dados utilizando algumpadrão. Isso nosmostra queeles são úteis para qualquer tipo de so�ware. O algoritmo abordado nessa se-ção é a busca em profundidade em grafos. Nesse algoritmo, à medida que osnós do grafo vão sendo percorridos, eles mudam de cor e o comportamentodo algoritmo depende da cor do nó que está sendo percorrido. Dessa forma, oState será utilizado para con�gurar o comportamento de cada nó de acordo

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 90: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. State - variando o comportamento com o estado da classe Casa do Código

com seu estado.A busca emprofundidade é um algoritmo de busca de grafos, que se inicia

com um nó raiz e vai se explorando cada um de seus ramos, sempre na maiorprofundidade possível, antes de retroceder e processar os outros ramos. Paraquem não se lembra do algoritmo de busca em largura em grafos, segue umabreve explicação de como ele funciona. A execução inicia-se a partir de umnóe todos se iniciam com a cor branca. Quando umnó começa a ser processado,ele adquire a cor cinza que signi�ca que ele está sendoprocessado. Ao adquirira cor cinza, são processados todos os seus nós adjacentes que tenham a corbranca. Ao �nalizar a execução o nó passa da cor cinza para cor preta, queindica que seu processamento foi concluído. O algoritmo termina quando onó raiz onde o algoritmo iniciou adquire a cor preta.

A �gura �.� apresenta um exemplo de execução do algoritmo passo apasso em um grafo. Nesse exemplo, conforme os nós vão mudando de es-tado, eles adquirem uma cor diferente e as arestas que já tiverem sido percor-ridas são colocadas em negrito. A partir do exemplo é possível entender deuma forma mais concreta como os nós são percorridos e a situação em quemudam de estado.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 91: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Delegando comportamento com composição

Fig. �.�: Busca em profundidade passo a passo

Implementação da busca em profundidade utilizando State

O primeiro passo da implementação é de�nir um nó e quais os pontosda execução que diferem com o estado. No contexto do algoritmo, asinformações relevantes de um nó são o seu nome, seus nós adjacentes esua cor. Essa classe possui um método chamado buscaProfundidade() queirá efetivamente executar o algoritmo. Os pontos identi�cados que podemvariar com o estado são: (a) a própria execução da busca em profundidade,pois no caso do nó ser preto ou cinza ela não deve ser executada; e (b)quando o nó assume uma cor. A implementação realizada para classe No

está representada na listagem a seguir.

Listagem �.� - De�nição da classe que representa um nó:

public class No {private Set<No> adjacentes = new HashSet<>();private Cor cor;

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 92: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. State - variando o comportamento com o estado da classe Casa do Código

private String name;

public No(String name){this.name = name;cor = new Branco();

}public void buscaProfundidade(List<No> list){

cor.busca(this, list);}public Set<No> getAdjacentes() {

return adjacentes;}public void addAdjacentes(No adj) {

adjacentes.add(adj);}public void setCor(Cor cor, List<No> list) {

this.cor = cor;cor.assumiu(this ,list);

}public String toString() {

return name;}

}

O atributo cor representa o estado do nó na execução do algoritmo. Ob-serve que métodos são invocados no objeto desse atributo quando o métodobuscaProfundidade() é invocado e quando uma nova cor é con�gurada apartir do método setCor(). O buscaProfundidade() recebe uma listaque deve ser populada com os nós do grafo na ordem que terminaram de serexecutados pelo algoritmo.

A listagem a seguir apresenta a classe abstrata Cor e suas três subclasses.Observe que cada cor implementa os hook methods da de�nição do estado,de acordo com a descrição do algoritmo. Quando o nó no estado Branco

recebe a chamada da busca, o mesmo deve passar para a cor Cinza. Essa,por sua vez, ao ser assumida pelo nó inicia o processamento de todos os nósadjacentes, assumindo a cor Preto ao seu �nal. Finalmente, ao se tornarPreto, o nó deve ser adicionado na lista recebida como parâmetro pelo

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 93: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Delegando comportamento com composição

algoritmo.

Listagem �.� - Implementação dos estados de um nó:

//Classe que abstrai o estado de um nópublic abstract class Cor {

void busca(No no, List<No> list){}void assumiu(No no, List<No> list){}

}

//Implementações dos três estados possíveispublic class Branco extends Cor {

public void busca(No no, List<No> list) {no.setCor(new Cinza(), list);

}}public class Cinza extends Cor {

void assumiu(No no, List<No> list) {for(No adj : no.getAdjacentes())

adj.buscaProfundidade(list);no.setCor(new Preto(), list);

}}public class Preto extends Cor {

void assumiu(No no, List<No> list) {list.add(no);

}}

A listagem a seguir mostra a criação do grafo apresentado na �gura �.�e a execução do algoritmo. Ao seu �nal, os nós são impressos na ordem emque seu processamento foi �nalizado.

Listagem �.� - Execução do algoritmo para o grafo do exemplo:

public static void main(String[] args) {No a = new No("A"); No b = new No("B");No c = new No("C"); No d = new No("D");No e = new No("E"); No f = new No("F");

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 94: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. State - variando o comportamento com o estado da classe Casa do Código

No g = new No("G"); No h = new No("H");

a.addAdjacentes(b); b.addAdjacentes(c);c.addAdjacentes(d); d.addAdjacentes(b);a.addAdjacentes(e); e.addAdjacentes(f);f.addAdjacentes(c); f.addAdjacentes(g);f.addAdjacentes(h); a.addAdjacentes(h);

List<No> l = new ArrayList<>();a.buscaProfundidade(l);for(No n : l)

System.out.println(n);}

Nesse algoritmo, diversas ações dependem do estado do nó, como se eledeve ser adicionado na lista ou se os nós adjacentes devem ser processados.Apesar disso, observe que nenhum comando condicional foi utilizado na im-plementação!

Utilizando um Enum para implementar o State

Quando temos que representar estados de um objeto em um so�ware,frequentemente utilizamos enumerações. O problema é que muitas vezes asenumerações possuem apenas a de�nição dos estados, �cando a lógica espe-cí�ca de cada estado presa dentro dos condicionais na classe. Construçõesdo tipo enum podem de�nir métodos, inclusive abstratos, que podem sersobrescritos na de�nição de cada estado. Segue na listagem a seguir como ascores dos nós do grafo poderiam ser de�nidas utilizando uma enumeração.Em relação ao resto do código, somente a inicialização do atributo cor daclasse No precisaria ser modi�cada.

Listagem �.� - Execução do algoritmo para o grafo do exemplo:

public enum Cor {

BRANCO{public void busca(No no, List<No> list) {

no.setCor(CINZA, list);

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 95: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Delegando comportamento com composição

}},CINZA{

void assumiu(No no, List<No> list) {for(No adj : no.getAdjacentes())

adj.buscaProfundidade(list);no.setCor(PRETO, list);

}},PRETO{

void assumiu(No no, List<No> list) {list.add(no);

}};

void busca(No no, List<No> list){}void assumiu(No no, List<No> list){}

}

Porém, existem algumas situações em que a utilização de enumerações édesaconselhada na representação de um estado. A primeira é quando é de-sejável que o estado seja um ponto de extensão e que novos estados possamser de�nidos. Nesse caso, como os possíveis estados são de�nidos dentro deuma enumeração �xa, não se pode adicionar um novo sem a modi�cação docódigo do próprio enum. Outra situação é quando algum estado precisa ar-mazenar uma informação especí�ca do objeto que está sendo composto porele. Nesse caso, como cada instância do enum é compartilhada por todos quea possuem, a informação não poderia ser diferente para cada uma.

�.� S����������� ������������ ��� �������-�����

No exemplo apresentado, os nós já possuíam um estado explícito segundo adescrição do algoritmo, porém um cenário para utilização do padrão State

muitas vezes é difícil de ser identi�cado inicialmente. O conceito do que sig-ni�ca um estado para uma determinada entidade do so�ware pode começara �car explícito somente no momento da codi�cação. A repetição de condi-cionais similares em diversos pontos da mesma classe pode ser um sinal de

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 96: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Substituindo condicionais por polimor�smo Casa do Código

que seria adequada a refatoração do código em direção ao padrão State.O mesmo vale para outros padrões que utilizam composição. Um exem-

plo é quando uma classe possuir um método grande que utiliza condicionaispara selecionar dentre alternativas de implementação para um mesmo algo-ritmo. Nesse caso, a refatoração poderia ser na direção do padrão Strategy.O exemplo apresentado na introdução do livro ilustra bem uma refatoraçãoem direção à composição.

Fig. �.�: Refatorando em direção à composição

A �gura �.� apresenta o processo que pode ser utilizado para extrair a

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 97: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Delegando comportamento com composição

classe que será utilizada para a composição e para a criação das implementa-ções de cada alternativa. O primeiro passo consiste na identi�cação dos locaiscom a lógica condicional comodescrito acima e na sua extração paramétodosque a princípio �carão na própria classe. Em seguida, uma nova classe deveser criada e um atributo do tipo dela deve ser introduzido na classe principalque se deseja refatorar. Note que a princípio ela estará vazia e nenhum usodela é feito a partir da principal.

Com a nova classe criada, o próximo passo é mover os métodos extraí-dos na primeira etapa da refatoração para ela. Cada método deve ser passadopara a nova classe comométodo público e os locais onde era invocado devemagora chamá-lo a partir do atributo criado. É aconselhável que cada métodoseja migrado separadamente, executando testes em cada passo. Uma questãoque deve ser considerada nesse ponto é relacionada aos dados que são utiliza-dos pela porção da lógica que está indo para outra classe. Quando a informa-ção for um parâmetro relacionado com o algoritmo em si, normalmente ele éincluído como um atributo da classe que está sendo extraída. Quando é umainformação da classe principal, normalmente ele é passado como parâmetro,ou a própria instância da classe é passada como parâmetro.

Depois que a lógica condicional for migrada para outra classe, é possívelcomeçar a extrair subclasses com cada uma das implementações. O conteúdode um condicional na superclasse deve sermovido para uma subclasse. Adici-onalmente, é preciso atribuir uma instância dessa classe na principal quandoela precisar ser invocada. Essa troca de instância é realizada normalmente nomomento em que a variável utilizada nos condicionais é alterada. Por exem-plo, se a refatoração for para o padrão State, então a mudança da instânciadeve ocorrer no momento da mudança de estado. Com isso, a lógica queantes era selecionada com condicional agora será invocada através de poli-mor�smo. As duas listagens a seguir apresentam respectivamente o antes e odepois da criação de uma nova subclasse.

Listagem �.� - Código do componente antes da refatoração:

public class Componente {public void metodoExtraido() {

if (possibilidadeA) {//lógica referente ao cenário A

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 98: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Substituindo condicionais por polimor�smo Casa do Código

} else if(possibilidadeB) {//lógica referente ao cenário B

} else if(possibilidadeC) {//lógica referente ao cenário C

}}

}

Listagem �.� - Código do componente e sua subclasse depois da refatoração:

public class Componente {public void metodoExtraido() {

if (possibilidadeB) {//lógica referente ao cenário B

} else if(possibilidadeC) {//lógica referente ao cenário C

}}

}

public class ComponenteA extends Componente {@Overridepublic void metodoExtraido() {

//lógica referente ao cenário A}

}

Depois que uma subclasse for extraída, a refatoração deve ser concluídacom a extração das outras subclasses. É importante não tentar criar todasas subclasses de uma só vez. A cada subclasse extraída, deve-se executar ostestes para veri�car ser ela foi realizada corretamente. Ao �nalizar a extração,a superclasse pode �car com algum comportamento default, caso exista, ou osmétodos podem ser transformados em abstratos caso toda lógica tenha sidomovida.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 99: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Delegando comportamento com composição

�.� C������� ��� ��������� ������� - O����-���

“A visão de um observador imparcial é uma janela para o mundo.”– Kenneth L. Pike

Nos exemplos vistos até o momento, a composição ocorre apenas comuma instância. Nessa seção será apresentado um padrão que utiliza a compo-sição com múltiplos objetos: o Observer. Esse é um padrão muito impor-tante e que frequentemente é necessário em aplicações. É aplicável quandoexiste um objeto cujos eventos precisam ser observados por outros objetos.Um exemplo seria um componente grá�co cujos eventos precisam ser trata-dos pela aplicação. Um outro exemplo, talvez menos óbvio, é um objeto dedados cujas mudanças demandam mudanças em outras partes do sistema.

A próxima seção apresenta um exemplo do padrão Observer, em quemudanças em um objeto são noti�cadas a outros objetos interessados. Emseguida esse padrão será analisado de forma mais profunda, avaliando suaestrutura básica e suas consequências. Por �m, serãomostradas algumasAPIsexistentes que fazem uso dele.

Noti�cando mudanças em uma carteira de ações

O exemplo que será abordado envolve um objeto que representa uma car-teira de ações. Essa carteira possui um mapa com os códigos das ações e suarespectiva quantidade. De acordo com as ações do usuário ou com gatilhoscon�gurados na aplicação, ações podem ser compradas ou vendidas alterandosua quantidade. A questão é que existem diversas classes interessadas em sa-ber quando uma informação é alterada nessa classe para poder executar asua lógica. Por exemplo, um componente que exibe um grá�co com a quan-tidade de cada ação da carteira precisa saber quando houve uma mudançapara ser atualizado. Outro exemplo seria um componente que �zesse um logdas alterações realizadas. Poderia também existir um componente para fazera auditoria dos dados.

Caso as noti�cações sejam feitas individualmente para cada necessidade,a classe que representa a carteira de ações �caria acoplada às classes que pre-

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 100: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Compondo com múltiplos objetos - Observer Casa do Código

cisaria noti�car. Isso não é desejável. Além de di�cultar mudanças nas clas-ses envolvidas, tornaria complicada a adição de novas classes interessadas emmudanças nessas informações.

Para resolver esse problema, o padrão Observer será implementado nasolução. Será criada uma interface Observador, apresentada na listagema seguir, a qual deve ser implementada pelas classes que desejam receber oseventos de mudança na quantidade das ações. Implementações dessa inter-face poderão ser registradas na classe CarteiraAcoes, cuja listagem tam-bém está apresentada, através dométodo addObservador(). Dessa forma,todos os observadores registrados receberão uma noti�cação quando umamudança for realizada. Observe que o método notificar() chama o mé-todo mudancaQuantidade() da interface Observador em todas as ins-tâncias registradas.

Listagem �.� - Interface para receber noti�cações da carteira da ações:

public interface Observador {void mudancaQuantidade(String acao, Integer qtd);

}

Listagem �.� - Classe cujos dados podem ser observados por outras classes:

public class CarteiraAcoes {private Map<String,Integer> acoes = new HashMap<>();private List<Observador> obs = new ArrayList<>();

public void adicionaAcoes(String acao, Integer qtd) {if(acoes.containsKey(acao)){

qtd += acoes.get(acao);}acoes.put(acao, qtd);notificar(acao, qtd);

}

private void notificar(String acao, Integer qtd) {for(Observador o: obs)

o.mudancaQuantidade(acao, qtd);}

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 101: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Delegando comportamento com composição

public void addObservador(Observador o) {obs.add(o);

}}

A seguir, são apresentadas duas listagens de classes que implementama interface Observador: AcoesLogger e GraficoBarras. A classeAcoesLogger possui uma implementação bem simples e apenas registra anova quantidade recebida no console. Já a classe GraficoBarras utiliza ocomponente de geração de grá�cos JFreeChart para a criação de uma janelacom um grá�co de barras que é atualizado toda vez que uma mudança énoti�cada.

Listagem �.�� - Classe que observa mudanças nas quantidades e regis-tra no console:

public class AcoesLogger implements Observador {public void mudancaQuantidade(String acao, Integer qtd) {

System.out.println("Alterada a quantidade da ação "+ acao + " para " + qtd);

}}

Listagem �.�� - Classe que coloca as quantidades em um grá�co de barras:

public class GraficoBarras implements Observador {

private DefaultCategoryDataset dataset;private JFrame frame = new JFrame();

public GraficoBarras() {dataset = new DefaultCategoryDataset();JFreeChart chart = ChartFactory.createBarChart(

"Carteira de Ações","Siglas","Quantidade", dataset, PlotOrientation.VERTICAL,

false, true, false);ChartPanel chartPanel = new ChartPanel(chart);frame.setContentPane(chartPanel);frame.setSize(500, 270);

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 102: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Compondo com múltiplos objetos - Observer Casa do Código

frame.setVisible(true);frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

}

public void mudancaQuantidade(String acao, Integer qtd) {dataset.setValue(qtd, "Quantidade", acao);

}}

U��������� � JF���C����

Se você deseja executar o exemplo apresentado, precisa baixar o JFree-Chart no endereço http://www.jfree.org/jfreechart/ e adicionar os arqui-vos jfreechart-1.x.x.jar e jcommon-1.x.x.jar no classpath.Os grá�cos desse framework sempre são divididos emduas partes: o con-junto de dados e a sua representação visual. No exemplo apresentado, oatributo dataset da classe DefaultCategoryDataset representao conjunto de dados e a variável local chart representa a parte visual.No caso, com a alteração do conjunto de dados associado ao grá�co, arepresentação também é atualizada.

Para �nalizar o exemplo, a listagem a seguir apresenta um métodomain() que associa os observadores na carteira de ações e adicionaações a carteira. No início do código, as instâncias de GraficoBarras eAcoesLogger são criadas e associadas ao objeto da classe CarteiraAcoes.Em seguida, o método Thread.sleep() é utilizado para gerar um inter-valo entre cada quantidade inserida na carteira de ações, para ser possível verde forma progressiva as alterações no grá�co e as modi�cações registradasno console. A �gura �.� apresenta o grá�co gerado no �nal da execução.

Listagem �.�� - Exemplo de execução da carteira de ações com observa-dores:

public static void main(String[] args) throws Exception {GraficoBarras gb = new GraficoBarras();

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 103: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Delegando comportamento com composição

AcoesLogger al = new AcoesLogger();CarteiraAcoes c = new CarteiraAcoes();c.addObservador(gb);c.addObservador(al);

Thread.sleep(2000);c.adicionaAcoes("COMP02", 200);Thread.sleep(2000);c.adicionaAcoes("EMP34", 400);Thread.sleep(2000);c.adicionaAcoes("ACDF89", 300);Thread.sleep(2000);c.adicionaAcoes("EMP34", -200);Thread.sleep(2000);c.adicionaAcoes("COMP02", 150);

}

Fig. �.�: Exemplo do grá�co gerado pela aplicação

Indo a fundo no padrão Observer

A estrutura do padrão Observer é bem simples: a classe que está sendo

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 104: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Compondo com múltiplos objetos - Observer Casa do Código

observada é composta por uma lista de observadores. Além disso, a classenormalmente oferece métodos que permitem a adição e remoção desses ob-servadores em tempo de execução. Dessa forma, quando o estado da classeobservada é alterado, todos os observadores são noti�cados. A �gura �.��apresenta um diagrama com a estrutura do padrão.

Fig. �.��: Estrutura do padrão Observer

Observe que no diagrama existe uma hierarquia de classes composta pe-las classes Observavel e ObservavelConcreto que não havia sido re-presentada no exemplo, no qual esses dois papéis são desempenhados pelamesma classe. Essa abstração de objetos observáveis pode ser extremamenteútil quando houver mais de uma classe que puder emitir noti�cações. Ima-gine, por exemplo, diversos componentes grá�cos que podem noti�car aocorrência de eventos gerados por ações do usuário. Nesse contexto podeser importante a capacidade de abstrair esse comportamento e atribuir ob-servadores ao mesmo tipo de classe.

Uma importante consequência desse padrão é o desacoplamento entre aclasse que está sendo observada e a observadora. A interface que deve serimplementada pelos observadores é o contrato que une as implementações.Dessa forma, um mesmo observador pode ser adicionado em diferentes ob-

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 105: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Delegando comportamento com composição

jetos, recebendo noti�cações de todos eles, e vários observadores diferentespodem receber noti�cações do mesmo objeto. No Observer ao invés depossuir uma hook class, a classe observada pode possuir um conjunto, sendoque o hook method de todas é chamado no momento em que deve ocorrer anoti�cação.

É importante ressaltar, especialmente ao se falar do Observer, que di-versas variações são possíveis. Por exemplo, podem existir vários métodos denoti�cação para diferentes tipos de evento, que precisam ser implementadospela classe observadora. Nesse caso, cada método seria chamado em uma si-tuação diferente. No exemplo apresentado, poderia haver um método paranoti�car quando uma ação é removida da carteira. Outro ponto em que podehaver variação é em como os observadores são gerenciados. Eles poderiam,por exemplo, ser passados no construtor e, outras vezes, não faz sentido haverum método de exclusão. Lembre-se que o padrão é apenas uma referência ecabe ao desenvolvedor adaptá-lo ao seu contexto!

Ao lidar com a composição de diversas hook classes, é importante levarem consideração como o que ocorre em uma in�uencia as outras. Por exem-plo, o que acontece se uma das classes lança uma exceção? As outras devemcontinuar sendo executadas? Em caso positivo, deve haver um tratamento deerro separado para cada invocação. Outra questão seria em relação à possi-bilidade de demora na execução de um observador in�uenciar a velocidadede noti�cação dos outros. Dependendo dos requisitos da aplicação, pode sernecessário a chamada do hook method de cada classe em um thread diferente.

Padrão Observer nas APIs Java

O padrão Observer pode ser visto com frequência em várias APIs dalinguagem Java. Umdosmotivos dele poder ser notado de forma tão explícitaé porque diversas classes permitem que suas mudanças sejam recebidas pelaaplicação através de observadores. Como foi dito, essa é uma boa forma depermitir o desacoplamento entre a classe que gera e a que recebe o evento.

A interface ActionListener, por exemplo, é utilizada no Swing nasclasses que desejam tratar eventos de interface. No caso, os objetos obser-vados são componentes de interface, como a classe JButton. O observa-dor, que também é chamado de listener, é adicionado no componente atra-

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 106: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Compondo com múltiplos objetos - Observer Casa do Código

vés do método addActionListener() e precisa implementar o métodoactionPerformed() para tratar os eventos.

Outro exemplo de implementação do Observer, agora nas APIs do JavaEE, é o MessageListener para o recebimento de mensagens JMS. Essainterface precisa ser implementada por classes que desejam receber men-sagens de um serviço de mensageria, as quais devem implementar o mé-todo onMessage() para tratar cada mensagem recebida. Uma instânciade MessageConsumer com a �la ou o tópico de origem é onde o listenerdeve ser con�gurado. Os Message Driven Beans, que são EJBs responsáveispor tratar mensagens JMS, também implementam essa interface.

Há dezenas de outros exemplos dentro das clássicas APIs do Java. Todosos listeners de sessão das servlets, os listeners de fase da JSF e os listeners demudanças de valores em entidades na JPA são bons exemplos de implementa-ção do Observer, que desacoplam seu código e facilitam a extensão. Todosos observadores podem trabalhar de forma totalmente independente, sem terconhecimento da existência dos outros.

Uma coisa interessante em aprender padrões é que a partir deles �ca maisfácil compreender o funcionamento de APIs e frameworks que utilizamos. Écomo se passássemos a olhar para eles com outros olhos. Imagino que sejaalgo comparável a quando estudamos física, química ou biologia e passamosa compreender melhor o mundo ao nosso redor. Ao perceber que um padrãofoi implementado em uma solução, é mais fácil entender como utilizar aquelaAPI e quais as suas consequências naquele contexto.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 107: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Delegando comportamento com composição

A ���������O������� � � ������O���������

Uma curiosidade que poucos sabem é que existe desde a JDK �.� a in-terface java.util.Observer e a classe java.util.Observable.A interface Observer possui o método update() que recebe comoparâmetro a instância de Observable que gerou o evento emais umob-jeto que é o parâmetro passado para o método notifyObservers().A ideia da classe Observable é que ela seja estendida por classes quequeiram emitir noti�cações sobre mudanças em seu estado. Ela pos-sui métodos implementados para gerenciar e noti�car as instâncias deObserver.

O que émais interessante é que essa classe não é utilizada em nenhumlugar da API da JDK, mesmo o padrão Observer sendo utilizado emdiversos contextos. A questão principal é que essas classes consideram oObserver como uma implementação pronta e não como uma soluçãoque pode ser adaptada para diversos contextos. Por mais que a soluçãoapresentada por essas classes seja reutilizável, di�cilmente ela será maisadequada do que uma implementação do padrão para um contexto es-pecí�co.

�.� C������������ ������ �� ��������Esse capítulo apresentou como a composição pode ser utilizada para inserirpontos de extensão que permitem que uma porção de lógica possa ser inse-rida durante a execução de uma classe. Esses pontos podem ser usados parainserir uma lógica especí�ca da aplicação ou de um domínio em uma classemais genérica, tornando-a mais reutilizável. Por meio de hook classes, a adap-tação de comportamento é feita através da combinação de objetos, podendo odesenvolvedor criar novos componentes para a con�guração de classes �nal.

A extensão de comportamento pode ocorrer por diferentes motivos e emvariados contextos. Pode ser delegada para outra classe a execução de umalgoritmo que pode ser trocado, como é o caso do padrão Strategy. Outrocenário é a delegação da lógica relativa às diferenças de estado de um objeto,

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 108: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Considerações �nais do capítulo Casa do Código

sendo esse o foco dopadrão State. Finalmente, o padrão Observer associaa execução de métodos de diversas outras classes à mudança de estado ou aeventos ocorridos em um objeto.

Além de compreender os padrões e esses cenários que favorecem o uso dacomposição, é importante também conhecer os princípios e consequênciaspor trás dessas decisões. A composição pode inclusive ser combinada comoutros tipos de solução, como no padrão Bridge no qual ela é combinadacom o uso de herança em uma hierarquia diferente.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 109: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

C������� �

Composição recursiva

“Recursão: ver recursividade. Recursividade: se ainda não entendeu, verrecursão”– Piada Nerd

No capítulo anterior, foi visto como a composição pode ser utilizada paracombinar classes de diferentes tipos para permitir que a funcionalidade sejaestendida. Através do polimor�smo, a classe composta pode abstrair a im-plementação da classe que a está compondo, permitindo que diversas clas-ses diferentes possam ser colocadas naquela posição. Agora imagine se umaclasse puder ser composta pela sua própria abstração, ou, em outras palavras,se ela possuir um atributo de sua superclasse ou de uma de suas interfaces.Isso permitiria que uma instância da mesma classe pudesse ser utilizada paracompô-la, tornando possível a criação de uma grande estrutura dessa forma.

Eu sei que isso parece sermuito abstrato, mas vamos tentar pensar em um

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 110: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Compondo um objeto com sua abstração Casa do Código

exemplo do mundo físico para ilustrar esse conceito. Imagine uma peça deLego que provê na parte de cima uma forma para que outras peças possam serencaixadas e, na parte de baixo, a forma do encaixe para que ela seja ligada naparte de cima. Como a mesma peça possui as duas formas de encaixe, possí-vel ligar e combinar várias peças do mesmo tipo em diferentes con�gurações.Quando consideramos que podem existir peças diferentes com omesmo for-mato de encaixe, as possibilidades são ainda maiores.

Voltando ao mundo do so�ware, é como se a forma que uma peça provêem sua parte superior fosse a interface da classe. Analogamente, o tipo deum atributo seria o encaixe disponibilizado para que uma outra peça possa seencaixar. Dessa forma, uma classe que implementa uma interface se encaixaem um atributo daquele tipo. Na composição simples, utilizando essa ideiaé possível conectar apenas dois objetos. Mas quando uma classe provê umainterface e um atributo domesmo tipo, é possível criar uma estrutura com di-versos objetos. Nesse caso, as próprias possibilidades de con�gurações dessaestrutura já tornam o so�ware mais �exível.

�.� C������� �� ������ ��� ��� ���������A recursividade é um conceito amplamente utilizado em programação. Umafunção recursiva, por exemplo, é aquela que chama a si mesma como parte dasua lógica. Em um algoritmo recursivo, a função é chamada novamente paradiminuir o problema que precisa ser resolvido. Dessa forma, ele vai sendoreduzido até o ponto em que sua resolução é trivial. Esse é chamado de pontode parada.

Um exemplo clássico e simples de função recursiva é o fatorial de umnúmero, que consiste na multiplicação de todos os números inteiros epositivos menores ou iguais àquele número. A listagem a seguir apresenta aimplementação do fatorial utilizando recursão. Observe que, pela de�niçãode fatorial, o fatorial de um número n é n vezes o fatorial de n-1. O fato deque o fatorial de � é � é utilizado como critério de parada.

Listagem �.� - Cálculo do fatorial com algoritmo recursivo:

public static int fatorial(int n){

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 111: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Composição recursiva

if(n == 1)return 1;

return n * fatorial(n-1);}

Quando criamos uma estrutura com objetos, é possível utilizar tambémo conceito de recursividade para que ela seja de�nida utilizando sua própriade�nição. Isso é feito quando uma classe contém um atributo do próprio tipoda classe. Esse tipo de estrutura é muito utilizado para de�nir muitas estrutu-ras de dados, como listas ligadas, árvores e grafos. Por exemplo, no capítulo� foi apresentada busca em profundidade de grafos, onde a classe No possuíauma lista de instâncias de No.

Para exempli�car vamos considerar o código do elemento de uma listaligada de�nido a seguir. A classe Elemento possui um atributo proximo

que possui o mesmo tipo. Dessa forma, é possível encadear diversasinstâncias dessa classe formando uma lista. O método contar() é ummétodo recursivo no qual o próprio método é chamado novamente, porémna instância que estiver no atributo proximo. A condição de parada équando um elemento não possui próximo, signi�cando que ele é o últimoelemento da lista e deve retornar um. Se houver um próximo elemento, entãoa contagem deve retornar um mais a contagem do próximo.

Listagem �.� - Exemplo de elemento de lista ligada:

public class Elemento{private Object valor;private Elemento proximo;

public Elemento(Object valor){this.valor = valor;

}public Object getValor(){

return valor;}public Elemento getProximo(){

return proximo;}

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 112: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Compondo um objeto com sua abstração Casa do Código

public void setProximo(Elemento proximo){this.proximo = proximo;

}public int contar(){

if(proximo == null)return 1;

return 1 + proximo.contar();}

}

Até então não tem muita diferença entre as estruturas que podem serconstruídas em uma linguagem estruturada, como C, em que se podem criarestruturas de dados utilizando a construção struct. O grande diferencialdessa de�nição recursiva de estruturas é a utilização do polimor�smo. Dessaforma, o tipo base utilizado na estrutura pode ser uma interface ou uma su-perclasse que possuem diversas implementações. Sendo assim, diversos tipospodem ser utilizados na criação da estrutura e a implementação de um podeser abstraída pelos outros. Adicionalmente, essa estrutura também pode serfacilmente estendida através da criação de novos tipos que possuam amesmaimplementação.

A �gura �.� apresenta diferentes possibilidades para a implementação dacomposição recursiva. Uma delas é ela ser de�nida na superclasse. Dessaforma, todas as subclasses poderão ser compostas por uma classe da hierar-quia. Outra possibilidade é quando a composição recursiva é de�nida emuma ou mais subclasses. Nessa opção, apenas alguns tipos podem ser uti-lizados para a implementação da recursividade e outros serão pontos �naisna estrutura. Nesse capítulo serão apresentados padrões que utilizam essasdiferentes alternativas de implementação.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 113: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Composição recursiva

Fig. �.�: Possibilidades para composição Recursiva

É importante ressaltar que existem variações das possibilidades apresen-tadas. A composição, por exemplo, pode ser de uma oumúltiplas instâncias, oque geraria soluções diferentes. Dependendo do domínio, pode também ha-ver mais de um atributo que possua uma de�nição recursiva, mas que possuadiferentes signi�cados para a classe. Em uma árvore, por exemplo, uma listade nós pode representar os nós �lhos e um atributo simples pode representaro pai.

�.� C�������� - ������ �� �������� � �� ����-�����

“Para sempre é composto de agoras.”– Emily Dickinson

Um dos erros de pessoas que estão iniciando na orientação a objetos émodelar o coletivo estendendo o indivíduo. Uma cesta de maçãs não é umamaçã para que o uso de herança seja justi�cado. Similarmente, um conjuntode processos não é um processo, logo também não é correto utilizar herançapara representá-lo. Normalmente isso é feito para a subclasse invocar direta-mente os métodos da superclasse e, dessa forma, a subclasse de�ne um novoconjunto de métodos, ignorando a interface pública da superclasse.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 114: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Composite - quando um conjunto é um indivíduo Casa do Código

Apesar do que foi dito, existem situações em que o conjunto também re-presenta um único indivíduo. Um conjunto de produtos pode ser um pro-duto? A resposta é sim dependendo do contexto. Uma loja ou um supermer-cado frequentemente montam kits com diversos produtos que são vendidoscomo um produto único. Como representar isso em um sistema? A questão éque esse conjunto de produtos terá uma lógica diferente para diversos fatores,como para o cálculo do preço, por exemplo. Porém, em outros cenários, eledeve ser considerado como um produto comum.

Essa seção apresenta o padrão Composite cujo objetivo é prover umasolução para objetos que representam um conjunto de objetos, mas que com-partilham a mesma abstração deles. Ele tem o potencial de encapsular umalógica complexa, dividindo-a em uma hierarquia de classes e simpli�cando asolução �nal. Todos que já utilizaram um Composite em um projeto realsabem que é um padrão realmente muito poderoso.

Apresentando o padrão Composite

O padrão Composite possui o objetivo de permitir que a mesma abs-tração possa ser utilizada para uma instância simples e para seu conjunto. A�gura �.� apresenta a estrutura básica usada no padrão. Uma abstração apre-senta uma interface básica com as operações que devem ser executadas tantonos objetos simples quanto nos compostos. A classe Simples representaum único indivíduo e a classe Composto representa o conjunto. Observeque existe uma relação de composição entre Composto e Abstracao, quemostra que um objeto da classe Composto pode ser composto por outrotambém da classe Composto.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 115: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Composição recursiva

Fig. �.�: Estrutura básica do Composite

As operações de�nidas na abstração devem ser implementadas em todasas subclasses. Para as subclasses simples, que não forem compostas por outrasdo tipo da abstração, pode ser uma implementação direta. Por exemplo, umaclasse Produto pode disponibilizar ummétodo para retornar o preço e pos-suir apenas o acesso a um atributo. O segredo do padrão Composite está naimplementação dos métodos compostos, que devem invocar os métodos dasinstâncias que o compõe e executar uma lógica para criar um resultado único.No exemplo do produto, o produto composto, uma classe KitProdutos,para retornar o preço ele poderia somar o preço dos produtos que o compõee dar um desconto em cima.

Os objetos organizados comopadrão Composite acabam seguindoumaestrutura de árvore. As folhas seriam as classes simples, que não possuem acomposição. Apesar de no diagrama da �gura �.� apenas um tipo de classesimples ser representado, é importante ressaltar que pode haver diversas clas-ses desse tipo com diferentes implementações. Os ramos seriam representa-dos pelas classes compostas, que seriam compostas pelos seus �lhos. O nú-mero de �lhos de uma classe composta varia com a necessidade da aplicação,podendo ser duas instâncias ou um conjunto.

O grande benefício do Composite é tornar totalmente transparente paraos clientes o fato de estarem trabalhando com um objeto simples ou com-posto. Isso torna muito simples adicionar uma implementação de um objetocomposto em uma hierarquia já existente na aplicação.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 116: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Composite - quando um conjunto é um indivíduo Casa do Código

C�������� �� ����������� ��������

Um contexto em que o padrão Composite émuito utilizado é para acriação de componentes grá�cos. Muitos são formados a partir da com-posição de outros componentes existentes, porém são tratados como umsó. Por exemplo, um componente de seleção de arquivos normalmentepossui botões e uma caixa de texto. Dessa forma, quando um métodosetEnable() for chamado, ele deverá coordenar a invocação dessemé-todo em todos os seus componentes internos.

Em Java, esse conceito é utilizado em suítes grá�cas para aplicaçõesdesktop, como no Swing e no SWT, e em frameworks com componentespara páginas web, como o JSF. O framework SwingBean, por exemplo,possui um que representa um formulário e é composto por diversos ou-tros componentes.

Modelando a representação de trechos aéreos

Para exempli�car a aplicação do padrão Composite, vamos modelar asclasses que representam trechos aéreos em uma aplicação de turismo. Paraesse exercício vamos considerar que as informações principais de um trechoaéreo são sua origem, seu destino e o seu preço. Muitas vezes, quando umcliente solicita um voo a partir de uma origem e um destino, não existe umvoo direto que conecta aqueles dois aeroportos. Nesse caso, normalmentebusca-se então um voo que pousa em um aeroporto intermediário, de onde ocliente pode pegar um outro voo até o seu destino. Nesse caso, além do preçodos dois trechos, deve-se adicionar a taxa de conexão cobrada pelo aeroportointermediário. A questão é independente de o trecho aéreo ser composto pormais de um trecho ou não, em certos pontos o sistema deve tratá-los de formasimilar.

O primeiro passo é a de�nição de uma interface para de�nir quais servi-ços serão disponibilizados pela classe que representará o trecho simples e ocomposto. Pela descrição do problema, o trecho deve ser capaz de retornara origem, o destino e o preço. Sendo assim, a listagem a seguir apresenta a

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 117: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Composição recursiva

interface TrechoAereo que de�ne métodos para recuperação dessas infor-mações. Em seguida, é apresentado o código da classe TrechoSimples,que possui uma implementação cuja lógica é apenas o acesso a atributos.

Listagem �.� - Interface que de�ne os serviços de um trecho aéreo:

public interface TrechoAereo {public String getOrigem();public String getDestino();public double getPreco();

}

Listagem �.� - Representação de um trecho aéreo simples:

public class TrechoSimples implements TrechoAereo{private String origem;private String destino;private double preco;

public TrechoSimples(String origem, String destino,double preco) {

this.origem = origem;this.destino = destino;this.preco = preco;

}public String getOrigem() {

return origem;}public String getDestino() {

return destino;}public double getPreco() {

return preco;}

}

Para a implementação do trecho composto, será aplicado o padrãoComposite. Nesse caso, o trecho composto possui dois atributos, respecti-vamente para representar o primeiro e o segundo trecho que o compõe. Tam-bém existe um atributo que representa a taxa de conexão a ser paga de forma

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 118: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Composite - quando um conjunto é um indivíduo Casa do Código

adicional ao preço de cada trecho individualmente. Note que no construtor éfeita uma veri�cação se o segundo trecho começa no mesmo local em que oprimeiro termina. Pode ser observado que as operações que precisavam serimplementadas delegam parte da execução para os trechos que compõem oobjeto.

Listagem �.� - Representação do trecho aéreo composto:

public class TrechoComposto implements TrechoAereo {private TrechoAereo primeiro;private TrechoAereo segundo;private double taxaconexao;

public TrechoComposto(TrechoAereo primeiro,TrechoAereo segundo, double taxaconexao) {

this.primeiro = primeiro;this.segundo = segundo;this.taxaconexao = taxaconexao;if(primeiro.getDestino() != segundo.getOrigem())

throw new RuntimeException("O destino do primeiro" +"não é igual a origem do segundo");

}public String getOrigem() {

return primeiro.getOrigem();}public String getDestino() {

return segundo.getDestino();}public double getPreco() {

return primeiro.getPreco() + segundo.getPreco() +taxaconexao;

}}

A �gura �.� apresenta um exemplo de como um voo com três trechos se-ria representado utilizando a estrutura de classes apresentada. A seguir tam-bém é mostrada uma listagem com o código para a criação dos trechos sim-ples e compostos. Observe pelo exemplo, que TrechoComposto pode sercomposto tanto por instâncias de TrechoSimples quanto por instâncias do

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 119: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Composição recursiva

próprio TrechoComposto de forma transparente, criando uma estrutura deárvore.

Fig. �.�: Representação de trechos aéreos

Listagem �.� - Criação de trechos compostos:

TrechoSimples ts1 =new TrechoSimples("São Paulo", "Brasília", 500);

TrechoSimples ts2 = new TrechoSimples("Brasília","Recife",300);TrechoSimples ts3 = new TrechoSimples("Recife","Natal", 350);TrechoComposto tc1 = new TrechoComposto(ts2, ts3, 30);TrechoComposto tc2 = new TrechoComposto(ts1, tc1, 50);

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 120: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Encadeando execuções com Chain of Responsibility Casa do Código

P�� ��� T�����C������� ��� ���� �������� T�����-S�������

Pode parecer tentador a princípio fazer com que TrechoComposto

estenda TrechoSimples em vez de criar uma interface comum entreas duas. Se isso ocorresse, a classe TrechoComposto iria simplesmenteignorar a estrutura de dados da classe TrechoSimples e ainda teriaque lidar com seu construtor. Isso geraria ummau cheiro de código cha-mado de Refused Bequest, que ocorre quando uma subclasse não utilizaa estrutura e os métodos herdados da sua superclasse. Para que aquelaherança é dada para a classe se ela não a utiliza?

�.� E��������� ��������� ��� C���� �� R��-�����������

“Nenhuma corrente pode ser mais forte que seu elo mais fraco.”– Provérbio Popular

Quando em um so�ware uma funcionalidade é executada, normalmenteexistem diversos passos que precisam ser executados em sequência. A di�cul-dade ocorre quando se precisa de �exibilidade na con�guração desses passos.Por exemplo, pode ser necessária a modi�cação da ordem dos passos e atémesmo para a inserção de novos passos. Um outro objetivo seria a possibili-dade de reutilização desses passos em outros processamentos ou para outrasaplicações.

Chain of Responsibility é um padrão de projeto que cria uma ca-deia de execução na qual cada elemento processa as informações e em seguidadelega a execução ao próximo da sequência. Em sua implementação tradici-onal, os elementos são percorridos até que um deles faça o tratamento darequisição, encerrando a execução depois disso. Como alternativa, tambémé possível criar uma cadeia de execução onde cada um executa sua funciona-lidade até que a cadeia termine ou ela seja explicitamente �nalizada por umdos elementos.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 121: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Composição recursiva

Recuperação de um arquivo remoto

Para �car mais concreto o entendimento do padrão, imagine que umarquivo, como uma lista de certi�cados digitais revogados, é atualizado deforma periódica em um servidor remoto, contendo a data em que sua vali-dade é expirada. Para tornar recuperações frequentes mais e�cientes a apli-cação criou dois tipos de cache, sendo um em memória e outro na base dedados. Dessa forma, primeiro veri�ca-se se existe um arquivo válido em me-mória, em seguida no banco de dados e somente então, se não for encontrado,o arquivo é recuperado do servidor remoto.

Em uma implementação tradicional, toda essa lógica seria implemen-tada na mesma classe, com diversos condicionais em sequência para veri�-car se uma determinada estratégia seria adequada para recuperação do ar-quivo. Utilizando o padrão Chain of Responsibility, cada possibili-dade é implementada em uma classe diferente, as quais são encadeadas emum �uxo de execução. Cada uma delas veri�ca se o arquivo existe e é válido,retornando omesmo em caso positivo e delegando a execução para a próximainstância da cadeia em caso negativo. A �gura �.� representa essa cadeia deexecução.

Fig. �.�: Cadeia de execução para recuperação de arquivo

Para implementarmos essa sequência de execução, o primeiro passoé de�nirmos uma superclasse que de�ne um elemento da cadeia. Aclasse RecuperadorArquivo, apresentada na próxima listagem, pos-sui um atributo do seu mesmo tipo chamado proximo que representao próximo elemento. Essa classe também de�ne um método abstrato

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 122: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Encadeando execuções com Chain of Responsibility Casa do Código

chamado recuperarArquivo() que é um hook method que deve serimplementado pelas subclasses para a recuperação do arquivo de acordocom a estratégia desejada. Além disso, o método chamarProximo() éresponsável por veri�car se existe um próximo elemento e invocá-lo. Adicio-nalmente, o método recuperar() tenta recuperar o arquivo, retornando-ose ele for válido e chamando o próximo elemento da cadeia em caso negativo.

Listagem �.� - Classe abstrata que de�ne a lógica do Chain of Respon-sibility:

public abstract class RecuperadorArquivo {private RecuperadorArquivo proximo;

public RecuperadorArquivo(RecuperadorArquivo proximo) {this.proximo = proximo;

}public Arquivo recuperar(String nome){

Arquivo a = recuperaArquivo(nome);if(a==null || !a.isValido())

return chamarProximo(nome);else

return a;}protected Arquivo chamarProximo(String nome) {

if(proximo == null)throw new RuntimeException("Não foi possível " +" recuperar o arquivo");

return proximo.recuperar(nome);}

protected abstract Arquivo recuperaArquivo(String nome);}

Cada subclasse de RecuperadorArquivo deve implementar o mé-todo recuperaArquivo() buscando o arquivo em uma fonte diferente.Pela descrição do problema, as implementações irão buscar o arquivo nobanco de dados, de um cache em memória e de em um servidor remoto.A listagem a seguir apresenta a classe que faz a busca em um cache em

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 123: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Composição recursiva

memória para exempli�car como seria uma dessas implementações. Ométodo recuperaArquivo() veri�ca se existe o arquivo no mapa eretorna nulo caso não exista. Para armazenar novos arquivos no mapa, ométodo chamarProximo() também é implementado para permitir que setenha acesso ao arquivo retornado pelo próximo.

Listagem �.� - Classe que de�ne o recuperador que de�ne o cache emmemória:

public class RecuperadorCacheMemoria extends RecuperadorArquivo{private Map<String, Arquivo> cache = new HashMap<>();

public RecuperadorCacheMemoria(RecuperadorArquivo proximo) {super(proximo);

}protected Arquivo recuperaArquivo(String nome) {

if(cache.containsKey(nome))return cache.get(nome);

return null;}protected Arquivo chamarProximo(String nome) {

Arquivo a = super.chamarProximo(nome);cache.put(nome, a);return a;

}}

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 124: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Encadeando execuções com Chain of Responsibility Casa do Código

A����� ��� �� T�������M������

Desa�o o leitor a observar melhor o exemplo de código dessa se-ção e achar uma implementação do padrão Template Method! Ométodo recuperar() da classe RecuperadorArquivo implementauma lógica comum aos elementos da cadeia de execução e delega ospontos variáveis a hook methods que serão implementados pelas sub-classes. Observe que o método chamarProximo(), também cha-mado pelo Template Method, apesar de nao ser abstrato, tam-bém pode ser considerado um hook method. Pelo exemplo da classeRecuperaCacheMemoria pode-se perceber como ele pode ser sobres-crito pela subclasse para a adição da funcionalidade.

Como já ressaltamos, é comumhaver uma ligação forte entre diferen-tes padrões de projeto. Muitas vezes eles aparecem em conjunto e até édifícil diferenciá-los.

Estrutura e alternativas de implementação

O padrão Chain of Responsibility coloca cada elemento que iráparticipar do processamento da lógica como o elemento de uma lista ligada,onde cada um temacesso ao próximona cadeia de execução. A�gura �.�mos-tra como a composição recursiva é utilizada na implementação da estruturade classes. Nesse caso, uma hook class do mesmo tipo é de�nida na super-classe, signi�cando que todo tipo de elemento da cadeia pode ser compostopor um próximo. Caso ele seja nulo, ou possua um elemento que representeum Null Object, será marcado o �nal da cadeia de execução.

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 125: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Composição recursiva

Fig. �.�: Estrutura do Chain of Responsibility

Nessa representação, o método executar() disponibiliza comoAPI pública a execução de todos elementos da cadeia e o métodoexecutarElemento() representa a execução da lógica somente daqueleelemento. Dessa forma, o método executar() invoca primeiro a lógicaimplementada na própria classe e em seguida invoca de forma recursiva opróprio método executar() no próximo elemento.

Como alternativa de implementação, o método executarElemento()

pode receber como parâmetro o próximo elemento, sendo também respon-sável pela sua invocação. Dessa forma, cabe à implementação de cada classedecidir em que situações o próximo deve ser invocado e em que momentoisso deve acontecer. Utilizando essa alternativa, cada implementação podeincluir lógica que será executada tanto antes quanto depois da execução dele.

A partir da implementação do Chain of Responsibility adquire-se uma grande �exibilidade para a con�guração da lógica que será executada.Por exemplo, facilmente um novo elemento da cadeia pode ser introduzido,ou um existente pode ser excluído. A própria ordem em que os elementos sãoorganizados pode gerar uma grande diferença no �nal. Por exemplo, para autilização do RecuperadorArquivo em dispositivos com memória RAMlimitada, o elemento que faz o cache em memória poderia facilmente ser ex-cluído da cadeia. Em um outro cenário em que o arquivo pudesse estar dis-

��

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 126: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Encadeando execuções com Chain of Responsibility Casa do Código

ponível em mais de um servidor, mais instâncias do elemento que o buscaremotamente com endereços diferentes poderiam ser incluídas.

Filtros em aplicações Web

Um exemplo do uso do Chain of Responsibility em APIs doJava EE são os �ltros de aplicações web [��]. Um exemplo da estrutura básicade um �ltro está representado na próxima listagem. A classe que representao �ltro deve implementar a interface Filter e, além de implementaros métodos init() e destroy() que servem respectivamente para ainicialização e �nalização, ainda é preciso incluir o método doFilter()

que é onde a lógica será efetivamente implementada.

Listagem �.� - Exemplo de �ltro para aplicações Web:

public class FiltroExemplo implements Filter{

public void doFilter(ServletRequest req,ServletResponse resp,FilterChain chain){

//antes do próximo filtrochain.doFilter(req,resp);//depois do próximo filtro

}public void destroy{}public void init(FilterConfig config){}

}

O método doFilter() recebe como um dos parâmetros uma instân-cia da classe FilterChain, que representa justamente a cadeia de �ltrosque está sendo executada. Ao ser chamado o método doFilter() nessainstância, o controle da execução é passado para o próximo �ltro ou, se nãohouver próximo, para o servlet que irá tratar a requisição. Observe que comoo próximo �ltro é invocado dentro do método doFilter(), pode ser adi-cionada funcionalidade tanto antes quanto depois da sua execução. Inclusiveo próximo �ltro nem precisa ser invocado, como no caso da requisição ser

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 127: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Composição recursiva

direcionada a uma página de erro. Outra coisa que pode ser feita é a modi�-cação dos parâmetros que serão passados para os próximos �ltros, inclusiveencapsulando-os com a utilização de Proxies (um padrão que será visto nopróximo capítulo).

Um dos usos mais clássicos para �ltros em aplicações web é para veri�carse o usuário está logado. Nesse caso, ele veri�ca se existe umusuário válido nasessão e redireciona para a página de login em caso negativo. Porém existemvários outros usos para �ltros, como: realização de controle de acesso; fazeruma busca por parâmetros maliciosos; iniciar e �nalizar uma transação coma base de dados; e modi�car a saída gerada pelo servlet. Em geral os �ltrossão utilizados para funcionalidades que precisam ser executadas em diversasrequisições diferentes. A utilização do Chain of Responsibility emsua implementação permite que a ordem dos �ltros seja modi�cada e que�ltros possam ser facilmente introduzidos ou removidos.

Essa modelagem utilizada para os �ltros é mesma dos interceptors do fra-mework Struts � [�]. Um interceptor tem uma função bem parecida, que é derealizar algum processamento antes e/ou depois do tratamento de uma requi-sição web. Grande parte das funcionalidades do framework, como a injeçãodos parâmetros do request ou a introdução de objetos da sessão do usuário, éimplementada através dos interceptors. Os interceptors que atuam para cadarequisição são con�gurados através de um arquivo XML. Nesse caso, a uti-lização do padrão Chain of Responsibility permite que esses componentesdo framework possam ser facilmente removidos ou que novos componentesespecí�cos da aplicação possam ser adicionados.

�.� R���������� ���� �������� � �������� ����������� �������

Uma evolução comum que ocorre em aplicações é a necessidade de que maisuma ação seja realizada como resposta a um evento. Por exemplo, imagineque ao receber uma informação a aplicação precise persisti-la em uma base dedados. Como tempo, novos requisitos vão surgindo e exigemque novas açõessejam tomadas naquele cenário, como a chamada de um serviço remoto ou oregistro em uma trilha de auditoria. O objetivo dessa refatoração é permitir

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 128: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Refatorando para permitir a execução de múltiplas classes Casa do Código

que, na chamada de ummétodo, diversas classes possam ser executadas. Issodeve ser realizado de forma que não haja um acoplamento entre essas classese que isso seja transparente aos que invocam.

Para que essa refatoração possa ser realizada, é importante que a execu-ção dessa funcionalidade já esteja isolada utilizando a composição simples.Ou seja, que a partir de uma hook class con�gurada seja possível trocar a ló-gica que será executada, mas não incluir múltiplas classes para a execução.Diferentemente do padrão Observer, no qual a classe observada precisagerenciar a existência de múltiplos observadores, nesse caso é desejável que aexistência de múltiplas execuções seja transparente para a classe.

Para mostrar a refatoração para os dois padrões apresentados nesse capí-tulo, será retomado o exemplo da classe GeradorArquivo. O objetivo serápermitir que diversos pós-processadores possam atuar em cima do arquivogerado de forma transparente para a classe principal. Após o padrão Bridge,existe uma interface chamada PosProcessador que deve ser implementadapor qualquer implementação que irá compor a classe GeradorArquivos.No exemplo, os dois pós-processadores existentes respectivamente criptogra-fam e compactam o arquivo após a sua geração. Observe que se ambos foremutilizados, a ordem de processamento irá alterar o resultado �nal, pois umarquivo criptografado e compactado é diferente de um arquivo compactadoe criptografado.

As seções a seguir descrevem como cada um dos padrões apresentadosnessa seção poderiam ser implementados para resolver o problema com osrequisitos descritos.

Introduzindo um Composite

Para implementar o padrão Composite, o que precisa serfeito é a implementação de uma classe que possua a mesma inter-face de um pós-processador e seja capaz de coordenar a execuçãode diversos pós-processadores. A listagem a seguir mostra a classePosProcessadorComposto que desempenha esse papel. Observe queela é composta por um array de pós-processadores que são recebidos noconstrutor. Adicionalmente, o método processar() executa o mesmométodo de forma recursiva nas instâncias de PosProcessador que

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 129: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Composição recursiva

compõem a classe.Listagem �.�� - Criação de um pós-processador composto:

public class PosProcessadorComposto implements PosProcessador{

private PosProcessador[] processadores;

public PosProcessadorComposto(PosProcessador... p){processadores = p;

}public byte[] processar(byte[] bytes){

for(PosProcessador p : processadores){bytes = p.processar();

}return bytes;

}}

A grande vantagem do uso do Composite nesse caso é queele não exige nenhuma mudança nas classes existentes. A classePosProcessadorComposto pode ser adicionada facilmente na classeGeradorArquivo e todos os outros pós-processadores podem facilmenteser incluídos nela. A di�culdade de usar o Composite irá aparecer em ce-nários onde a integração entre as classes envolvidas não acontece sempre damesma forma. Isso pode exigir a criação de diversas classes que desempe-nham esse papel, mas possuem uma lógica de integração diferente.

Refatorando para Chain of Responsibility

Uma outra abordagem para a refatoração seria implementar o padrãoChain of Responsibility para permitir que os pós-processadores pos-sam ser organizados em uma cadeia de processamento. Isso vai exigir umamudança na abstração que representa um pós-processador para incluir acomposição recursiva, ou seja, para a adição de uma hook class que representeo próximo processador da cadeia. A interface PosProcessador precisariaser representada como uma classe abstrata, cuja listagem está apresentada aseguir.

Listagem �.�� - Classe PosProcessador para a criação de uma cadeia de

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 130: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Refatorando para permitir a execução de múltiplas classes Casa do Código

processamento:

public abstract class PosProcessador{

private PosProcessador proximo;

public PosProcessador(PosProcessador prox){proximo = prox;

}public PosProcessador(){

proximo = nem PosProcessadorNulo();}public byte[] processarCadeia(byte[] bytes){

bytes = processar(bytes);return proximo.processarCadeia(bytes);

}protected abstract byte[] processar(byte[] bytes);

}

A classe PosProcessador possui o atributo proximo de seu própriotipo, que deve ser passado no construtor. Caso seja o �nal da cadeia, um cons-trutor vazio pode ser invocado, o qual irá con�gurar um Null Object noatributo proximo. Ométodo abstrato processar() representa a lógica deprocessamento especí�ca daquela implementação que será invocada durantea execução da cadeia - e, por ele não poder ser invocado de fora, sua visibili-dade é protected. O método processarCadeia() é o responsável pelaexecução da cadeia e invoca não somente o método processar() da pró-pria classe, quanto o método processarCadeia() do atributo proximo.

Diferentemente da implementação do Composite, essa refatoração temum pequeno impacto nas outras classes envolvidas. Por exemplo, as classesque invocam pós-processadores precisarão alterar o método que está sendochamado de processar() para processarCadeia(). Ele poderia sermantido, porém às custas de uma mudança no nome do hook method queprecisa ser criado nos pós-processadores. Ou seja, como ométodo disponívelpublicamente não seria mais o ponto de extensão, o nome de algum delesprecisa sermodi�cado. Outrasmudanças devem ser feitas nas subclasses, queprecisamdisponibilizar o novo construtor que recebe um PosProcessador

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 131: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Composição recursiva

como parâmetro e alterar de implements para extends a relação comPosProcessador, que não é mais uma interface.

�.� C������������ ������ �� ��������Esse capítulo apresentou conceitos a respeito da composição recursiva, naqual objetos são compostos por outros que possuem uma de suas abstrações.Com a utilização do polimor�smo, é possível combinar diversas implementa-ções de uma mesma abstração formando uma estrutura �exível e complexa.A partir da criação de novas classes e sua inclusão em um de seus pontospode-se estender o seu comportamento. A própria organização das classesnão deixa de ser um ponto que pode ser adaptado e con�gurado.

O padrão Composite utiliza esse conceito para permitir a representaçãode objetos compostos, em outras palavras, objetos que combinam a funcio-nalidade de outros do mesmo tipo. Com esse padrão, uma das subclasses équem possui a composição recursiva. Nesse padrão as classes se organizamem forma de árvore, sendo as compostas os nós internos e as outras, as folhas.

Já o Chain of Responsibility cria uma estrutura parecida comuma lista ligada, na qual cada elemento possui uma referência para o pró-ximo. A composição recursiva normalmente é implementada na superclasse,pois todo tipo de elemento deve poder ter umpróximo. Apartir desse encade-amento de classes, é possível con�gurar quais elementos devem ser incluídosno processamento.

A utilização desse tipo de estrutura normalmente é ligada à necessidadede �exibilidade e con�gurabilidade da lógica da aplicação. A recursividade dasolução permite o desacoplamento entre os elementos e a criação de diversaspossibilidades de con�guração.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 132: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 133: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

C������� �

Envolvendo objetos

“Não existe colher. Você verá que não é a colher que se dobra, apenas vocêmesmo. "– Garoto com a colher, Matrix

O encapsulamento é uma característica que faz com que os clientes dasclasses precisem conhecer apenas a sua interface, abstraindo sua implementa-ção interna. O polimor�smo permite que a instância de uma classe possa seratribuída a uma variável, desde que obedeça à abstração utilizada como tipo.Essas duas características fazem com que exista uma ignorância, no sentidocorreto da palavra, da classe cliente em relação à classe com que ela realmenteestá lidando.

A partir desses conceitos, é possível que uma classe envolva uma instân-cia sem que a cliente saiba que está lidando com outro objeto. Dessa forma,ela servirá como intermediária entre a classe cliente e a classe alvo. Com isso,

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 134: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Proxies e decorators Casa do Código

ela obtém o controle da execução antes e depois de cada invocação de mé-todo. Isso permite que, por exemplo, funcionalidades sejam adicionadas eque validações sejam realizadas. Pode-se inclusive interceptar a execução deum método, retornando um valor ou lançando um erro mesmo sem invocaro método original.

Esse capítulo apresenta os padrões e conceitos relacionados a esse encap-sulamento de objetos. Este mesmo conceito pode ser utilizado para protegerum objeto, adicionar funcionalidades e até mesmo adaptar interfaces. Serámostrado que um dos grandes poderes dessa técnica é fazer o código clientepensar que ainda está lidando com o objeto original e fazer que esse objetonão saiba que está sendo envolvido.

�.� P������ � ����������“O olhar é, antes de mais nada, um intermediário que remete de mim a mimmesmo.”– Sartre

Diferentemente dos capítulos anteriores, vou começar já apresentandodois padrões ao mesmo tempo. O motivo para isso é que o padrão Proxy

possui uma estrutura muito parecida com o padrão Decorator, sendo quea diferença é basicamente a motivação e o contexto no qual os padrões sãoaplicados.

A ideia básica desses padrões é criar uma classe que envolve uma outra domesmo tipo. Dessa forma, ela pode ser passada de forma transparente comose fosse a classe original para quem a irá utilizar. A �gura �.� representa ofuncionamento desses padrões.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 135: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Envolvendo objetos

Fig. �.�: Envolvendo um objeto

A utilização desse tipo de técnica traz uma nova perspectiva para amode-lagem de classes, pois ao invés de concentrar todos os aspectos de ummétodoem apenas uma, é possível tratar diferentes questões em diferentes camadas.Assim a funcionalidade da classe que está encapsulando pode ser utilizadapara várias implementações. Para �car mais concreto, uma validação de pa-râmetros que normalmente é feita dentro dos métodos poderia ser colocadaem uma classe que envolve a classe principal. Dessa forma, essa validaçãopoderia ser reutilizada para diversas implementações.

A estrutura dos padrões

A estrutura do Proxy e do Decorator utiliza a composição recursiva.Isso signi�ca que ambos são compostos por uma classe que possui a mesmaabstração que eles. A estrutura desses padrões está apresentada na �gura �.�.Observe que são muito similares e permitem que a instância de uma classeencapsule um objeto e possa assumir o seu lugar. Dessa forma, os clientesdela não terão conhecimento de se estão lidando com a original ou com umaque a está encapsulando.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 136: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Proxies e decorators Casa do Código

Fig. �.�: Estrutura dos padrões Proxy e Decorator

O padrão Decorator recebeu esse nome relacionado ao fato de “de-corar” uma classe existente adicionando uma nova funcionalidade. Imaginecomo se a classe principal fosse um quadro e o Decorator fosse a moldura,que está acrescentando elementos no visual do quadro. O principal objetivodesse padrão é acrescentar funcionalidades a classes existentes de uma formatransparente a quem as utiliza. Isso também pode ser utilizado para umame-lhor distribuição de responsabilidades, permitindo que a principal se foquena regra de negócio central e que outras classes que a encapsulam cuidem deoutras funcionalidades.

O padrão Proxy estámais ligado a prover umobjeto para servir como in-termediário na comunicação com um outro principal. Um exemplo comumde utilização desse padrão é para representar localmente objetos que estãoem servidores remotos. Dessa forma, o proxy encapsula a lógica de acesso re-moto fazendo parecer que o objeto está sendo acessado localmente. Um ou-tro exemplo é para a criação de objetos “caros”, ou seja, que possuem um altocusto computacional de consumo de recursos para criação. O Proxy podeservir como uma representação para esse objeto, permitindo que o mesmoseja criado somente quando for necessário.

Uma coisa interessante da estrutura de ambos os padrões é sua compo-sição recursiva que permite que seja feito um encadeamento de classes, deforma similar ao Chain of Responsability. Sendo assim, um Proxy

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 137: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Envolvendo objetos

ou Decorator pode não somente encapsular a classe original, mas tambémencapsular uma classe que já esteja encapsulada com um outro Proxy ouDecorator. Dessa forma, a classe �ca com diversas camadas, cada uma comuma responsabilidade diferente, encapsulando a regra de negócio principal.

A principal diferença entre os dois padrões está no objetivo de cada um.Enquanto o Decorator visa adicionar novas funcionalidades, o Proxy

serve mais como uma proteção ao objeto principal. Em termos de estrutura,o Decorator costuma permitir que o objeto encapsulado seja passado noconstrutor, ou con�gurado de alguma outra forma. Desse modo, a compo-sição é feita utilizando a abstração, permitindo que qualquer implementaçãoseja encapsulada. Já o Proxy, muitas vezes, protege um objeto especí�co e acriação do objeto encapsulado ocorre dentro dele. No caso de um Proxy quefaça acesso remoto, ele nem compõe a classe propriamente dita, mas utilizaas classes de acesso à rede para delegar as chamadas.

Porém essa diferença estrutural é muito sutil e pode até mesmo ser con-siderada como um detalhe de implementação. Em relação aos objetivos, aproteção ao acesso feita pelo Proxy não deixa de ser uma funcionalidadecomo no Decorator. Por esse motivo, daqui para frente nesse livro o pa-drão Proxy será citado referenciando ambos os padrões.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 138: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Proxies e decorators Casa do Código

P��� ��� �� ������� �� ��� ��������� �� ����� ��� ��-��� ���� ��������������

Muitas vezes vejo desenvolvedores pensando duas vezes antes de criaruma abstração, no formato de uma interface, de algum componente im-portante do sistema. Por exemplo, vamos imaginar uma classe que re-torne propriedades de um arquivo de con�guração do sistema. Por maisque não exista a possibilidade de se criar uma nova implementação paraessa classe, pode ser interessante que ela possua uma abstração.

Um dosmotivos para isso é a possibilidade de se criar um Proxy queirá envolver essa implementação. Nesse exemplo da leitura do arquivo,poderia haver Proxies para funcionalidades como o armazenamentodos valores em memória e para a veri�cação da consistência desses va-lores. Sendo assim, antes de pensar que só vai haver uma classe, penseque a abstração também pode ser utilizada na criação de Proxies quepodem encapsular a classe principal.

Se você por acaso não criou uma interface para uma classe que precisade um Proxy, lembre-se que não é difícil refatorá-la com as refatoraçõesautomatizadas disponíveis nos IDEs. No Eclipse, por exemplo, ao extraira interface, existe a opção para utilizá-la ao invés do tipo da classe ondefor possível. Sendo assim, mais importante que criar a interface é obede-cer ao encapsulamento, evitando depender de detalhes internos da classe.

Cenários para aplicação de Proxies

A utilização de Proxies é um recurso tão poderoso que me arriscariaa dizer que é o padrão que mais utilizo. Antes de entrar em exemplos maisdetalhados, acho importante mostrar alguns cenários onde a utilização dessatécnica pode ser feita. Acredito que isso ajuda a enxergar o potencial dessespadrões.

Muitas vezes é necessária a criação de código de proteção para evitar queseja feita uma invocação de método com parâmetros inadequados ou em um

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 139: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Envolvendo objetos

contexto errado. Exemplos dessas situações seriam parâmetros que precisamestar em um formato especi�co ou métodos que só podem ser invocados emdeterminados estados da classe. Outro cenário onde é importante proteger aclasse é para requisitos de segurança, evitando que uma funcionalidade sejaacessada por um usuário não autorizado ou bloqueando parâmetros malici-osos que tentam executar ataques de injeção (ver quadro). Para todos essescasos, para evitar que esse código de proteção �que misturado com o códigodo negócio, muitas vezes é interessante criar um proxy exclusivamente parabloquear esses acessos indevidos.

A������ �� �������

Um ataque de injeção é aquele que se aproveita de aplicações que seutilizam da concatenação de strings para a formação de um comandoque será executado. Dessa forma, o atacante procura enviar um parâ-metro malicioso para a aplicação visando a formação de um comandodiferente durante a concatenação. Um dos exemplos mais famosos é oSQL Injection, no qual se procura injetar partes de comandos SQL emuma string que formará uma consulta que será executada no banco dedados. Outro exemplo de ataque é o Cross-site Scripting, que faz o uso deinjeção de javascript em parâmetros que são utilizados para a construçãode páginas web.

Um outro cenário onde o uso de Proxies é bastante comum é para fazercache da execução de métodos. Quando uma funcionalidade demorada pre-cisa ser executada de forma repetida em uma aplicação, é interessante arma-zenar o resultado obtido na primeira execução e reutilizá-lo emnovas invoca-ções. Colocar isso junto com a própria funcionalidade pode tornar o códigoconfuso, misturando duas responsabilidades no mesmo local. Dessa forma,um Proxy pode ser utilizado para encapsular a classe, armazenando o resul-tado da primeira execução e retornando-o sem executar a classe novamentenas próximas invocações.

Outra utilização clássica desse tipo de técnica é para tornar transparenteo acesso a um sistema remoto. Há um certo tempo atrás, a comunicação entre

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 140: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Proxies e decorators Casa do Código

aplicações remotas precisava ser implementada através do acesso a bibliote-cas de rede, sendo uma atividade difícil e trabalhosa. Hoje, grande parte dastecnologias para comunicação remota, como RMI, EJB ou mesmo classes cli-entes de webservices, encapsula todo esse processo dentro de um Proxy, oqual é responsável por acessar remotamente um componente disponibilizadopelo servidor. Esse Proxy implementa a mesma interface do objeto origi-nal, permitindo uma transparência desse processo para a aplicação, que podeacessar de forma indiferente o objeto local ou o remoto.

Em geral, esses padrões são utilizados para funcionalidades que são orto-gonais a estrutura do so�ware. Isso signi�ca que elas cortam a estrutura doso�ware estando presentes em diversas classes, não estando ligadas direta-mente à sua funcionalidade. Por exemplo, uma funcionalidade de registro deauditoria ou de controle de acesso costuma ser necessária em diversas classesde ummesmo tipo. A utilização de um Proxy permitiria, não somente a reu-tilização disso em diversas implementações de uma abstração, como tambémpermitiria que essa característica fosse modularizada, evitando seu espalha-mento em diversas classes.

Proxies na API do Java

As APIs da linguagem Java estão repletas de exemplos de implementaçãodos padrões Proxy e Decorator. Como todo bomencapsulamento, se vocêjá utilizou algum deles, provavelmente não percebeu a sua presença. Essaseção traz alguns exemplos de uso desses padrões, que podem servir comouma referência para o seu uso.

O primeiro exemplo está na API de coleções. Essa API possui algumasclasses que servem para “decorar” as coleções existentes adicionando funci-onalidades a elas. A classe Collections possui métodos estáticos comosynchronizedList(List<T> list) que retorna a lista recebida comsincronização e unmodifiableList(List<? extends T> list) paracriar uma lista não-modi�cável. O que na verdade esses métodos fazem éencapsular a lista passada em um Proxy que adiciona essa característica aoobjeto retornado.

Outro uso importante do padrão Proxy acontece no RMI (RemoteMethod Invocation), que é a tecnologia utilizada em Java para a invocação

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 141: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Envolvendo objetos

remota de métodos. Nesse modelo, após a criação da classe que disponibili-zará seus métodos remotamente, são gerados os chamados stubs e skeletons.Os skeletons são classes que recebem no servidor as requisições e invocam aclasse remota. Já os stubs são Proxies que �cam no cliente e representam aclasse remota, possuindo a mesma interface que ela.

A API padrão para o mapeamento objeto-relacional no Java EE, o JPA(Java Persitence API), possui uma funcionalidademuito interessante que per-mite um carregamento preguiçoso de listas associadas à classe principal. Issosigni�ca que se eu tenho uma classe Pessoa que possui uma lista de ins-tâncias de Telefone que são persistentes em uma base relacional, é possívelque ao carregar uma Pessoa, sua lista de Telefone seja carregada somentena primeira vez em que ela é acessada. Isso é feito a partir de um Proxy queacessa a base de dados e popula a lista somente em seu primeiro acesso.

P������ ���������

Quando criamos uma classe para ser um Proxy, ela precisa imple-mentar a mesma abstração da classe que está encapsulando para quepossa ser passada no lugar dela. Em Java é possível criar os chamadosProxies Dinâmicos. Eles têm a capacidade de assumir dinamicamenteuma interface, permitindo que seja utilizado para diversas interfaces.Esse é um recurso avançado da API de re�exão que foge ao escopo desselivro, porém é importante citar para que o leitor saiba da existência dessetipo de recurso.

�.� E������� �� P������“Falar é fácil. Mostre-me o código.”– Linus Torvalds

Nesse capítulo, um longo caminho foi percorrido sem nem um exemplode código. Para não decepcionar os leitores, introduzo esta seção dedicada aapresentar alguns exemplos de Proxy. A ideia é mostrar exemplos realistasque possam ser adaptados para outras aplicações. Cada subseção irá apresen-

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 142: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Exemplos de Proxies Casa do Código

tar um contexto, a interface utilizada pelas classes e em seguida o código doProxy.

Invocações assíncronas

Muitas vezes em alguns sistemas temos funcionalidades que demoram aser executadas. Nesses casos, é ruim deixar o usuário esperando, sendo queem alguns casos ele pode inclusive achar que o sistema travou. O que costumaacontecer é essas funcionalidades serem executadas em uma thread diferente.Veja a seguir a listagem da interface que abstrai os tipos de transação do sis-tema. Para as implementações, as informações da transação são armazenadasem atributos e normalmente passadas no construtor. Paramais detalhes sobreessa construção, ver o capítulo � sobre o padrão Command.

Listagem �.� - Interface que de�ne os serviços de um trecho aéreo:

public interface Transacao {public void executar();

}

Talvez o primeiro impulso fosse adicionar a criação de novas threads naspróprias implementações de Transacao. Porém isso iria gerar uma dupli-cação de código entre as classes que precisariam disso. Isso também causa-ria problemas se uma mesma transação precisasse ser executada na mesmathread ou em uma thread diferente dependendo do contexto. Nesse caso, acriação da thread na chamada da transação poderia resolver esse problema,mas não resolveria o problema da duplicação.

Uma forma de abordar isso seria a criação de um Proxy que en-capsulasse uma transação e a invocasse de forma assíncrona. A classeProxyAssincrono apresentada a seguir implementa essa funcionalidade.Ele pode ser utilizado em qualquer implementação de Transacao, evitandoa duplicação, e inclusive a mesma classe pode ser utilizada com ou sem oProxy.

Listagem �.� - Interface que de�ne os serviços de um trecho aéreo:

public class ProxyAssincrono implements Transacao{

private Transacao t;

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 143: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Envolvendo objetos

public ProxyAssincrono(Transacao t) {this.t = t;

}public void executar() {

Runnable r = new Runnable(){public void run() {

t.executar();}

};Thread t = new Thread(r);t.start();

}}

Realizando o cache de um DAO

Data Access Object, também conhecido como DAO, é um padrãocom o objetivo de isolar o acesso a dados de uma aplicação [�]. A partir dele,de�ne-se uma interface para o acesso a dados e uma implementação para en-capsular todas as responsabilidades referentes à interface com um banco dedados. Apesar de ser um padrão fora do escopo desse livro, ele descreve umaprática bastante popular em aplicações que lidam com dados.

Esse exemplo se contextualiza em um so�ware que possui um DAO

que realiza o acesso a um banco relacional. Imagine que a interface dessacamada seja a apresentada na listagem a seguir. Normalmente um DAO

costuma possuir mais métodos, porém nesse exemplo vamos considerarapenas três, respectivamente para recuperação, exclusão e gravação na basede dados. Imagine que a aplicação esteja tendo problemas de desempenhoe tentando diminuir a quantidade de acessos a banco. Uma forma de fazerisso é guardando alguns objetos em memória e reaproveitá-los em leiturassubsequentes. Como fazer isso com o menor impacto possível?

Listagem �.� - Interface que de�ne os serviços de um trecho aéreo:

public interface DAO {public Identificavel recuperar(int id);

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 144: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Exemplos de Proxies Casa do Código

public void excluir(int id);public void salvar(Identificavel obj);

}

Para não afetar nem os clientes nem os próprios DAOs, a solução seriacolocar um Proxy encapsulando o DAO original para adicionar a funcionali-dade de cache. A seguir é apresentada a implementação da classe CacheDAO.Ele possui um mapa que é utilizado para armazenar os objetos persistidos apartir do seu identi�cador. Os métodos salvar() e excluir() respecti-vamente adicionam e removem os objetos do mapa, mas apenas após delegara chamada à classe que está encapsulando.

Listagem �.� - Interface que de�ne os serviços de um trecho aéreo:

public class CacheDAO implements DAO {

private DAO dao;private Map<Integer,Identificavel> cache;

public CacheDAO(DAO dao) {this.dao = dao;this.cache = new HashMap<>();

}public Identificavel recuperar(int id) {

if(cache.containsKey(id))return cache.get(id);

Identificavel obj = dao.recuperar(id);cache.put(id, obj);return obj;

}public void excluir(int id) {

dao.excluir(id);cache.remove(id);

}public void salvar(Identificavel obj) {

dao.salvar(obj);cache.put(obj.getId(), obj);

}}

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 145: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Envolvendo objetos

Dessa forma, o método recuperar() primeiro veri�ca se o objeto estáno cache e em caso positivo o retorna sem invocar o DAO encapsulado. Casoo objeto não esteja no cache, o DAO é então invocado e o objeto encapsuladoé armazenado no cache antes de ser retornado. Observe que nesse exemploo Proxy pode, em alguns casos, retornar para o cliente sem nem invocar ométodo do objeto encapsulado.

Vale ressaltar que o exemplo de cache apresentando é bem simples e nãoestá preparado para lidar com uma série de questões importantes. Para exem-pli�car podem ser citadas a sincronização para diversos acessos paralelos, aexpiração devido à possibilidade de atualização dos dados por outras origense a distribuição no caso de aplicações em que os dados são acessados de váriasmáquinas. Caso em sua aplicação seja necessário um cache mais elaborado,sugere-se a utilização de padrões destinados à criação de cache para acesso adados [��]. Porém, quanto mais complexo ele precisar ser, mais adequada é autilização de um Proxy para isolar essa complexidade do resto da aplicação.Frameworks como o Hibernate já trabalham dessa forma e até possibilitam aescolha da sua biblioteca de cache, como o EhCache e o In�nispan.

Tentativas repetidas de acesso

Outra situação que não é incomum de acontecer é ser necessário adicio-nar uma funcionalidade em uma classe a qual não podemos modi�car. Issonormalmente acontece quando essa classe faz parte de um framework oubiblioteca de classes que está sendo utilizada. Considere a interface represen-tada a seguir como a implementada por uma classe disponibilizada por umaempresa para buscar um arquivo em seus servidores remotos. Porém, devidoa instabilidade da rede, existem muitas falhas ao tentar recuperar o arquivo.

Listagem �.� - Interface que de�ne os serviços de um trecho aéreo:

public interface AcessoRemoto {public byte[] buscarArquivo(String url) throws IOException;

}

Para diminuir o número de falhas, uma alternativa é realizar tenta-tivas repetidas de acesso ao arquivo. Como a classe não pode ser mo-

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 146: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Exemplos de Proxies Casa do Código

di�cada e não é desejável modi�car as classes que buscam o arquivo,uma alternativa é a criação de um Proxy que tente buscar o arquivonovamente no caso de uma falha. Na listagem a seguir é apresentadaa classe TentativasRepetidas que encapsula uma instância do tipoAcessoRemoto. O método buscarArquivo() invoca o mesmo mé-todo no objeto que está sendo encapsulado, porém dentro de um blocotry/catch onde um possível erro é capturado incrementando o númerode tentativas. Sendo assim, o método é invocado novamente até que nenhumerro seja retornado ou que o número limite de tentativas seja atingido.

Listagem �.� - Interface que de�ne os serviços de um trecho aéreo:

public class TentativasRepetidas implements AcessoRemoto{

private AcessoRemoto ar;private int maximoTentativas;

public TentativasRepetidas(AcessoRemoto ar,int maximoTentativas) {

this.ar = ar;this.maximoTentativas = maximoTentativas;

}public byte[] buscarArquivo(String url) throws IOException{

int tentativas = 0;IOException ultimoErro = null;while(tentativas < maximoTentativas){

try{return ar.buscarArquivo(url);

}catch(IOException ex){tentativas++;ultimoErro = ex;

}}throw ultimoErro;

}}

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 147: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Envolvendo objetos

�.� E�������� �� P����Normalmente os padrões Proxy e Decorator são aplicados quando umafuncionalidade precisa ser adicionada e deseja-se causar um impactomínimono código que já foi implementado. Nesse caso não é necessário refatorar aaplicação para a implementação do padrão. A necessidade de refatoração nadireção de um Proxy pode ocorrer quando uma classe está inchada commuitas funcionalidades e deseja-se dividir essas responsabilidades. A refato-ração para esse cenário seria extrair um Proxy com uma funcionalidade nãoligada diretamente ao objetivo principal da classe. Esse procedimento poderiaser realizado diversas vezes para diferentes funcionalidades.

Um outro caso no qual essa refatoração seria necessária é quando existeduplicação em uma parte do código entre implementações de uma mesmaabstração. Isso também pode ser motivado quando se perceber que em umanova implementação será necessária parte da funcionalidade já presente emuma outra classe. Como exemplo, outro dia criei uma classe em que havia umcódigo interno que validava se a ordem de invocação dos métodos estava cor-reta, porém quando fui criar uma nova implementação percebi que a mesmavalidação seria necessária. Dessa forma, extraí um Proxy com a parte de va-lidação da primeira classe e passei a utilizá-lo com todas as implementações.

A �gura �.� apresenta os passos da refatoração de extração de um Proxy

de uma classe existente. O primeiro passo é criar a classe que representaráo Proxy, delegando todas as chamadas dos métodos para a instância queestá sendo encapsulada. Nesse ponto é importante fazer com que os testesnão atuem isoladamente na classe, mas nela encapsulada pelo Proxy recém-criado. Inicialmente os testes deverão passar sem problemas, pois nenhumcódigo foi adicionado na classe intermediária.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 148: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Extraindo um Proxy Casa do Código

Fig. �.�: Refatoração de extração de Proxy

Depois de possuir a suíte de testes atuando em cima da classe em conjuntocom o Proxy, é hora de começar a migrar funcionalidade da classe encap-sulada. A principal regra é que o comportamento conjunto deve permanecerinalterado. Dessa forma, a parte relativa a cada umdosmétodos pode ir sendomovida para o intermediário, até que toda funcionalidade desejada esteja noProxy. Depois disso, se o desenvolvedor achar necessário, ele pode separaros testes da classe dos testes relativos à funcionalidade do Proxy, que devemfuncionar com qualquer outra implementação encapsulada.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 149: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Envolvendo objetos

C������ �� P���� �� E������

A criação da versão inicial do Proxy, que simplesmente delega osmétodos para classe encapsulada, pode ser bem trabalhosa e braçal sea interface alvo possuir um número grande de métodos. Felizmente, oEclipse possui uma funcionalidade de geração de código que cria auto-maticamente os métodos que delegam a chamada para os métodos deum atributo.

Para utilizá-la, primeiramente deve-se criar uma classe e incluir umatributo do tipo que será encapsulado. Não caia na tentação de adicionara interface juntamente com seus métodos. Em seguida, clique com o bo-tão direito sobre o código e escolha Source > Generate Delegate Methods.Nesse momento irá abrir uma janela apresentando quais métodos dosatributos você deseja criar e delegar para ele a chamada. É só escolhertodos os métodos e em seguida declarar que a classe implementa a in-terface alvo. A �gura �.� mostra a caixa de diálogo aberta durante esseprocesso.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 150: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Adaptando interfaces Casa do Código

Fig. �.�: Automatizando a delegação de métodos no Eclipse

�.� A�������� ����������“A incapacidade de se adaptar traz a destruição.”– Bruce Lee

Um outro cenário onde existe a necessidade de criar uma classe que en-volva outra é quando possuímos uma classe que implementa uma interface,mas precisamos que ela implemente outra. Um exemplo do mundo físicoocorre quando compramos um notebook nos Estados Unidos e precisamosligá-lo aqui nas tomadas do Brasil. No caso, o notebook americano possuiuma fonte que possui a interface das tomadas americanas, porém precisamosque ele possua a que permita que ele seja ligado aqui no Brasil. Todos sabemque esse problema não é difícil de resolver, bastando um adaptador que en-caixe em uma entrada no padrão brasileiro e possua uma entrada no padrãoamericano. A �gura �.�mostra de forma visual a questão do adaptador.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 151: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Envolvendo objetos

Fig. �.�: Representação do adaptador de tomadas

A ideia do padrão Adapter não é muito diferente de um adaptador detomadas. Criamos uma classe que implementa a interface necessária, a qualprecisamos nos adaptar, a “tomada nova”. Internamente essa classe é com-posta por uma referência a um objeto que implementa a outra interface, a“tomada antiga”. Dessa forma, o Adapter traduz as chamadas da interfaceque ele implementa para a classe que ele encapsula. Isso nem sempre é trivial,pois normalmente não é somente o nome do método que muda, mas outrasquestões muitas vezes inesperadas.

Estrutura do padrão Adapter

A estrutura do padrão Adapter é similar à dos padrões Proxy eDecorator, sendo que a principal diferença é que a classe que está sendoencapsulada não possui a mesma interface da que a está envolvendo. É jus-tamente esse fato que permite a adaptação entre as interfaces. A �gura �.�mostra em um diagrama os principais participantes desse padrão.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 152: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Adaptando interfaces Casa do Código

Fig. �.�: Estrutura do padrão Adapter

Nesse diagrama temos uma classe Cliente que depende de algumaforma de uma InterfaceAlvo. Sendo assim, para tornar possível elaacessar uma classe Adaptada que não implementa essa abstração, ela pre-cisa utilizar a classe Adaptador, que irá receber chamadas de método con-forme a InterfaceAlvo e irá chamar osmétodos correspondentes na classeAdaptada.

É importante �car claro que nem sempre a adaptação é simples de serfeita. Questões comuns que precisam ser traduzidas incluem a nomenclaturade classes e métodos, além da conversão de parâmetros e retornos. Questõesmais complexas podem envolver diferentes formas de interagir comas classes.Por exemplo, o que é um parâmetro de método em uma pode ser um atributopassado no construtor na outra, ou ainda, o que é o retorno em uma pode serum atributo populado em um parâmetro passado para o método na outra.Porém, a maior di�culdade é quando existem diferenças semânticas entre asdiferentes APIs. Nesse caso, normalmente é necessária a ajuda de especialistasdo domínio para compreender as diferenças e possibilitar a tradução.

Exemplo de adaptador

Imagine uma aplicação que interaja com o serviço de SMS de operadoras

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 153: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Envolvendo objetos

de celular. Considere que a versão inicial da aplicação possua uma interfacepara interagir com o serviço de apenas uma operadora. A listagem a seguirapresenta a interface SMSSender que é utilizada pela aplicação para o enviode mensagens. Essa classe possui o método sendSMS() que recebe comoparâmetro uma instância da classe SMS, também apresentada na listagem, eretorna um valor booleano dizendo se a mensagem foi enviada com sucessoou não.

Listagem �.� - Interface usada pelo sistema para envio de SMS:

public interface SMSSender {public boolean sendSMS(SMS sms);

}

public class SMS {private String destino;private String origem;private String texto;//getters e setters omitidos

}

Ao evoluir a aplicação, foi necessário incorporar o serviço de envio deSMS de uma outra operadora. Não foi uma surpresa muito grande quandofoi constatado que a API para o acesso a funcionalidade era completamentediferente. A listagem com a interface dessa nova API, EnviadorSMS, estárepresentada a seguir. A primeira diferença está nos parâmetros, que emvez de serem encapsulados em uma classe, no caso a SMS, são passadodiretamente para o método. Outra diferença é que o texto da mensagem nãoé mais uma string, mas um array de strings. A primeira API recebia um textolongo e dividia em várias mensagens se necessário, porém nessa outra API amensagem precisa ser divida em trechos de ��� caracteres antes da chamadado método. Finalmente, a nova API lança uma exceção para indicar umafalha, e não retorna um valor booleano para indicar o sucesso ou não.

Listagem �.� - Interface usada pelo sistema para envio de SMS:

public interface EnviadorSMS {

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 154: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Adaptando interfaces Casa do Código

public void enviarSMS(String destino, String origem,String[] msgs)

throws SMSException;}

Para evitar que as classes cliente precisem ser alteradas e se acoplarem auma nova API, decidiu-se criar um Adapter para traduzir as chamadas deuma interface para outra. Para criar esse adaptador é preciso lidar com todasas questões relativas às diferenças entre as interfaces. A implementação doadaptador está representada na listagem a seguir.

Listagem �.� - Interface usada pelo sistema para Envio de SMS:

public class SMSAdapter implements SMSSender {

private EnviadorSMS env;

public SMSAdapter(EnviadorSMS env) {this.env = env;

}public boolean sendSMS(SMS sms) {

String dest = sms.getDestino();String orig = sms.getOrigem();String[] txts = quebrarMsgs(sms.getTexto());try {

env.enviarSMS(dest, orig, txts);} catch (SMSException e) {

return false;}return true;

}private String[] quebrarMsgs(String text){

int size = text.length();int qtd = (size%160==0)? size/160: size/160+1;String[] msgs = new String[qtd];for(int i=0; i<qtd; i++){

int min = i*160;int max = (i==qtd-1)? size: (i+1)*160;msgs[i] = text.substring(min,max);

}

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 155: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Envolvendo objetos

return msgs;}

}

O método sendSMS() inicialmente recupera as informações do ob-jeto SMS e as atribui a variáveis locais. O método quebrarMsgs() é cha-mado para quebrar o texto da mensagem que está em apenas uma stringem um array strings com tamanho máximo de ��� caracteres. Para lidarcom a diferença do retorno, a chamada ao método enviarSMS() da classeEnviadorSMS é encapsulada em um bloco try/catch. Dessa forma, casoa exceção for capturada o método retorna false, e caso o método siga seu�uxo normal, é retornado true.

Através desse exemplo, é possível observar como existem vários fatoresque devem ser levados em consideração na hora da adaptação. Muitas vezespode-se perder funcionalidade e informações. Em um caso oposto, podemser fornecidas informações incompletas ou haver funcionalidades da inter-face que não podem ser executadas. Na classe SMSAdapter, por exemplo,para transformar a exceção recebida em um retorno booleano, perdeu-se amensagem com a causa do erro e outras informações associadas a ela.

A�������� E�������

Quando a exceção lançada por um método precisa ser transformadana exceção lançada pela interface da classe adaptadora, a solução maiscomum é realizar a chamada dométodo encapsulada em um bloco try,e lançar a nova exceção dentro do bloco catch. Caso ela seja criadasem relação com a anterior, o erro lançado possuirá em seu stacktrace ométodo do Adapter como origem, omitindo a real causa do erro. O se-gredo nesse caso é não esquecer de passar a exceção original no constru-tor da nova, dessa forma o stacktrace anterior será incluído como causado erro.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 156: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Considerações �nais do capítulo Casa do Código

Adaptadores para migração de APIs

Um outro cenário onde o padrão Adapter costuma ser bastante utili-zado é para dar suporte a classes que utilizaramuma versão antiga de umaAPIem uma versão nova do so�ware. Imagine, por exemplo, que em uma aplica-ção fossem criadas classes que implementassem a interface de um frameworkpara serem utilizadas por ele. Caso o framework modi�que essa interface emsua próxima versão, a aplicação não poderá utilizar as classes já criadas. Issoé um grande problema, visto que a aplicação pode nem compilar com a novaversão do framework.

Como uma forma de contornar esse problema, o framework pode proverum adaptador que adapte as classes desenvolvidas na versão anterior da in-terface para a versão nova. Dessa forma, as classes da aplicação podem usaresse adaptador para continuar utilizando as classes já desenvolvidas.

Como um exemplo disso, a JDK �.� possuía a interface Enumeration,com os métodos hasMoreElements() e nextElement(), e era imple-mentada por classes que desejavam permitir a iteração de seus elementos. NaJDK �.� foi introduzida a interface Iterator, com omesmo objetivo, porémcom nomes demétodosmenores, hasNext() e next(), e com ummétodoopcional remove().

Para permitir que códigos que trabalhem comuma interface possam lidarcom classes que sabem trabalhar com a outra, a biblioteca de classes ApacheCommons Collections provê a classe IteratorUtils. Ela possui os mé-todos estáticos asIterator(), que adapta um Enumeration para umIterator, e asEnumeration(), que faz a adaptação inversa. Dessa forma,se um código que utiliza Enumeration precisa utilizar uma classe que im-plementa Iterator, ele pode utilizar o IteratorUtils para fazer essaadaptação.

�.� C������������ ������ �� ��������Este capítulo abordou padrões que de�nem classes que envolvem objetos, ser-vindo como intermediárias entre a que fornece a funcionalidade e a cliente.Comouso do encapsulamento e do polimor�smo, é possível tornar essa inter-mediária transparente, permitindo facilmente a adição de uma nova camada

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 157: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Envolvendo objetos

entre elas. Esse recurso poderoso permite a utilização de diversos artifíciosescondidos sob a máscara da interface da qual a classe cliente depende.

Na primeira parte do capítulo, foram abordados os padrões Proxy eDecorator, nos quais essa classe intermediária possui a mesma abstraçãoda que a está envolvendo. Dessa forma, ela pode adicionar novas funciona-lidades e se passar pela própria classe. Já na segunda parte, foi abordado opadrão Adapter no qual a intermediária é utilizada para encapsular umaclasse com uma interface diferente da sua. Nesse caso, a ideia é justamentepermitir que uma classe com uma determinada interface possa ser utilizadapor outra que sabe interagir com classes com interface diferente.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 158: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 159: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

C������� �

Estratégias de criação de objetos

“A verdadeira felicidade vem da alegria de atos bem feitos, o entusiasmo decriar coisas novas.”– Antoine de Saint-Exupery

Nos capítulos anteriores, foram apresentados diversos padrões que mon-tamestruturas utilizadas para fornecer�exibilidade e extensibilidade nas clas-ses da aplicação. Quando utilizamos herança, devemos escolher entre di-ferentes implementações de uma abstração para que ela tenha o comporta-mento adequado. Com composição, outros objetos devem ser instanciadose inseridos na classe principal. Com a composição recursiva, diversas ins-tâncias de implementações de uma abstração são combinadas em uma estru-tura que fornece o comportamento desejado. Finalmente, para a utilização deProxies, é preciso envolver o objeto e colocá-lo no lugar do encapsulado.

Para os padrões funcionarem é preciso que essas estruturas estejammon-

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 160: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Limitações dos construtores Casa do Código

tadas, porém saber como criá-las de forma adequada não é tão simples. Porexemplo, de nada adianta a classe utilizar as abstrações para se desacoplar dasimplementações, se na hora de criar os objetos ela referencia diretamente asclasses. Os objetos devem estar desacoplados também no momento da cria-ção! Não somente a utilização de uma estrutura de herança ou composiçãodeve ser transparente à classe cliente, mas também a criação dessa estrutura.Para que realmente exista �exibilidade, deve ser possível criar as diferentesimplementações ou combinações de classe para que o comportamento possaser alterado.

Esse capítulo irá abordar os padrões para a criação de objetos. Eles mos-tram técnicas para o desacoplamento da lógica de criação de objetos, princi-palmente quando se precisa utilizar um dos padrões apresentados nos capí-tulos anteriores. Também serão apresentadas soluções para encapsular umalógica complexa de criação e para vincular a criação de objetos relacionados.A próxima seção inicia o capítulomostrando por que os construtores, a formatradicional de criar objetos, podem não ser su�cientes para encapsular essalógica de criação.

�.� L��������� ��� ������������Se você perguntar para qualquer desenvolvedor Java como se cria um ob-jeto, ele irá prontamente responder que é através de construtores. Apesar derealmente não ser possível criar uma nova instância de uma classe sem a in-vocação de um construtor, sua aplicação não precisa interagir diretamentecom eles para criar os objetos de que precisa. Principalmente quando se de-seja desacoplar a classe cliente da que está sendo criada. Se o construtor fordiretamente invocado, o código da classe cliente precisará ser mudado parasubstituí-la por uma nova implementação.

Esta seçãomostra algumas limitações dos construtores que di�cultam suautilização para a criação de objetos. Algumas dessas questões são abordadasno famoso livro E�ective Java de Joshua Bloch [�].

Dois construtores não podem ter parâmetros de mesmo tipo

Uma característica dos construtores é que eles possuem o mesmo nome

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 161: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Estratégias de criação de objetos

da classe que criam. Isso não é um problema se ela possui um processo de cri-ação simples, fornecendo um ou dois construtores para sua criação. Porém,quando existem diversas formas de criar objetos de uma mesma classe, issopode ser um problema. Para começar, toda a expressividade que é possívelatravés da utilização de nomes descritivos para métodos não pode ser utili-zada nos construtores, visto que eles precisam ter o mesmo nome da classe.Isso di�culta bastante quando uma classe possui diversos construtores e nãose sabe exatamente qual o comportamento que será fornecido por cada umdeles.

Essa questão �ca aindamais crítica quando é necessária a criação de cons-trutores que possuemparâmetros demesmo tipo. Como isso não é possível nalinguagem Java, é preciso utilizar artifícios para diferenciar os construtores.Por exemplo, adicionar umparâmetro diferente amais para diferenciá-los, ouainda criar apenas um construtor com um parâmetro dizendo qual o tipo decriação que se deseja.

Para �car mais concreta essa situação, imagine que uma classe chamadaCoordenadaGeografica possa receber strings com representações textu-ais das coordenadas em seu construtor. Como existem diversos formatos paracoordenada, como oGeodésico (Lat ���°��.����’ S ; Long ���°��.����’ W) e oGeodésicoDecimal (Lat -��.���������� ; Long -��.����������), a intenção se-ria ter construtores separados para cada tipo de formato. Infelizmente, comoambos construtores teriam o mesmo nome e receberiam uma string comoparâmetro, essa solução não seria possível de ser implementada.

Um construtor sempre cria um novo objeto

Uma outra característica de um construtor é que toda vez que ele é cha-mado ele retorna umnovo objeto da classe. Certamente é issomesmo que umconstrutor deveria fazer, porémmuitas vezes deseja-se utilizar um objeto quejá existe e não gerar um novo. Se esse for o caso, �ca inviabilizada a criaçãodesses objetos através de construtores.

Objetos que não contenham estado, ou seja, contenham basicamente mé-todos com lógica de negócio, são bons candidatos para terem um número li-mitado de instâncias. Outras vezes, deseja-semanter umaúnica entidade pararepresentar uma determinada entidade em todo sistema. Imagine que em um

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 162: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Limitações dos construtores Casa do Código

sistema de locadora de carros deseja-se ter uma única instância para repre-sentar a instância de TipoCarro que representa um Corsa. Dessa forma,quando for necessário representar um Corsa em vez de se criar uma nova ins-tância, a já existente deve ser retornada. Nada disso pode ser feito utilizandoconstrutores diretamente.

Um construtor só pode retornar objetos da mesma classe

Talvez o principal problema dos construtores seja retornar objetos apenasda classe que de�niu aquele construtor. Não é possível, por exemplo, retornaruma instância de uma subclasse ou envolver o objeto em um Proxy. Dessaforma, a classe que invoca o construtor de uma outra se acopla a ela forte-mente. Por mais que ela utilize a abstração em outras partes do código, seránecessário uma alteração no código de criação caso uma outra classe preciseser utilizada.

Para entender como isso pode ser um problema, imagine uma classe queé utilizada em vários pontos do sistema. Suponha agora que ela possuísse umgrande método que fosse refatorado para a utilização do padrão Template

Method, no qual subclasses implementariampartes do algoritmo. Uma situa-ção similar seria se fosse criado um Decorator para adicionar alguma novafuncionalidade a essa classe. Se ela fosse utilizada em vários pontos diferentesdo sistema, o código de criação deveria ser modi�cado em todos esses pontospara que a subclasse adequada fosse criada ou que o Decorator fosse criadopara encapsular a instância.

Sendo assim, é possível perceber que a utilização direta de construtorespode di�cultar bastante o uso dos padrões que foram vistos até o momentonesse livro. A lógica de criação pode se complicar principalmente quandoalguns padrões são combinados e surgemquestões como: escolher a subclassecorreta para instanciar; escolher as implementações para compor a instância;e adicionar funcionalidades envolvendo o objeto em Proxies. As próximasseções apresentam alguns padrões que podem ser utilizados para eliminaressas limitações e encapsular uma lógica complexa de criação de objetos.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 163: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Estratégias de criação de objetos

�.� C������ ������� ��� ������� ���������“Se permaneces estático na derrota, mova-se rumo à vitória.”– Edilson Sanches Pontes

A soluçãomais simples para eliminar a necessidade da utilização de cons-trutores diretamente é a criação de métodos estáticos para criarem os obje-tos. Essa prática é conhecida como Static Factory Method. Apesar deser considerado um padrão, essa prática não foi devidamente documentadacomo um padrão e é abordada com mais detalhes no E�ective Java [�].

Nesse padrão, as classes clientes delegam para os métodos estáticos a ló-gica de criação das instâncias. Esses métodos recebem os parâmetros neces-sários e estes retornam a instância adequada. Como esses métodos de criaçãonão possuem as restrições dos construtores, eles podem resolver as limitaçõesdescritas na seção anterior.

Um dos primeiros benefícios da utilização do Static Factory

Method está na expressividade, pois é possível nomear o métodode acordo com a lógica de criação envolvida. Isso acaba resol-vendo o problema dos construtores com tipos de parâmetro repeti-dos, pois é só criar nomes diferentes para eles. No exemplo daclasse CoordenadaGeografica citado anteriormente, seria possível pos-suir métodos de criação chamados criarCoordenadaGeodesico() ecriarCoordenadaGeodesicoDecimal(). Dessa forma, o código cliente�caria inclusive mais claro em relação à estratégia de criação que está sendoutilizada.

Outra possibilidade interessante é a de retornar um objeto já existente.Esse objeto normalmente é guardado na primeira vez que ele é criado e de-pois é aproveitado para as chamadas seguintes. Ele é armazenado em umavariável estática, ou em alguma coleção também em uma variável estática.Um exemplo clássico disso é a obtenção de conexões para um banco de da-dos. A classe DriverManager, por exemplo, disponibiliza ométodo estáticogetConnection() em que uma conexão é retornada. Algumas implemen-tações criam um pool de conexões, onde as conexões são reaproveitada depoisde serem utilizadas.

Um outro ponto interessante do Static Factory Method é que ele

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 164: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Criando objetos com métodos estáticos Casa do Código

pode retornar qualquer implementação. Dessa forma, é possível introduzirnovas subclasses e retorná-las nesse método de forma transparente à classecliente. Assim, a classe cliente depende realmente apenas do tipo retornadopelo método fábrica e não das implementações retornadas. Isso simpli�camuito refatorações que criam novas classes de uma abstração, como a criaçãode Template Method e de um Proxy, pois ao alterar o Static Factory

Method, todas as que o utilizam para a criação poderão receber uma novaimplementação de forma transparente.

S����� F������M����� X F������M�����

Antes de ir fundo nas características do Static Factory Method

é importante deixar bem claro que ele é diferente do padrão Factory

Method apresentado no capítulo �. O padrão que está sendo descritonessa seção consiste em um método estático utilizado para encapsulara criação e/ou obtenção de instâncias por parte da classe cliente. Já oFactory Method é um hook method de�nido por uma classe para de-legar a criação de uma instância para suas subclasses.

Exemplos de Static Factory Method

Para exempli�car a utilização demétodos fábrica, vamos retomar o exem-plo do gerador de arquivo apresentado nos capítulos anteriores (a versão queutiliza o padrão Composite). A listagem a seguir apresenta o código de umaclasse que disponibiliza Static Factory Methodspara a criação de gera-dores de arquivo. Nessa implementação optou-se por disponibilizar métodosdiferentes para a criação dos geradores de arquivos em XML e de proprieda-des. Outra alternativa seria a passagem de um parâmetro que identi�caria oformato do arquivo.

Listagem �.� - Fábrica de GeradorArquivo utilizando Static FactoryMethod:

public abstract class FabricaGerador {

public static final String ZIP = "ZIP";

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 165: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Estratégias de criação de objetos

public static final String CRYPTO = "CRYPTO";

public static GeradorArquivo criarGeradorXML(String... processadores) {

GeradorArquivo g = new GeradorXML();g.setProcessador(criarProcessador(processadores));return g;

}public static GeradorArquivo criarGeradorPropriedades

(String... processadores) {GeradorArquivo g = new GeradorPropriedades();g.setProcessador(criarProcessador(processadores));return g;

}private static PosProcessador criarProcessador

(String... processadores) {if(processadores.length > 1) {

PosProcessadorComposto pp =new PosProcessadorComposto();

for(String proc : processadores){pp.add(criarProcessador(proc));

}return pp;

} else if(processadores[0].equals(ZIP)){return new Compactador();

} else if(processadores[0].equals(CRYPTO)){return new Criptografador();

}}

}

Para os processadores, é passado um array de strings que identi�cam ospós-processadores em sua respectiva ordem. Ambos os métodos fábrica de-legam a criação deles para o método criarProcessador(). Observe quecaso exista mais de um, a classe PosProcessadorComposto que imple-menta o padrão Composite é utilizada.

O código a seguir mostra como a classe FabricaGerador seriautilizada na criação de um GeradorArquivo. Um dos métodos estáticos,

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 166: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Um único objeto da classe com Singleton Casa do Código

como criarGeradorXML() ou criarGeradorPropriedades(), deveser invocado passando como parâmetro de forma opcional as strings querepresentam os pós-processadores. Note que a classe tem contato apenascom a FabricaGerador e a abstração GeradorArquivo, sendo que ouso de todas as subclasses e pós-processadores �cam encapsulados dentro dafábrica.

Listagem �.� - Exemplo de criação do GeradorArquivo:

GeradorArquivo ga = FabricaGerador.criarGeradorXML(FabricaGerador.ZIP,FabricaGerador.CRYPTO);

A própria API do Java utiliza esse padrão em diversos pontos para encap-sular a criação de objetos. Umexemplo comumsão osmétodos parseInt()e valueOf() da classe Integer. Além de seremmétodos estáticos que re-tornam instâncias de Integer, eles fazem cache para que amesma instânciaseja retornada caso o mesmo número seja passado. Isso ocorre igualmentecom métodos similares em outras classes que encapsulam tipos primitivos.

Impedindo de invocar o construtor

Disponibilizar um Static Factory Method não impede as classesclientes de invocarem o construtor e ignorar toda a lógica de criação encap-sulada. Para resolver essa questão, o segredo é fazer com que o construtortenha uma visibilidade que dê acesso ao método fábrica e não dê acesso àsclasses clientes. Se o método estiver na mesma classe que está sendo criada,o construtor pode ser declarado como privado. Se o método fábrica estiverem uma classe diferente, então uma solução é declará-lo como protegido ecolocar essa classe fábrica no mesmo pacote que as que são criadas por ele.

�.� U� ����� ������ �� ������ ��� S��������“Só pode haver um!”– Connor MacLeod, Highlander

Um caso especial da utilização do Static Factory Method é quandose deseja que a aplicação possua apenas uma instância de uma determinada

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 167: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Estratégias de criação de objetos

classe. Isso normalmente é motivado por questões de negócio, em que fazsentido apenas um objeto daquele tipo. Por exemplo, imagine um so�wareque represente um jogo de xadrez entre duas pessoas. Nesse contexto prova-velmente fará sentido apenas um tabuleiro de jogo. A estrutura para permitiresse tipo de construção é o padrão Singleton.

A listagem a seguir apresenta um exemplo de implementação do padrãoSingleton. No exemplo, o Singleton é utilizado em uma classe querepresenta e armazena con�gurações do sistema. Esse é um exemplo emque o uso desse padrão é adequado, pois só existe uma única con�guraçãopara todo o sistema, e dessa forma ela pode ser facilmente obtida a partir dequalquer classe.

Listagem �.� - Exemplo de Singleton:

public class Configuracao {

private static Configuracao instancia;

public static Configuracao getInstancia() {if(instancia == null)

instancia = new Configuracao();return instancia;

}

// Construtor privado!private Configuracao() {

//lê as configurações}

// ...}

Nesse caso, o método fábrica é normalmente de�nido na própria classe.O artifício de de�nir um construtor privado é frequentemente utilizado paraimpedir a criação de outras instâncias da classe. Um atributo estático é de�-nido para armazenar essa instância e um método estático é disponibilizadode forma pública para sua recuperação. Observe que na listagem de exem-

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 168: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Um único objeto da classe com Singleton Casa do Código

plo o objeto é criado no primeiro acesso ao método. Uma implementaçãoalternativa, seria incluir essa criação em um bloco estático para que ela fosserealizada quando a classe fosse carregada pela máquina virtual.

A listagem a seguir mostra o exemplo de como uma classe pode obter ainstância da que implementa o padrão Singleton. Observe que o processonão é muito diferente da invocação de um Static Factory method. Emqualquer local da aplicação em que o método getInstancia() for invo-cado, o mesmo objeto será retornado.

Listagem �.� - Obtenção de um Singleton:

Configuracao c = Configuracao.getInstancia();

Uma das vantagens do Singleton é a facilidade de acesso dessa instân-cia por qualquer objeto na aplicação. De qualquer classe é possível chamar ométodo getInstancia() e obtê-la. Isso evita, por exemplo, que essa ins-tância única precise ser passada como parâmetro para diversos lugares. Noexemplo do tabuleiro de xadrez, essa provavelmente seria uma classe que pre-cisaria estar acessível de diversos locais de acordo com a lógica do jogo.

Uma solução utilizando um Singleton oferece uma �exibilidademuitomaior do que uma solução que utiliza métodos estáticos para a execução dasregras de negócio. Por ser um objeto, a instância única pode ser especializadae encapsulada por um Proxy, o que não é possível fazer com métodos está-ticos. Essa questão prejudica bastante a testabilidade de classes que acessammétodos estáticos, pois não é possível substituí-los por um objeto falso compropósitos de teste, conhecido comoMockObject [��]. Uma solução que con-cilia a praticidade dos métodos estáticos com a �exibilidade do Singleton

seria os métodos estáticos delegarem a lógica de sua execução para os méto-dos do Singleton.

O lado negro do Singleton

O padrão Singleton deve ser utilizado commuito cuidado e nas situa-ções em que realmente �zer sentido ter apenas uma instância de uma classe.Muitos acham que, por ser um padrão, ele pode ser utilizado em qualquerparte do sistema. O resultado é que o Singleton acaba sendo usado como

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 169: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Estratégias de criação de objetos

uma variável global da orientação a objetos, o que pode reduzir a �exibili-dade da aplicação deixando sua modelagem de�ciente. Por ele ser utilizadomais em situações inadequadas, muitos acabam considerando o Singleton

como umamá prática. Sendo assim, toda vez que for utilizar um Singleton

re�ita bastante se sua utilização é mesmo necessária. Também veremos, emum capítulo posterior, que inversão de controle e injeção de dependênciaspodem ajudar bastante nesse caso.

�.� E����������� ������ �������� �� ���������� B������

“Podemos construir? Sim! Podemos sim!”– Bob, o Construtor

Pelo que já foi dito sobre a criação de objetos até o momento nesse ca-pítulo, acredito que tenha sido possível perceber como a criação de determi-nados tipos pode ser complexa. Nesses casos, a lógica da criação, que muitasvezes precisa validar parâmetros ou buscar informações em arquivos, acaba semisturando com a lógica da própria classe. Sendo assim, mantê-las namesmaclasse pode deixá-la grande e confusa, tanto com a utilização de construtoresquanto com a utilização de métodos estáticos de criação.

O padrão Builder fornece uma solução para essa questão da criação deobjetos complexos, com a de�nição de uma classe responsável pelo processode criação. A partir de um Builder é possível seguir o mesmo processo decriação para diferentes estruturas e diferentes representações. Dessa forma, aclasse que invoca os métodos de um Builder para a criação do objeto queprecisa �ca desacoplada dessa lógica de criação complexa e da classe concretaque está sendo criada.

Além disso, pode ser criada uma abstração para um Builder de formaque diferentes implementações possam ser criadas. Desse modo, os clientespodem guiar a criação de diferentes objetos através do Builder, porém semsaber a implementação que está sendo utilizada e, consequentemente, qual aclasse do objeto que está sendo criado. Esse tipo de prática é muito comumemAPIs que disponibilizam interfaces que são implementadas por diferentes

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 170: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Encapsulando lógica complexa de criação com Builder Casa do Código

fornecedores. Por exemplo, imagine uma API que lida com certi�cados digi-tais. O padrão Builder pode ser utilizado para criar o objeto que assina umarquivo digitalmente, porém cada fornecedor possuirá uma implementaçãodiferente do Builder de acordo com suas classes.

A �gura �.� apresenta a estrutura do padrão Builder. No diagrama, aclasse Cliente precisa criar uma instância da abstração Produto e utilizaum Builder para essa tarefa. Observe que pela estrutura é possível ter vá-rias implementações do Builder, possibilitando que diferentes estratégiasde implementação sejam utilizadas. Porém é importante ressaltar que essaabstração do Builder só deve ser usada quando realmente for necessária.

Fig. �.�: Representação do padrão Builder

Na �gura alguns outros padrões estão representados para ilustrar a com-plexidade que o processo de criação pode possuir. Veja que a classe represen-tada como Produto pode possuir subclasses, pode ser composta por outras,pode ser envolvida por um Proxy e pode ter suas implementações combina-das por um Composite, isso entre outros padrões que podem ser utilizados.Nenhum desses outros padrões citados são obrigatórios para a utilização deum Builder, porém sua presença aumenta a complexidade do processo de

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 171: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Estratégias de criação de objetos

criação favorecendo a utilização de um padrão de criação de objetos.

Criando o SessionFactory do Hibernate com um Builder

Para melhorar a compreensão do padrão Builder essa seção apresentaum exemplo do seu uso no framework Hibernate para a criação do objeto daclasse SessionFactory. O Hibernate é um framework cuja principal fun-cionalidade é realizar omapeamento objeto-relacional, ou seja, entre classes etabelas do banco de dados. Para que isso possa ser feito, é preciso que diversascon�gurações sejam lidas de arquivos e de anotações em classes, tornando acriação de um SessionFactory, a classe que gera as sessões para o acessoa base de dados, um processo bem complexo.

A listagem a seguir mostra a utilização da classeAnnotationConfiguration para a criação de um SessionFactory.Nesse contexto, AnnotationConfiguration é a implementação dopadrão Builder. Observe como diversos métodos são chamados paraadicionar con�gurações em relação a como essa classe deve ser criada.O encadeamento na chamada dos métodos se deve ao fato de cada umdeles retornar a própria instância de AnnotationConfiguration, comexceção do método buildSessionFactory() que retorna a instância deSessionFactory.

Listagem �.� - Criação de um SessionFactory do Hibernate:

SessionFactory sessionFactory = new AnnotationConfiguration().addPackage("com.lojavirtual").addAnnotatedClass(CarrinhoCompras.class).addAnnotatedClass(Cliente.class).addAnnotatedClass(Produtos.class).addResource("orm.xml").configure().buildSessionFactory();

Esse é um bom exemplo de uso do padrão Builder pois envolve real-mente um processo complexo de criação. A partir da leitura de arquivos XMLe das anotações das classesmapeadas são extraídas as informações necessáriaspara o SessionFactory. É importante ressaltar que essa lógica extrapola

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 172: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Encapsulando lógica complexa de criação com Builder Casa do Código

as responsabilidades da classe SessionFactory, fazendo todo sentido aseparação da responsabilidade da sua criação em uma classe separada.

Builder com interface �uente

O termo interface �uente foi cunhado por Martin Fowler e Erick Evans[��], como uma forma de descrever um estilo de construção de interfaces.A ideia é dar o nome dos métodos da classe de forma que o código pareçauma frase em linguagem natural. A listagem a seguir mostra o exemplode uma interface tradicional, utilizando métodos começados com get eset, e a mesma classe utilizando uma interface �uente. Com uma interface�uente é como se você estivesse de�nindo uma nova linguagem dentro dalinguagem de programação a partir dos nomes dos métodos, prática quetambém é chamada de DSL (Linguagem Especí�ca de Domínio) interna [��].

Listagem �.� - Exemplo de uso de interface �uente:

//Estilo comumPessoa p = new Pessoa();p.setNome("João");p.setDataNascimento("30/03/1985");p.setCargo("gerente");

//Interface fluentePessoa p = new Pessoa()

.chamada("João")

.nascidaEm("30/03/1985")

.comCargo("gerente");

A parte mais importante de uma interface �uente está na nomencla-tura utilizada para os métodos. Eles devem soar como se fossem partede uma frase, de forma que “uma nova pessoa chamada João” vira new

Pessoa().chamada("João"). Outra questão é que os métodos devemretornar a instância da própria classe, ou seja, this, para as chamadas demétodo possam ser encadeadas. No exemplo apresentado, as chamadas dosmétodos chamada(), nascidaEm() e comCargo() retornam a própriainstância de Pessoa.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 173: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Estratégias de criação de objetos

Muitas vezes, as aplicações precisam utilizar as interfaces no padrão JavaBeans (utilizando métodos com get e set), por requisito dos frameworksutilizados. Isso acaba inviabilizando a utilização da interface �uente nasclasses de domínio. Porém, isso não impede que elas sejam utilizadas nosBuilders, que criam essas classes. É importante mais uma vez ressaltar que,para que a utilização do Builder seja adequada, é preciso que o processo decriação do objeto em questão seja complexo e exija a con�guração de diversosparâmetros.

Um dos frameworks pioneiros na utilização de interfaces �uentes paraa criação de objetos foi o framework JMock [��]. Ele é utilizado para acriação de mock objects, que são objetos falsos utilizados para substituiras dependências de uma classe em testes de unidade. A listagem a seguirapresenta alguns trechos de código do JMock para de�nir o comportamentodo objeto falso e suas expectativas em relação às chamadas da classe testada.Observe que apesar das limitações relativas à sintaxe da linguagem Java, épossível ler o código como se fosse uma frase.

Listagem �.� - Interface �uente no JMock:

//uma chamada ao método log() com uma string contendo ’erro’one (logger).log(with(stringContaining("error")));

//exatamente 3 chamadas de count()//retornando consecutivamente 10, 20 e 30exactly(3).of(counter).count();will(onConsecutiveCalls(returnValue(10),

returnValue(20),returnValue(30)));

//permitir chamada ao método sqrt com valor menor que zero//irá lançar a exceção IllegalArgumentExceptionallowing (calculator).sqrt(with(lessThan(0)));will(throwException(new IllegalArgumentException());

Exemplos de utilização do Builder

Para exempli�car os conceitos do padrão Builder e de interface �u-ente, será utilizado como contexto o mesmo exemplo para a criação da classe

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 174: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Encapsulando lógica complexa de criação com Builder Casa do Código

GeradorArquivo mostrado com o Static Factory Method. A lis-tagem a seguir apresenta a implementação da classe BuilderGerador.Essa classe possui um atributo instancia que armazena o objeto que estásendo construído. Ao chamar um dos dois métodos gerandoEmXML() ougerandoEmPropriedades() a subclasse correta é recuperada e atribuídaao atributo. Já os métodos comCriptografia() e comCompactacao()

adicionam os processadores na instância sendo construída, utilizando oComposite quando houver mais de um. No �nal de todo processo, o mé-todo construir() deve ser chamado para retornar o objeto criado.

Listagem �.� - Builder para a classe GeradorArquivo:

public class BuilderGerador {

private GeradorArquivo instancia;

public BuilderGerador gerandoEmXML() {instancia = new GeradorXML();return this;

}public BuilderGerador gerandoEmPropriedades() {

instancia = new GeradorPropriedades();return this;

}public BuilderGerador comCriptografia() {

adicionaProcessador(new Criptografador());return this;

}public BuilderGerador comCompactacao() {

adicionaProcessador(new Compactador());return this;

}private void adicionaProcessador(PosProcessador p) {

PosProcessador atual = instancia.getProcessador();if(atual instanceof NullProcessador) {

instancia.setProcessador(p);}else{

PosProcessadorComposto pc =new PosProcessadorComposto();

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 175: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Estratégias de criação de objetos

pc.add(atual);pc.add(p);instancia.setProcessador(pc);

}}public GeradorArquivo construir() {

return instancia;}

}

Para deixar o exemplo mais interessante, vamos imaginar queexistam ainda Proxys que possam ser utilizados para encapsular aclasse GeradorArquivo. Dentro desse exemplo, imagine que a classeProxyAssincrono invoque o método de geração de arquivos em umathread diferente e que a classe LoggerProxy grave as chamadas realizadasem um arquivo de log para �ns de auditoria. A listagem a seguir mostracomo essas classes poderiam facilmente ser incorporadas no Builder.Ao ser chamado o método, o proxy encapsula a instância armazenada e oresultado é atribuído a ela mesma.

Listagem �.� - Adicionando métodos no Builder para adição de Proxies:

public class BuilderGerador {//...public BuilderGerador assincrono() {

instancia = new ProxyAssincrono(instancia);return this;

}public BuilderGerador registrandoAuditoria() {

instancia = new LoggerProxy(instancia);return this;

}}

Para ver como �ca a construção do objeto utilizando interface �uente, alistagem a seguir mostra um exemplo de código. Observe como a construçãodo objeto �ui de forma fácil entre as chamadas de método para sua con�gu-ração.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 176: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Relacionando famílias de objetos com Abstract Factory Casa do Código

Listagem �.�� - Exemplo de utilização do Builder com interface �uente:

GeradorArquivo ga = new BuilderGerador().gerandoEmXML().comCriptografia().comCompactacao().assincrono().registrandoAuditoria().construir();

�.� R����������� �������� �� ������� ��� A��-����� F������

“Família que �{algumVerbo} unida, permanece unida.”– Dito popular

Nos padrões anteriores, foram abordados problemas referentes à comple-xidade de criação dos objetos, além de seu desacoplamento da classe clientee da própria classe que está sendo criada. Um outro problema surge quandomais de um objeto relacionado precisa ser criado. Muitas vezes existem famí-lias de objetos nos quais existem classes relacionadas emhierarquias paralelas.Um exemplo é quando uma mesma API é implementada por diversos forne-cedores. Nesse caso, cada um deles fornece implementações diferentes paraas mesmas abstrações. Não faz sentido utilizar uma classe de um fornece-dor junto com outra classe de outro, pois apesar de obedecerem às mesmasabstrações elas não podem ser misturadas.

Um padrão que existe que foca na solução desse problema é o Abstract

Factory. Nesse padrão, em vez de termos uma fábrica para a criação de umobjeto, ela é destinada à criação de uma família de objetos relacionados. Dessaforma, se todos os objetos relacionados forem obtidos a partir da mesma fá-brica, não haverá inconsistência entre eles.

Um exemplo que temos na plataforma Java é a API JDBC. A classeConnection representa uma conexão com o banco de dados e tambémé utilizada para criar outros objetos utilizados para interagir com o bancode dados. Se misturarmos os objetos obtidos de conexões diferentes (oResultSet do MySQL com o Statement do PostGreSQL, por exemplo)obteremos um erro, porém o fato de existir uma instância como um pontocentral de onde todos podem ser obtidos evita que esse tipo de erro acon-teça. Nesse caso, a classe Connection faz o papel de Abstract Factory,

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 177: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Estratégias de criação de objetos

pois só vai fabricar objetos relativos ao banco de dados com o qual ela sabe secomunicar.

Estrutura do Abstract Factory

A estrutura básica do padrão Abstract Factory está apresentada nodiagrama a seguir. As classes ProdutoA e ProdutoB representam as classespertencentes a uma família. Dessa forma, a família “X” é representada pelasimplementações ProdutoAX e ProdutoBX e o similar ocorre com a família“Y”. O exemplo apresenta apenas dois tipos de produto e duas famílias, porémem uma implementação desse padrão pode haver mais de cada um deles.

A classe FabricaAbstrata é a que abstrai a criação de uma família deobjetos. Cada implementação é responsável pela criação de uma das famíliasde objeto e possui métodos para a criação de cada um dos tipos de objetoda família. Como pode ser visto no diagrama, cada família possui uma im-plementação da Abstract Factory. Por exemplo, a FabricaFamiliaX

cria instâncias das classes ProdutoAX e ProdutoBX.Existe uma troca importante que se faz quando se utiliza o Abstract

Factory. Utilizando esse padrão, émuito fácil criar e introduzir na aplicaçãouma nova família de objetos. Para isso, basta criar as implementações para asabstrações dos objetos que serão feitos e uma nova Abstract Factory

que os retorne. Por outro lado, esse padrão di�culta a criação de um novotipo de objeto pertencente a essa família. Isso exigiria a adição de ummétodode criação na Abstract Factory, exigindomodi�cações em todas as suasimplementações. Em resumo, é fácil adicionar uma nova família de objetos,porém é difícil adicionar um novo tipo de objeto na família.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 178: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Relacionando famílias de objetos com Abstract Factory Casa do Código

Fig. �.�: Estrutura básica do Abstract Factory

Criando objetos para o envio de SMS

Vamos imaginar que uma aplicação precise interagir com o sistema deSMS de diversas operadoras de telefonia móvel. Também vamos supor queexistam diversos objetos que precisem ser criados para interagir com esse ser-viço. Eles são:

• ObservadorSMS: recebe eventos relativos à chegada de mensagensdestinadas à aplicação.

• FiltroSMS: con�gura um �ltro que restringe as mensagens que de-vem ser recebidas ou tratadas por um determinado objeto.

• EnviadorSMS: responsável por enviar SMS para dispositivos móveis.

• RespondedorAutomatico: con�gura respostas automáticas para de-terminados tipos de mensagem.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 179: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Estratégias de criação de objetos

Dependendo da operadora, alguns serviços, como a resposta automáticaou o �ltro para o recebimento de mensagens, pode ser implementados dire-tamente no servidor ou pela API cliente. Por esse motivo, não é possível, porexemplo, utilizar um �ltro de uma operadora para �ltrar as mensagens rece-bidas por um observador de outra operadora. O mesmo vale para a classeRespondendorAutomatico que também pode ser criada a partir de um�ltro.

Dessa forma, o padrão Abstract Factory pode ser utilizado paraconcentrar a criação desses objetos em uma única classe. Esse objeto nãoprecisa ser necessariamente apenas destinado a isso, podendo representar al-gum conceito do sistema. Nesse caso, a classe que irá criar os objetos pode sera representação de uma conexão do sistema com os servidores da operadora.A seguir está representada a listagem que representa a abstração dessa classe.

Listagem �.�� - Exemplo de utilização do Builder com interface �uente:

public interface ConexaoOperadora{public FiltroSMS criarFiltro(String expressao);public EnviadorSMS criarEnviador();public ObservadorSMS criarObservador();public ObservadorSMS criarObservadorComFiltro(FiltroSMS f);public RespondendorAutomatico

criarRespostaAutomatica(FiltroSMS f);//outros métodos referentes a conexão

}

Pode-se observar pela listagem que existemmétodos de criação que rece-bem uma instância de FiltroSMS como parâmetro. Caso um objeto criadoa partir de uma operadora diferente fosse passado como parâmetro, um erroseria lançado, por mais que a abstração fosse aceita pelo tipo do parâmetro. Aconcentração desses métodos de criação na mesma classe ajuda a evitar queesse tipo de mistura de objetos ocorra.

O fato da Abstract Factory ser de�nida a partir de uma abstraçãopermite que os clientes possam interagir de forma transparente com diferen-tes operadoras. Essa abstração também permite que cada classe tenha suasparticularidades, como por exemplo, os parâmetros que são necessários paraque a conexão possa ser estabelecida.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 180: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Considerações �nais do capítulo Casa do Código

�.� C������������ ������ �� ��������Esse capítulo focou em padrões para a criação de objetos. O padrão Static

Factory Method encapsula a criação de objetos permitindo que sejam re-tornadas instâncias de subclasses, instâncias já existentes, e que se tenha umaexpressividade maior a partir de nomes mais signi�cativos. Foi visto tam-bém o padrão Singleton como um caso especial do Static Factory

Method para o cenário em que deve haver apenas uma instância de uma de-terminada classe.

Para casos mais complexos, em que diversos parâmetros podem ser utili-zados para construção do objeto, o Builder é um padrão adequado. Tam-bém foi mostrado como ele pode ser útil quando outros padrões que deixama estrutura dos objetos mais complicada, como Proxy, Template Method

e Composite, são empregados nas classes que precisam ser criadas. Final-mente, foi abordado o Abstract Factory, que foca em cenários onde fa-mílias de objetos relacionados precisam ser criadas.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 181: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

C������� �

Modularidade

“Nosso objetivo de�nitivo é a programação extensível. Por isso queremos dizera construção de uma hierarquia de módulos, cada um adicionando novafuncionalidade ao sistema.”– Niklaus Wirth

No capítulo anterior foram apresentados diversos padrões para a criaçãode objetos. Esses padrões, de uma forma geral, desacoplam a classe clienteda sua criação. Isso permite que essa classe dependa apenas da sua abstra-ção, possibilitando que novas implementações sejam criadas e incorporadasà classe responsável pela criação. Apesar disso, a cliente acaba dependendoindiretamente da que está sendo criada. Por exemplo, ela pode depender deum Builder, que por sua vez depende das implementações. O mesmo valepara os outros padrões!

Essa dependência, mesmo que indireta, impede que a classe cliente e as

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 182: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Fábrica dinâmica de objetos Casa do Código

implementações sejam divididas em módulos independentes. No exemplocitado do Builder, a classe cliente precisa ter acesso a ele, o qual precisa teracesso às implementações. Com essa estrutura não é possível, por exemplo,inserir dinamicamente novas implementações, permitindo que elas sejamuti-lizadas pela cliente, pois para isso a classe responsável pela criação, como oBuilder, também precisaria de ser modi�cada.

A modularidade é cada vez mais um requisito não funcional importantenas aplicações. Porémmodularidade não é apenas dividir o so�ware emmó-dulos que formam um único bloco, mas permitir que novos módulos possamser criados e incorporados no so�ware sem a necessidade de suamodi�cação.Um exemplo de modularidade é o IDE Eclipse, ao qual novos plugins podemser facilmente e dinamicamente incorporados.

Para que esse tipo de modularidade possa ser atingido, é necessário nãosomente a utilização de interfaces e abstrações para a interação entre os ob-jetos, mas também um desacoplamento no momento de sua criação. Dessaforma, se uma classe cliente utilizar um Builder ou qualquer outro inter-mediário para instanciar seus objetos, ela não pode depender diretamente dasimplementações. Isso irá permitir que novas implementações sejam incorpo-radas sem modi�car a aplicação.

�.� F������ �������� �� �������“Vidas fortes são motivadas por �ns dinâmicos; as menores por desejos einclinações.”– Kenneth Hildebrand

A primeira pergunta a ser respondida é: como obter um objeto sem de-pender direta ou indiretamente de sua classe? Em Java, a API de re�exão per-mite que uma classe seja instanciada a partir de uma string com o seu nome.Isso permite que as classes que serão instanciadas possam ser de�nidas, porexemplo, em um arquivo de con�guração. Dessa forma, ao ler esse arquivo, épossível instanciar a classe sem depender diretamente dela. Isso permite quenovas classes possam ser simplesmente adicionadas ao classpath da aplicaçãoe con�guradas no arquivo.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 183: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Modularidade

O padrão Dynamic Factory [��] propõe essa estrutura para a criaçãode objetos. Ela é aplicável quando se deseja criar um objeto de uma abstraçãoconhecida, ou seja, que possui uma determinada interface ou superclasse, po-rém cuja implementação não pode ser determinada em tempo de compilação.Isso normalmente ocorre quando se deseja permitir que novas implementa-ções possam ser incorporadas ao so�ware sem a sua modi�cação, tornando-o mais extensível e �exível. Um exemplo seria um so�ware que precisa serimplantado em diversos clientes e em alguns deles é necessário desenvolverclasses especí�cas para aquele contexto.

Estrutura do padrão

A estrutura do padrão Dynamic factory não é muito diferente da es-trutura de outros padrões de criação. Dessa forma, a classe cliente irá depen-der apenas da abstração compartilhada pelas implementações e irá acessaruma classe que implementa uma fábrica dinâmica, responsável pela criaçãodos objetos. Essa fábrica por sua vez acessa uma outra classe responsável porler as informações a respeito de que implementação deve ser instanciada parauma determinada interface ou superclasse. A partir dessa informação, a APIde re�exão é utilizada para instanciar essa classe.

A �gura �.� mostra a estrutura do padrão. A classe representada comoLeitorMetadados é responsável por ler as informações a respeito de quaisimplementações devem ser instanciadas. Essas informações podem estar ar-mazenadas em um arquivo de con�guração, em um banco de dados ou atémesmo nas anotações de alguma classe. Independente da forma como essascon�gurações são de�nidas, é importante que elas contenham qual a imple-mentação que deve ser instanciada para uma determinada abstração.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 184: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Fábrica dinâmica de objetos Casa do Código

Fig. �.�: Estrutura do Dynamic Factory

A partir dessa estrutura é muito fácil introduzir no sistema novas clas-ses. Para isso basta con�gurar o nome dessa classe no local onde oLeitorMetadados obtém as informações e adicionar a classe em um ar-quivo .jar no classpath da aplicação. Sendo assim, não apenas a classeCliente irá depender apenas da abstração de Produto, mas o processode criação dentro da classe FabricaDinamica também.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 185: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Modularidade

O ��� ��� ����������

A palavra “metadado” é sobrecarregada de signi�cado dependendoda área da computação em que estamos. De uma forma geral, metadadosigni�ca “dado sobre o dado”, que no contexto de uma linguagem orien-tada a objetos seriam as informações a respeito das classes de um sistema.Nesse caso, os dados são as informações da aplicação e as classes contêminformações sobre os seus tipos, sua visibilidade e como são manipula-dos. Dessa forma, no contexto de uma classe, os metadados seriam seusatributos, seus métodos, suas interfaces, sua superclasse, entre outros.

Na estrutura do padrão Dynamic Factory, a classeLeitorMetadados é responsável por ler uma informação a res-peito de uma abstração, que é a classe que deve ser instanciada naquelecontexto. Dessa forma, como essa é uma informação referente a umaclasse do so�ware, pode-se dizer que ele está lendo um novo metadadopara ela.

O padrão Dynamic Factory pode ser implementado em conjunto comum Builder ou comum Static Factory Method de acordo com as ne-cessidades da aplicação. Se necessário, mais de uma classe pode ser con�gu-rada para umamesma abstração, assim como classes que serão utilizadas paracomposição, ou mesmo para envolver a classe principal, como um Proxy. Oque diferencia esse padrão dos vistos no capítulo anterior é o desacoplamentoentre a fábrica e as implementações que estão sendo criadas.

Dynamic Factory na prática

Para exempli�car a criação de uma fábrica dinâmica com o uso de re�e-xão, a listagem a seguir mostra o exemplo de uma classe que cria os objetos deacordo com o nome da classe con�gurado em um arquivo de propriedades.A classe FabricaDinamica possui um construtor que recebe o nome doarquivo com as con�gurações de criação. A classe Properties da própria APIpadrão do Java é utilizada para fazer essa leitura, fazendo o papel de leitorade metadados.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 186: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Fábrica dinâmica de objetos Casa do Código

Listagem �.� - Fabrica dinâmica que lê a classe da implementação deum arquivo de propriedades:

public class FabricaDinamica {

private Properties props;

public FabricaDinamica(String arquivo)throws FileNotFoundException, IOException{

props = new Properties();props.load(new FileInputStream(arquivo));

}

public <E> E criaImplementacao(Class<E> interface) {String nomeClasse =

props.getProperty(interface.getName());

if(nomeClasse == null) {throw new IllegalArgumentException(

"Interface não configurada");}

try {Class clazz = Class.forName(nomeClasse);if (interface.isAssignableFrom(clazz)) {

return (E) clazz.newInstance();} else {

throw new IllegalArgumentException("Classe configurada não implementa ainterface");

}} catch (ClassNotFoundException e) {

throw new IllegalArgumentException("Classe configurada não existe",e);

} catch (Exception e) {throw new IllegalArgumentException(

"Não foi possível criar a implementação",e);}

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 187: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Modularidade

}

}

Muitas vezes, classes já existentes no sistema ou em algum framework uti-lizado pela aplicação podem ser utilizadas para ser um participante de umpa-drão. No exemplo apresentado da Dynamic factory, foi utilizada a classeProperties para realização da leitura do arquivo com os metadados. Emoutros padrões, como o Observer, por exemplos, o papel de observador ouobservado pode ser desempenhado por alguma classe já existente. Caso sejarequerida uma interface diferente para a classe existente, o padrão Adapter

pode ser utilizada para fazer a adaptação.A classe do exemplo espera que no arquivo sejam de�nidas proprieda-

des com o nome da interface e valores com o nome da classe que deve serinstanciada. Dessa forma, quando o método criaImplementacao() é in-vocado, o nome da interface é utilizado para recuperar a respectiva imple-mentação que deve ser instanciada. Essa instanciação é feita, primeiramenteobtendo-se a instância de Class referente àquela implementação atravésdo método estático Class.forName(), e em seguida invocando o métodonewInstance(). Observe que antes de efetivamente criar o objeto é veri-�cado se a classe realmente implementa a interface. Nesse caso, com a cha-mada do método newInstance(), o construtor sem parâmetros da classeserá invocado. Sendo assim, para que essa fábrica seja utilizada, é preciso queas implementações possuam esse construtor.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 188: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Fábrica dinâmica de objetos Casa do Código

R������� �� J���

Re�exão é a capacidade de um sistema de executar computações arespeito de si mesmo. De uma formamais prática, isso envolve o sistemapoder obter informações, acessar e até mesmo modi�car suas própriasclasses. A API Re�ection da linguagem Java provê funcionalidades ape-nas para obter informações e acessar as classes do sistema. Funcionalida-des demodi�cação de classes sãomais comuns em linguagens dinâmicas.

A classe básica da API Re�ection é a Class que representa umaclasse do sistema. A partir dela é possível obter informações, comoseu nome, seus métodos, seus atributos, sua superclasse e suas inter-faces. Além disso, também é possível instanciá-la através do métodonewInstance(), como visto no exemplo. Outras classes dessa APIrepresentam outros elementos de código, como Method, Field eConstructor. Está fora do escopo desse livro a explicação do funci-onamento dessa API, porém é importante saber esses fundamentos paraa compreensão de como o padrão Dynamic Factory funciona.

Para exempli�car a utilização da classe FabricaDinamica, será reto-mado o exemplo do gerador de arquivos. Para simpli�car o exemplo, vamossupor que as implementações possuem um construtor sem parâmetros e queo pós-processador é con�gurado através de ummétodo setter. A versão apre-sentada da classe FabricaDinamica não suporta o carregamento de maisde uma implementação da mesma interface, porém isso poderia ser contor-nado com uma pequena modi�cação. Para manter o exemplo simples, serácon�gurada apenas uma instância da interface PosProcessador para serinserida no GeradorArquivo.

A seguir são apresentadas listagens que mostram um exemplo de comoo arquivo de propriedades seria de�nido e como a criação das classes seriarealizada. Observe que o código que instancia o GeradorArquivo comsua dependência não referencia em nenhum momento a implementação.Adicionalmente, a implementação também não é referenciada pela classeFabricaDinamica, que lê o nome da classe de um arquivo externo. Essetipo de prática permite que novas classes sejam criadas e plugadas no sistema,

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 189: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Modularidade

sem a necessidade da recompilação das classes já existentes.

Listagem �.� - Arquivo de propriedades com a de�nição das classes:

br.com.casadocodigo.GeradorArquivo=br.com.casadocodigo.GeradorXML

br.com.casadocodigo.PosProcessador=br.com.casadocodigo.Compactador

Listagem �.� - Utilizando a FabricaDinamica para a criação do GeradorAr-quivo:

FabricaDinamica f = new FabricaDinamica("configuracoes.prop");GeradorArquivo gerador =

f.criaImplementacao(GeradorArquivo.class);gerador.setPosProcessador(f.criaImplementacao(

PosProcessador.class);)

Uma base para outros padrões

Mesmo sendo um padrão importante, a Dynamic Factory é muitasvezes utilizada por baixo dos panos. Apesar de ser válida sua utilização di-reta para a criação de alguns objetos do sistema, pode ser complicado utilizaresse padrão como estratégia de criação geral dentro de uma arquitetura. Aspróximas seções apresentam padrões para a criação desacoplada de objetosque podem ser utilizados no lugar da Dynamic Factory. Observem que,para que eles sejam possíveis de ser implementados para se obter modulari-dade, ainda é necessário que uma Dynamic Factory atue no carregamentodinâmico dessas classes em algum momento.

�.� I������ �� ������������“A verdadeira felicidade é... aproveitar o presente, sem a ansiedade dadependência do futuro.”– Lucius Annaeus Seneca

Quandoumobjeto precisa da colaboração de outro para cumprir suas res-ponsabilidades, é umprocesso natural que o próprio objeto crie ou busque es-sas instâncias. Essa responsabilidade pela criação das dependências, quando

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 190: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Injeção de dependências Casa do Código

está no próprio objeto, pode acabar criando um acoplamento e prejudicandoamodularidade, mesmo quando interfaces são utilizadas para interação entreas classes.

Uma forma de evitar que a própria classe seja responsável por criarsuas dependências é criá-las de forma externa e inseri-las no objeto nomomento ou depois de sua criação. Essa é a ideia principal do padrãoDependency Injection [��]! Apesar de se utilizar muito o nome emportuguês, Injeção de Dependências, para padronizar a utilizaçãodos nomes dos padrões em inglês nesse livro, ele será referenciado comoDependency Injection.

I������� �� ���������

Devido ao fato desse padrão inverter a responsabilidade de criaçãodas dependências de uma classe, ele também é conhecido como Inver-são de Controle. Porém o nome “inversão de controle” também é utili-zado para denominar práticas para o desenvolvimento de frameworks,nas quais as classes do framework invocam classes da aplicação, inver-tendo assim o controle do �uxo de execução. Dessa forma, o nomeDependency Injection acabou prevalecendo.

Apesar de sua simplicidade, o Dependency Injection é um padrãomuito poderoso, pois desacopla a classe de suas dependências, permitindoque elas sejam reutilizadas em diferentes contextos. Consequentemente, atestabilidade dessa classe acaba também sendo aumentada, pois objetos vol-tados para o teste podem ser injetados no lugar das dependências.

Funcionamento da injeção

O foco do padrão Dependency Injection é na criação e con�guraçãode uma classe que possui uma outra como dependência. Essa dependência,por sua vez, é do tipo de uma abstração, normalmente representada por umainterface, a qual geralmente possui mais de uma implementação. O problemano caso é como criar e conectar essas instâncias sem que exista uma depen-dência de uma para outra. Esse padrão propõe como solução a existência de

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 191: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Modularidade

uma outra classe que irá “montar” a aplicação, criando as implementaçõescorretas de cada componente e conectando-as de forma a se obter o compor-tamento desejado.

A �gura �.� apresenta uma representação do padrão Dependency

Injection. A classe Montador é quem na verdade coordena todo o pro-cesso, tanto de criar as instâncias corretas, quanto de injetá-las na classeProduto. Essa, por sua vez, deve apenas prover uma forma de as dependên-cias poderem ser injetadas. A próxima seção explora mais detalhes a respeitodesse processo.

Fig. �.�: Injeção de dependências

O processo de criação e reutilização de instâncias é controlado peloMontador, que pode de�nir diferentes estratégias para isso. Por exemplo,caso a dependência seja um objeto que possa ser compartilhado, a mesmainstância pode ser injetada diversas vezes. Uma outra estratégia, muito co-mum para conexões a serviços ou servidores remotos, é a criação de um poolde objetos, de onde eles são retirados para a injeção em uma classe e retorna-dos ao �nal de sua utilização. O fato desse processo não ser controlado pelaclasse permite que ela possa ser reutilizada em diversos contextos.

Imagine uma classe que realize acesso a uma base de dados e receba atra-vés de uma Dependency Injection a conexão que deve ser utilizada porela. Ela poderia ser utilizada por uma aplicação desktop onde apenas umaconexão é criada para toda aplicação, mas também poderia ser reusada em

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 192: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Injeção de dependências Casa do Código

uma aplicação Java EE que executa em um servidor de aplicação no qual aconexão é obtida de um pool. O simples fato de essa conexão ser criada deforma externa à classe e injetada em sua estrutura é que a torna reutilizávelnas diferentes arquiteturas citadas.

Como se pode perceber, grande parte do trabalho acaba �cando com aclasse Montador, responsável pela criação e conexão entre as classes envol-vidas. Ela pode ser simples e criada especi�camente para uma aplicação, ouser genérica e carregar dinamicamente as classes que precisam ser instanci-adas através de uma Dynamic Factory. Felizmente, existem frameworkse APIs, como o Spring e o CDI do Java EE, que implementam montadoresque criam as instâncias a partir de con�gurações em arquivos XML e anota-ções. Sendo assim, raramente é necessário criar uma classe para fazer o papelde montador. Mais à frente será mostrado como a injeção de dependênciasfunciona no Spring.

Formas de injetar dependências

A injeção de dependências pode ser habilitada de diversas formas emumaclasse. Essa seção irá explorar as alternativas existentes, assim como amotiva-ção na utilização de cada uma delas. De alguma forma, deve ser possível queuma entidade externa reconheça a dependência e possa inseri-la na classe,mesmo que em apenas certos momentos de seu ciclo de vida.

O tipo mais comum de injeção de dependências é através do métodosetter. Através desse método, a classe permite que a dependência seja con�-gurada com um método setter, que a recebe como parâmetro. A listagem aseguir mostra como a classe AcessoDados disponibiliza um método setterpara que a conexão com a base de dados possa ser con�gurada. Utilizandoessa estratégia, a dependência pode ser atribuída e alterada a qualquermomento.

Listagem �.� - Injeção de dependência por método setter:

public class AcessoDados {private Connection connection;public void setConnection(Connection c) {

connection = c;

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 193: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Modularidade

}//...

}

Uma outra abordagem para a injeção de dependências é o construtor.Nessa estratégia, a dependência deve ser injetada no objeto no momento desua criação. Esse caso acaba sendo mais indicado para cenários onde elaé obrigatória e essencial para o funcionamento da classe. A listagem a se-guir mostra o mesmo exemplo da classe AcessoDados, porém utilizando aDependency Injection através do construtor.

Listagem �.� - Injeção de dependência pelo construtor:

public class AcessoDados {private Connection connection;public AcessoDados(Connection c) {

connection = c;}//...

}

Como foi apresentado no capítulo anterior, a utilização de construtorestraz diversas restrições e a classe que utiliza injeção por construtores acabasofrendo os mesmos problemas. Por outro lado, o construtor é uma forma degarantir que uma instância da classe não será criada sem receber aquele parâ-metro. Uma dependência bidirecional, por exemplo, seria impossível de serinjetada por construtores nas duas classes... Na prática, a injeção por méto-dos setter acaba sendomais comum, sendo feita uma con�guração cuidadosapara que o montador não deixe de con�gurar as dependências importantes.

Uma outra abordagem existente para a Dependency Injection é autilização de interfaces. Nesse caso, é de�nida uma interface que declara mé-todos que devem ser utilizados para a injeção da dependência. Dessa forma,a classe que precisa dela deve implementar essa interface para poder recebê-la. Apesar de ser uma abordagem mais “burocrática” que as outras duas, elapermite que a classe que injeta a dependência reconheça facilmente o objetoque precisa dela para poder proceder com a injeção.

A estratégia de utilização de interfaces para injetar dependências é muitoutilizada quando o montador precisa gerenciar diversas instâncias e passar

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 194: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Injeção de dependências Casa do Código

certos objetos apenas para as classes que precisam deles. A interface acabafuncionando como um marcador que indica quando o objeto precisa da de-pendência.

Para exempli�car essa situação, considere um sistema que possui umaabstração chamada Processo que representam processos de negócio quepodem ser executados. Das classes que implementam esses processos, al-gumas delas precisam saber qual é o usuário que as está executando. Paranão tornar essas classes acopladas a como as instâncias da classe Usuario

são armazenadas e acessadas, tomou-se a decisão de utilizar Dependency

Injection para que o usuário seja inserido nessas instâncias. A listagem aseguir mostra a de�nição da interface que as classes que desejam receber ainstância de Usuario devem implementar.

Listagem �.� - De�nição de um interface para injeção do usuário:

public interface CienteUsuario{public void recebeUsuario(Usuario u);

}

Como forma de injetar a dependência, foi criado um Proxy que encap-sula classes da interface Executor, que é responsável por executar as classesdo tipo Processo passadas a elas. A classe ProxyInjecaoUsuario,apresentada na próxima listagem, veri�ca com o operador instanceof seo objeto implementa a interface e injeta a dependência da classe Usuario

em caso positivo.

Listagem �.� - Proxy para a injeção do usuário:

public class ProxyInjecaoUsuario implements Executor {private Executor executor;private Usuario usuario;public ProxyInjecaoUsuario(Executor e, Usuario u) {

executor = e;usuario = u;

}public void executar(Processo p) {

if(p instanceof CienteUsuario) {((CienteUsuario)p).recebeUsuario(usuario);

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 195: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Modularidade

}executor.executar(p);

}}

Pelas técnicas anteriores pode ser observado que a classe deve sinalizarde alguma forma onde e qual dependência precisa ser injetada. Uma outraforma de se sinalizar isso, bastante utilizada em frameworks e APIs maisrecentes, é através de anotações. A anotação é normalmente incluída em umatributo ou no seu respectivo método setter no qual ela deve ser injetada.A listagem a seguir mostra um exemplo de um Servlet que deve receberuma injeção de dependência de um EJB do servidor de aplicações ondeestá implantado. Note que nesse caso é feita utilização de re�exão para quea dependência possa ser inserida diretamente em um atributo, mesmo elesendo privado.

Listagem �.� - Injeção de dependência pelo construtor:

public class ServletLogin extends HttpServlet {@EJBprivate ServicoAutenticacao servico;//lógica do servlet omitida

}

Os efeitos colaterais da injeção

Se por um lado a Dependency Injection tem o potencial de tornar aclassemais reutilizável, a passagemde responsabilidade damontagemdo rela-cionamento das classes para um terceiro também pode gerar erros em tempode execução. Como a classe não tem mais controle sobre suas dependências,ela �ca sujeita a erros referentes a uma falha nesse processo que ocorre deforma externa.

Um erro bastante comum quando esse padrão é utilizado ocorre quandouma dependência deixa de ser con�gurada. Nesse caso, é lançada a conhe-cida e indesejável NullPointerException quando a classe tenta invocaralgummétodo nela. Por uma questão de segurança de código, isso pode levar

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 196: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Injeção de dependências Casa do Código

muitos desenvolvedores a incluírem diversos condicionais para veri�car se ainstância não é nula, o que gera uma certa poluição e verbosidade.

Apesar da injeção de dependências simpli�car os testes de unidade, de-vido à chance de erro com as montagens, quando esse padrão é utilizado, émuito importante que se faça os testes de integração da aplicação. Esses testesservem para garantir não somente que as classes estão funcionando de formaadequada juntas, mas que a aplicação está criando e injetando as instânciasde forma correta. Seria como se estivéssemos testando se o montador estáfazendo a injeção adequadamente.

Amontagem dinâmica da aplicação também pode di�cultar a compreen-são do contexto global de como os objetos interagem para formar a funcio-nalidade da aplicação. A �exibilidade de uma classe em poder interagir comdiversas implementações também di�culta saber com que classes ela está in-teragindo emumcontexto concreto. Por exemplo, ao se deparar comumerro,o desenvolvedor não tem como saber qual foi a classe invocada por ela. Paraisso ele deve procurar no montador ou nas con�gurações da aplicação parasaber qual era a estrutura naquele caso. Essa indireção acaba sendo um efeitocolateral ao desacoplamento conquistado.

Injeção de dependências no Spring

Normalmente, quando a injeção de dependências é utilizada emuma apli-cação, é utilizado algum framework para realizar amontagemdas classes. Umdos frameworks que primeiro se destacou por esse tipo de funcionalidade emuito utilizado até hoje é o Spring. Utilizando-o, as instâncias com suas res-pectivas dependências são de�nidas em um arquivo XML e o framework seencarrega de criá-las.

A listagem a seguir exempli�ca como o Spring seria utilizado no exemplodo gerador de arquivo. Cada instância é de�nida como um bean no arquivode con�guração, recebendo um nome que será utilizado para referênciasinternas e para recuperá-lo de forma externa. Utilizando o nome dos beans,as instâncias podem ser injetadas utilizando diferentes estratégias, como peloconstrutor ou através de propriedades.

Listagem �.� - Con�guração da injeção de dependência pelo Spring:

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 197: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Modularidade

<?xml version="1.0" encoding="UTF-8"?><beans...>

<bean id="compactador"class="br.com.casadocodigo.Compactador" />

<bean id="criptografador"class="br.com.casadocodigo.Criptografador">

<constructor-arg type="int" value="3"/></bean><bean id="composite"

class="br.com.casadocodigo.PosProcessadorComposto"><constructor-arg><ref bean="compactador"/><ref bean="criptografador"/>

</constructor-arg></bean><bean id="geradorArquivo"

class="br.com.casadocodigo.GeradorXML"><property name="processador" ref="composite" />

</bean></beans>

No exemplo, o bean criptografador recebe um parâmetro no cons-trutor referente ao tamanho do deslocamento de caracteres utilizado no al-goritmo. As referências dos beans criptografador e compactador sãotambém passadas como parâmetro para o construtor do bean composite.Finalmente, o bean geradorArquivo recebe uma injeção do beancomposite na propriedade processador. Através do arquivo XML apre-sentado, é possível de�nir qual classe será instanciada para cada bean e comouma deve ser utilizada na construção da outra.

Os usuários do framework Spring, para recuperarem um bean devemfazer uso da classe ApplicationContext. Ela é responsável por carregaro arquivo XML com a de�nição da composição dos objetos e retorná-los àaplicação. A listagem a seguir mostra como ela seria utilizada na criaçãode uma instância do GeradorArquivo como de�nido no arquivo XML.Em aplicações web, o Spring normalmente é integrado diretamente com ocontainer web já criando os objetos e injetando-os nas classes que tratamas requisições. Nesse caso, não há a necessidade de acesso direto à classe

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 198: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Injeção de dependências Casa do Código

ApplicationContext pela aplicação, pois isso é feito pelo container webou pelo framework que está sendo utilizado.

Listagem �.�� - Recuperação de um bean con�gurado no Spring:

String xml = "applicationContext.xml";ApplicationContext ac =

new ClassPathXmlApplicationContext(xml);GeradorArquivo ga =

(GeradorArquivo) ac.getBean("geradorArquivo");

A partir do que foi mostrado, é possível perceber como seria simplesa mudança das implementações envolvidas e da con�guração da estruturamontada. Por serem de�nidos em um arquivo XML, os beans podem ser al-terados, consequentemente mudando o comportamento da aplicação, sem anecessidade de sua recompilação. Para exempli�car esse fato, imagine queseja necessário introduzir um Proxy na classe GeradorArquivo para ainvocação assíncrona de seus métodos. Isso poderia ser facilmente realizadocolocando a classe do Proxy no bean geradorArquivo, fazendo-o encap-sular o que estava de�nido anteriormente, conforme mostrado na listagem aseguir.

Listagem �.�� - Inserção de um Proxy de forma transparente:

<?xml version="1.0" encoding="UTF-8"?><beans...>

...<bean id="geradorArquivo"

class="br.com.casadocodigo.GeradorProxyAssincrono"><constructor-arg>

<ref bean="geradorEncapsulado"/></constructor-arg>

</bean><bean id="geradorEncapsulado"

class="br.com.casadocodigo.GeradorXML"><property name="processador" ref="composite" />

</bean></beans>

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 199: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Modularidade

�.� S������ L������“Quando tentar localizar algo, procure primeiro em sua mente.”– Craig Bruce

O padrão Service Locator [��] é uma alternativa ao padrãoDependency Injection para permitir amodularidade nas aplicações. Di-ferentemente da injeção, as próprias classes são responsáveis por buscar assuas dependências. Porém, elas fazem isso através de uma outra classe res-ponsável por localizar a implementação que deve ser utilizada para uma de-terminada interface.

No contexto desse padrão, a palavra “serviço” é utilizada para denotaruma colaboração de uma outra classe. Seria como se a classe externa esti-vesse prestando um serviço para a que a utiliza. Nesse caso, a classe principaldeclara o tipo de serviço que ela precisa utilizando uma abstração, como umaclasse abstrata ou uma interface, e então utiliza um Service Locator paraencontrar a que irá prestar aquele serviço para ela.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 200: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Service Locator Casa do Código

S������ L������ ���� �� J�EE P������

O Service Locator foi documentado como um Core J�EE Pat-tern e foi muito utilizado para a localização de serviços remotos, prin-cipalmente enterprise beans, em aplicações J�EE [�]. Nesse contexto, oService Locator se conectava a um repositório JNDI para adquiriruma referência a esses serviços remotos e fazia um cache dessa referên-cia para evitar acessos desnecessários ao registro de nomes. Nas novasversões da plataforma enterprise Java, que perdeu o “�” e agora se chamasimplesmente Java EE, a utilização desse padrão �cou obsoleta a própriaplataforma já presta esse serviço, realizando inclusive a injeção dessasdependências nas classes.

Por esse motivo, quando se fala em Service Locatormuita genteacha que é um padrão obsoleto pela experiência que teve com ele na pla-taforma J�EE. Nesse contexto, esse padrão tinha o objetivo principal decentralizar o mecanismo de busca de serviços, encapsulando as peculi-aridades de cada registro e evitando buscas desnecessárias. Porém, foradesse contexto, esse padrão ainda é muito importante, principalmentepara permitir a modularidade das aplicações. Sendo assim, se você aindaguarda algum trauma da utilização desse padrão para buscar EJBs, deixeisso de lado porque ele pode ser muito útil em outras situações.

Estrutura para localização de serviços

No Service Locator, não existe a �gura de uma classe que centralizaa responsabilidade de criação e ligação das classes que irão compor a apli-cação. Nesse padrão cada uma busca suas próprias dependências através deuma classe responsável por buscar a instância que deve ser adicionada comodependência.

A �gura �.� apresenta a estrutura do padrão Service Locator. No di-agrama, a classe Cliente representa a classe que precisa criar uma instânciada abstração Servico. Apesar de haver uma relação de agregação entre es-sas classes, nesse padrão a relação poderia ser mais fraca, como a criação e

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 201: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Modularidade

utilização do serviço de forma encapsulada dentro de um método. Segundoo padrão, uma classe responsável por encontrar e retornar a implementaçãodesse serviço, representada por LocalizadorServicos, é utilizada pelaclasse Cliente para fazer isso.

Fig. �.�: Estrutura do Service Locator

Uma classe que implemente um localizador de serviços pode possuir umaimplementação �xa, retornando as instâncias desejadas de acordo com o pa-râmetro passado. Era esse tipo de Service Locator que costumava serutilizado nas aplicações J�EE. Porém, para conseguirmodularizar a aplicação,é preciso que ele faça uso de uma Dynamic Factory para que os serviçosretornados possam ser mais facilmente alterados.

Quando o padrão Dependency Injection é utilizado, essa questãoainda precisa ser implementada, porém quem é o responsável por buscar osserviços não é alguém que é invocado pelas classes, e sim o montador. Porisso,muitas vezes, omontador precisa de um Service Locatorpara poderlocalizar as implementações e injetá-las nas classes.

Umdos problemas desse padrão está no fato das classes �carem acopladasà classe responsável pela busca de serviços. Isso as impede de serem reutili-zadas fora de um contexto em que o Service Locator esteja presente. Dequalquer forma, o Service Locator costuma ser utilizado encapsuladona classe, não sendo uma dependência exposta na API externa. Isso é bompois não cria essa dependência nas classes cliente.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 202: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Service Locator Casa do Código

Utilizando a classe ServiceLoader

A JDK possui uma classe chamada ServiceLoader que pode ser uti-lizada como um Service Locator que carrega dinamicamente as imple-mentações de uma interface. Essas implementações podem estar empaco-tadas e con�guradas dentro de diferentes arquivos JAR. Sendo assim, a im-plementação será carregada de acordo com os arquivos JAR que estiverempresentes no classpath da aplicação. Basta incluir e remover arquivos do JARpara alterar a implementação utilizada.

Para que a classe ServiceLoader possa ser utilizada, o primeiro passoseria ter uma interface que serviria de abstração para o serviço a ser criado.As implementações dessa interface poderiam então ser colocadas em arqui-vos JAR para serem localizadas. Além da implementação, ainda é necessárioincluir no JAR um arquivo que registra a classe criada como uma implemen-tação daquele serviço. Esse arquivo deve ser colocado dentro do diretórioMETA_INF/services e deve ter o nome completo da interface, incluindo opacote. Como conteúdo, deve haver simplesmente uma linha com o nome daclasse para cada implementação daquela abstração contida naquele JAR.

A �gura �.� ilustra como deve ser essa estrutura. Considere Abstracao

como a interface para qual se deseje de�nir os serviços. No exem-plo, o arquivo1.jar possui as implementações ImplementacaoA eImplementacaoB e o arquivo2.jar possui a ImplementacaoC. Cadaarquivo JAR possui um arquivo chamado Abstracao, o nome da interface,com os nomes das classes. Dessa forma, de acordo com o arquivo JAR co-locado no classpath da aplicação, classes diferentes seriam retornadas peloServiceLoader.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 203: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Modularidade

Fig. �.�: Arquivos JAR para utilização do ServiceLoader

A listagem a seguir ilustra, dentro do exemplo da �gura �.�, comofunciona a classe ServiceLoader. Esse trecho de código imprime noconsole todas as implementações que estão registradas para uma interface.O método estático load() da classe ServiceLoader é utilizado paracarregar todas as implementações registradas da interface Abstracao.Em seguida, é utilizado um Iterator para percorrer as implementaçõesrecuperadas e imprimi-las no console.

Listagem �.�� - Imprimindo as implementações encontradas pelo Servi-ceLoader:

ServiceLoader<Abstracao> sl =ServiceLoader.load(Abstracao.class);

Iterator<Abstracao> i = sl.iterator();while(i.hasNext()) {

System.out.println(i.next().getClass().getName());}

Para mostrar um exemplo mais concreto do uso do ServiceLoader, alistagem a seguir mostra como ele poderia ser utilizado para a recuperação

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 204: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Service Locator Casa do Código

dos pós-processadores para a classe GeradorArquivo. Nesse caso, oService Locator está sendo utilizado no construtor para popular oatributo processador da classe. Observe que caso exista apenas uma im-plementação, ela será atribuída diretamente ao atributo, mas caso exista maisde uma, a classe PosProcessadorComposto é utilizada. Dessa forma, casoum arquivo JAR con�gurado com uma nova implementação for adicionadoao classpath da aplicação, ela será recuperada pelo ServiceLoader. Nocaso da classe PosProcessadorComposto, que é uma implementação quenão deve ser retornada, basta não incluí-la no arquivo.

Listagem �.�� - Buscando os pós-processadores do GeradorArquivo com umService Locator:

public abstract class GeradorArquivo {

private PosProcessador processador = new NullProcessador();

public GeradorArquivo()) {ServiceLoader<PosProcessador> sl =

ServiceLoader.load(PosProcessador.class);Iterator<PosProcessador> i = sl.iterator();List<PosProcessador> lista = new ArrayList<>();while(i.hasNext()) {

lista.add(i);}if(lista.size() == 1) {

processador = lista.get(0);} else if (lista.size > 1) {

processador = new PosProcessadorComposto(lista);}

}

//demais métodos}

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 205: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Modularidade

C��������� �������� ������� �� ��� �������

No decorrer desse livro, vimos diversos padrões que aos poucos fo-ram sendo incorporados às soluções apresentadas. A ideia é que paula-tinamente eles comecem a fazer parte da nossa forma de pensar e queacabamos utilizando-os mesmo sem perceber. Desa�o você a dar maisuma olhada no código da última listagem e tentar identi�car os pa-drões que estão ali presentes. Utilizamos o Bridge na abstração doGeradorArquivo e no uso de composição para os pós-processadores,o Null Object é utilizado para os casos onde não há pós-processador,o Composite é utilizado para quando há mais de um pós-processador,e, �nalmente, o Service Locator é utilizado para o carregamentodinâmico dos pós-processadores.

Note que não estou dizendo para aplicar todos os padrões possíveisem uma mesma solução, pois isso é uma má prática. Porém, o que acabaacontecendo, é que na solução sucessiva dos problemas de design de umaclasse, é natural que o resultado seja fruto da combinação de diversospadrões.

�.� S������ L������ ������ D��������� I����-����

Depois de aprender sobre os padrões Dependency Injection e ServiceLocator, surge a dúvida de qual deles utilizar. Antes de começar a exploraras vantagens e desvantagens de cada um, é importante ressaltar que com autilização de qualquer um dos dois é possível se obter modularidade, parti-cionando a aplicação em partes efetivamente independentes. Dessa forma,�ca mais simples a introdução de novas implementações sem a necessidadede recompilação do código das classes que as utilizam. Com isso, cada umdos módulos do sistema pode evoluir de forma independente, facilitando amanutenção e evolução do so�ware.

Uma das grandes diferenças entre os dois padrões está em quem possui aresponsabilidade demontar a con�guração de objetos da aplicação. Quando o

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 206: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Service Locator versus Dependency Injection Casa do Código

padrão Dependency Injection é utilizado, ela �ca centralizada na classeresponsável pela montagem. Nesse caso, ela precisa conhecer todas as de-pendências de todas as classes envolvidas, estaticamente ou dinamicamente apartir de alguma con�guração. Diferentemente, quando se utiliza o Service

Locator, cada classe é responsável por buscar suas dependências a partir deuma classe responsável pela localização dos serviços. Isso permite que novasclasses de�nam novos pontos onde outras abstrações possam ser inseridas,sem a necessidade de haver um ponto central que saiba dessa nova depen-dência.

Em resumo, quando se injeta dependências, a responsabilidade de mon-tar toda a aplicação �ca centralizada e, quando cada classe busca seus servi-ços, essa responsabilidade �ca distribuída. Sendo assim, se o contexto é umaaplicação onde se deseja ter modularidade para questões de manutenção eevolução, mas não existe a necessidade da adição de novas classes dinamica-mente, então o Dependency Injection é uma boa solução. Porém, emcenários em que a arquitetura é baseada em plugins e não existe um ponto naaplicação no qual se tem consciência de todas possíveis dependências, entãoo Service Locator se mostra mais adequado.

Uma consequência disso está em quão explícitas são as dependências deuma classe. Quando se usa o **Dependency Injection, é fácil visuali-zar através dos construtores, interfaces e métodos de acesso quais são asdependências que podem ser injetadas. Por outro lado, a invocação de umService Locator** pode estar encapsulada dentro dos métodos da classe,não as expondo externamente. Isso pode ter um efeito negativo, pois nãodeixa claro quais precisam ser con�guradas para uma determinada classe.

Apesar de não ser obrigatório, o Dependency Injection acaba sendomuito utilizado em ambientes onde os objetos da aplicação são gerenciadospor um container. Um exemplo disso seriam aplicações web ou aplicaçõesEJB, em que o ciclo de vida dos objetos é controlado pelo servidor. Dessaforma, como a criação dos objetos que tratarão as requisições �ca a cargo docontainer, ele tem a oportunidade de injetar as dependências no momentocerto. Frameworks como Spring são chamados de lightweight container, oucontainer leve, devido ao fato de controlarem o ciclo de vida dos objetos ge-renciados por eles.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 207: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Modularidade

Um ponto onde o Dependency Injection certamente leva vantagemé na testabilidade. Como a dependência é injetada na classe, esse mesmome-canismo pode ser utilizado para a injeção de um objeto falso, oumock object[��], para isolar a classe durante o teste. Já com o Service Locator, issopode ser um pouco mais complicado, pois omock object precisaria ser con�-gurado para ser retornado por ele. Dependendo de como essa con�guraçãoé feita, isso pode ser mais simples ou mais complicado, porém de qualquerforma pode ser um problema para casos em que a classe domock é gerada di-namicamente por algum framework. Nesses casos, uma solução seria já pen-sar em uma implementação que simpli�que a substituição da implementaçãoretornada para um serviço em código de teste.

A����������� �� �������

Os padrões de projeto documentam soluções recorrentes que são uti-lizadas em um determinado contexto. Por mais que eles documentemboas soluções, nem sempre um determinado padrão é a melhor soluçãopara uma aplicação. Por isso, podem existir mais de um padrão que po-dem ser aplicados na resolução de ummesmo problema, comomostradonesse capítulo. Se você quer �exibilizar os passos de um algoritmo, vocêpode utilizar o Template Method para fazer isso com herança, ou oStrategy para usar composição. Se você quer combinar soluções declasses existentes de forma transparente, pode-se utilizar o Composite

ou o Chain of Responsibility. Por mais que ambos os padrõesnesses casos resolvam o problema, eles possuem características e con-sequências diferentes. Dessa forma, é importante não utilizar os padrõescegamente e avaliar dentre as alternativas existentes quais as que possuemconsequências mais adequadas para o contexto da sua aplicação.

�.� C������������ ������ �� ��������Nesse capítulo abordamos padrões que focam em soluções para que se con-siga obter modularidade para aplicações. Essas soluções atuam em um mo-mento crucial na vida dos objetos: a sua criação. Desde o primeiro capítulo

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 208: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Considerações �nais do capítulo Casa do Código

desse livro, já foram apresentadas soluções que utilizam encapsulamento epolimor�smo para desacoplar os objetos, porém no momento da criação, emque a implementação efetivamente precisa ser instanciada, a referência diretaà classe costuma interferir na modularidade.

O padrão Dynamic Factory foi apresentado como uma base que podeser utilizada pelos outros padrões para o carregamento dinâmico das imple-mentações que devem ser utilizadas para compor a classe principal. Em se-guida, foram mostrados os padrões Dependency Injection e Service

Locator, que apresentam alternativas a respeito de como uma classe podedelegar a criação de suas dependências. Se combinados com uma soluçãoque utiliza um Dynamic Factory na criação das instâncias, é possível re-almente modularizar a aplicação.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 209: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

C������� �

Adicionando operações

“Nós não podemos resolver problemas usando o mesmo tipo de pensamentoque usamos quando nós os criamos.”– Albert Einstein

Nos capítulos anteriores desse livro foram apresentados alguns padrõesque auxiliam na adição de funcionalidade em classes já existentes. Por exem-plo, os padrões Proxy e Decorator permitem a adição de funcionalidadesatravés do encapsulamento do objeto original. Dessa forma, um Proxy podeadicionar uma lógica antes ou depois da invocação de um método. Porémessa lógica adicionada é considerada transversal à da classe que está sendoencapsulada, ou seja, ela é independente e complementar a elas.

Por outro lado, padrões que utilizam composição, como o Strategy**e o Bridge, podem trocar a instância que a compõe para alterar a fun-cionalidade que está sendo executada. De forma complementar, padrões

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 210: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Classes que representam comandos Casa do Código

que permitem a composição recursiva, como o Composite e o Chain

of Responsibility**, possibilitam a combinação do comportamento dasimplementações. Apesar de esse recurso permitir que seja possível variar, oumesmo complementar, a funcionalidade de certos pontos de um objeto, elanão permite que sejam adicionadas novas operações.

Esse capítulo irá explorar padrões que podem ser utilizados para a adiçãode novas funcionalidades e novas operações para classes. Seguindo os princí-pios da orientação a objetos, nesses casos, a própria operação será consideradauma entidade no so�ware. A partir disso, serão de�nidas abstrações para quediversas operações possam ser implementadas e incorporadas às classes sema necessidade de sua modi�cação.

�.� C������ ��� ����������� ��������“Um comando com sabedoria é obedecido com alegria.”–�omas Fuller

Muitas vezes, precisamos de�nir operações a serem executadas sem quequem aciona essa operação tenha ciência do que está sendo executado. Umexemplo comum disso ocorre em interfaces grá�cas, quando algo precisa seradicionado para ser executado no acionamento de um botão ou de um itemde menu. Em outras palavras, é preciso que a execução de uma lógica, quenormalmente é representada através de um método, seja representada poruma classe.

Vamos tomar como contexto do exemplo uma aplicação de e-commerceque precisa ser implantada em diversos clientes que possuem diferentes ne-cessidades. O carrinho de compras, por exemplo, que é uma importante classepara o sistema, precisa de diferentes operações dependendo da implantação.Em uma loja que vende produtos de informática, o carrinho deve saber in-formar o valor do frete e a previsão de entrega, porém essas informações nãofazem sentido para uma loja que vende música digital e precisa informar otamanho dos arquivos para download e o tempo estimado de acordo com avelocidade da conexão do cliente.

Como foi dito na introdução, alguns dos padrões já apresentados nesselivro permitem alterar a implementação de uma operação implementada por

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 211: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Adicionando operações

uma classe. Porém esse seria um caso diferente, no qual diferentes operaçõesseriamdisponibilizadas pela classe, dependendo da situação. Como lidar comisso? Como permitir que as operações disponibilizadas pela classe possam serincorporadas de forma dinâmica?

Conhecendo o padrão Command

O Command é um padrão que consiste na representação de uma operaçãocomo uma classe. A abstração que representa o comando, no formato de umasuperclasse ou interface, de�ne um método que deve ser utilizado para exe-cutar a operação. Os comandos concretos, que implementam ou estendem aabstração, devem de�nir como atributo a estrutura de dados necessária para asua execução. Dessa forma, o comando acaba sendo uma classe que pode re-presentar uma operação do sistema independente de todo o contexto. É essacaracterística que permite diversas aplicações desse padrão!

A �gura �.� apresenta a utilização do Command no contexto de um com-ponente. Um componente pode possuir como umaHook Class um comando,o qual ele irá executar como resposta a algum evento do sistema. A classe quecria o componente, na �gura representada pela classe Fabrica, deve injetara implementação de Comando adequada. Nesse caso, o uso de uma interfacemais geral permite que uma mesma implementação possa ser usada em vá-rios componentes e que qualquer implementação possa ser associada a umcomponente especí�co.

Fig. �.�: Utilização do Command como parte de um componente

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 212: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Classes que representam comandos Casa do Código

Apesar da classe Componente representada no diagrama poder ser umcomponente grá�co ou não, o uso desse padrão é muito comum para a con-�guração de respostas para ações de usuário para componentes de interfacegrá�ca. Imagine, por exemplo, que um mesmo comando pode ser executadoao se clicar em um botão, ao selecionar um item de menu ou ao acessar umatalho no teclado. Por outro lado, vários botões que são representados pelamesma classe precisam executar diferentes comandos ao serem acionados.Essa liberdade na associação dos comandos aos componentes é uma das van-tagens desse padrão.

Muitas vezes, a classe que possui Command não o executa diretamente,mas o passa para outra classe para sua execução. A �gura �.� apresenta essecenário. Nesse caso, a classe Cliente pega o comando con�gurado em al-gum componente ou cria a instância de ComandoConcreto com o compor-tamento desejado, e

envia a classe ExecutorComandos para execução. Essa estrutura fazsentido quando existe a necessidade de um controle central dos comandosque estão sendo executados. Um exemplo dessa situação seria quando é ne-cessário injetar recursos nesse comando, como uma conexão com o bancode dados. Outro cenário seria quando se precisa guardar um histórico doscomandos executados, como para questões de transação ou para possibilitarque o usuário volte atrás em uma ação.

Fig. �.�: Passando o Command como parâmetro para execução

Uma das principais consequências positivas do padrão Command está na

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 213: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Adicionando operações

facilidade de adição de novas operações ao so�ware. Por ele abstrair o con-ceito de operação, isso permite que novas implementações possam ser facil-mente incorporadas. Outro aspecto positivo do Command está no fato de eleser uma representação de uma operação independente do contexto em queela está inserida. Isso permite que essa operação seja armazenada em umhistórico ou enviada pela rede, de forma que suas informações possam seracessadas e a funcionalidade executada em qualquer momento. Isso é muitoútil para a implementação de transações, registro de auditoria e até mesmo afuncionalidade de desfazer ações do usuário.

Por outro lado, o fato de cada operação ser representada por uma classediferente faz com que o número de classes cresça bastante. Um Command

também acaba prejudicando o encapsulamento da classe relacionada com suaexecução, pois ele precisa ter acesso às informações dela, que muitas vezes fa-riam sentido apenas internamente. Uma forma de contornar essa questão éutilizar Dependency Injection para injetar as informações nas classesque representam um comando. Uma alternativa para identi�car qual parâ-metro deve ser injetado poderia ser a utilização da injeção por interfaces, emque a presença de uma interface indicaria a necessidade de uma informaçãono comando.

Implementando o carrinho de compras

Para exempli�car a utilização do padrão Command, vamos pegar o exem-plo do carrinho de compras utilizado na introdução dessa seção. O objetivo épermitir que o carrinho de compras possa ter diferentes operações de acordocomo contexto emque a aplicação tiver sido implantada. Isso irá permitir nãosomente que ele possa possuir diferentes operações em diferentes contextos,mas também que novas operações possam ser facilmente incorporadas.

Para implementar o padrão Command nesse contexto, as operações docarrinho de compras devem ser representadas pelos comandos. A listagema seguir apresenta a interface que deve ser implementada pelas classes querepresentam os comandos. Observe que o método executar() não recebenenhum parâmetro, pois os parâmetros que essa execução tiver deverãoser inseridos na classe e armazenados como atributos. O retorno do tipoObject foi utilizado para possibilitar que o comando retornasse qualquer

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 214: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Classes que representam comandos Casa do Código

objeto como resultado de sua execução, o qual deve ser tratado por queminvocá-lo.

Listagem �.� - Interface que abstrai uma operação do carrinho de com-pras:

public interface ComandoCarrinho{public Object executar();

}

A classe CarrinhoCompras, apresentada na listagem a seguir, é a classeque representa o carrinho de compras propriamente dito. Em sua estruturade dados, além do usuário e da lista de produtos, também estão os comandosdisponíveis para aquela instância. Os comandos são armazenados em ummapa e são identi�cados por uma String. Dentro desse contexto, o métodoexecutaComando() seria responsável por executá-lo e passar para ele asinformações necessárias para sua execução. Caso o comando não seja encon-trado no Map, uma exceção do tipo ComandoNaoEncontradoException

é lançada pelo método.

Listagem �.� - Implementação do Carrinho de Compras:

public class CarrinhoCompras{

private Map<String, ComandoCarrinho> comandos;private List<Produto> produtos;private Usuario usuario;

//outros métodos de carrinho de compras

public Object executaComando(String nomeComando)throws ComandoNaoEncontradoException{ComandoCarrinho c = comandos.get(nomeComando);if(c == null)

throw new ComandoNaoEncontradoException();if(c instanceof CienteDosProdutos){

((CienteDosProdutos) c).setlistaProdutos(produtos);}

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 215: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Adicionando operações

if(c instanceof CienteDoUsuario){((CienteDoUsuario) c).setUsuario(usuario);

}return c.executar();

}

public Set<String> getComandosDisponiveis(){return comandos.keySet();

}}

Uma característica interessante dessa solução é a utilização de interfacespara que o carrinho possa injetar as informações nos comandos. As inter-faces CienteDosProdutos e CienteDoUsuario são implementadaspelas classes que desejam respectivamente receber a lista de produtos eas informações do usuário. Isso permite às classes sinalizarem de qualinformação precisam para seu processamento e à classe CarrinhoCompras

saber qual informação ela precisa passar.

Listagem �.� - Exemplo de operação que calcula o tamanho para downloaddos produtos do carrinho:

public class TamanhoParaDownloadimplements ComandoCarrinho, CienteDosProdutos{

private List<Produto> produtos;

public void setListaProdutos(List<Produto> produtos){this.produtos =produtos;

}

public Object executar(){double tamanho = 0;for(Produto p : produtos){

if(p.isDigital()){tamanho += p.getTamanhoDownlaod();

}}

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 216: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Classes que representam comandos Casa do Código

return tamanho;}

}

Combinando o Command com outros padrões

Opadrão Command possui estrutura simples, até umpouco parecida comoutros padrões baseados em composição, como o Observer e o Strategy.Devido a essa simplicidade, muitas vezes acaba sendo fácil sua combinaçãocom outros padrões. Por exemplo, quando diversos comandos possuírempassos similares, a utilização de um Template Method como base do mé-todo de execução pode evitar a repetição de código. Devido à interface co-mum compartilhada pelos comandos, a criação de um Proxy que encapsuleum comando pode ser útil para a adição de funcionalidades em qualquer im-plementação.

Para que que novos comandos possam ser facilmente adicionados ao sis-tema, uma combinação comum é a utilização de um Dynamic Factory

ou de um Service Locator para o carregamento das implementações.Muitas vezes, a con�guração dos comandos é acompanhada de informaçõesa respeito das situações em que devem ser invocados. A listagem a seguir re-toma o exemplo da classe CarrinhoCompras e mostra como os comandospoderiam ser carregados dinamicamente no construtor da classe. No caso, opadrão Service Locator é utilizado a partir da classe ServiceLoader.

Listagem �.� - Utilização de um Service Locator para localizar os coman-dos:

public class CarrinhoCompras{

private Map<String, ComandoCarrinho> comandos;

public CarrinhoCompras(){comandos = new HashMap<>();carregarComandos();

}

public void carregarComandos(){

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 217: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Adicionando operações

}}

Uma outra situação que pode ocorrer é quando utilizamos o Command

e há passos similares a serem executados em diversos cenários. Por exem-plo, a solicitação de uma con�rmação do usuário ou a atualização de umatabela podem ser passos necessários para diferentes operações. Nesse caso,pode-se considerar a implementação de sequências de comandos, em que oacionamento de um comando resultará na execução de uma cadeia de outroscomandos ligados a ele. Somente pela palavra “encadeamento” já é possívelpensar no Chain of Responsibility!

Para exempli�carmos uma cadeia de comandos, considere a abstraçãoComandoCarrinho apresentada na seção anterior. Para implementar oencadeamento de comandos, iremos transformar a interface em uma classeabstrata, cujo código está apresentado na listagem a seguir. Essa classeabstrata possui o atributo proximo que representa o próximo comandoda sequência. O método executar(), que estava presente na interface,agora se tornou uma método concreto. Esse método invoca primeiramente ométodo abstrato executarComando(), no qual a funcionalidade daquelecomando deve ser inserida, e em seguida executa o próximo comando casoesse não seja nulo.

Listagem �.� - Interface que abstrai uma operação do carrinho de com-pras:

public abstract class ComandoCarrinho{private ComandoCarrinho proximo;

public ComandoCarrinho(ComandoCarrinho proximo){this.proximo = proximo;

}public void executar(){

executarComando();if(proximo != null)

proximo.executar();}

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 218: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Cenários de aplicação do Command Casa do Código

protected abstract void executarComando();public abstract Object getResultado();

}

Outra questão que precisou ser alterada para a implementação da sequên-cia de comandos foi a forma que o resultado do comando é disponibili-zado. Na implementação anterior, o retorno era dado pelo próprio métodoexecutar(). Nesse caso, a não ser que haja uma forma padrão de combinaros resultados da sequência, não é possível retornar o resultado no própriomé-todo. A solução dada nesse caso foi a de�nição de ummétodo abstrato para oretorno do resultado. Dessa forma, a classe do comando pode escolher comoo resultado será obtido e, caso seja aplicável, como ele será combinado com oresultado do comando executado na sequência.

C������ �������,��� ���� ���������� ���������

A combinação de padrões é um recurso muito poderoso, que podegerar uma sinergia que traz benefícios muito maiores do que a utiliza-ção isolada de cada padrão. Porém essa combinação deve ser feita comcuidado, pois a utilização de vários padrões em umamesma solução semnecessidade é uma má prática. Isso porque eles podem tornar a solu-ção complexa sem trazer nenhum benefício. Toda vez que pensar emuma solução commúltiplos padrões, minha sugestão é que você faça umexercício se perguntando qual o problema que cada um está resolvendo.Se caso não conseguir responder essa pergunta para algum deles, consi-dere remover aquele padrão da solução. Mesmo que você esteja erradoe o padrão acabe voltando, pelo menos você aprendeu mais sobre o seuproblema e pode inclusive considerar outras alternativas.

�.� C������� �� ��������� �� C������A seção anterior descreveu o padrão Command e apresentou o exemplo docarrinho de compras, no qual ele era utilizado para representar novas ope-rações a serem inseridas no carrinho. Existem outros cenários importantesonde esse padrão possui uma grande aplicabilidade. É interessante como em

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 219: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Adicionando operações

cada um esse padrão é implementado por ummotivo diferente, porém em to-dos eles a solução como Command é relacionada comouso de uma classe pararepresentar uma operação. As próximas subseções irão apresentar diferentesocasiões para o uso, mostrando exemplos de código de como implementá-los.

Execução remota

Uma das vantagens de se ter uma representação de uma operação do sis-tema é poder passar essa operação como parâmetro. Esse é o caso mostradona seção anterior quando havia um executor de comandos. Uma vantagemdessa estrutura é que esse executor pode estar localizado emumamáquina re-mota, oferecendo serviços para a execução desses comandos. Essa estruturapermite ter uma interface de serviços mais enxuta, provendo para clientesremotos a funcionalidade de execução de comandos.

Para exempli�car como um executor remoto de comandos poderia serimplementado, essa seção mostra o exemplo de um Session Bean que executacomandos remotamente. Primeiro, é preciso de�nir uma interface remotapara essa execução, como mostrado na listagem a seguir. Uma questãonotável nesse caso é a interface Comando estender Serializable parapoder ser enviada através da rede. Nesse caso, é importante que as imple-mentações tragam consigo todas as informações necessárias para a execução.

Listagem �.� - Interface remota pra execução de comandos:

@Remotepublic interface ExecutorComandos{

public Object executarComando(Comando c);}

Em seguida, precisamos implementar o Session Bean que implementaa interface remota de�nida. Isso é apresentado na listagem a seguir. Ométodo executarComando() executa o comando e em seguida retorna oresultado da execução que é recuperado de uma propriedade desse objeto.Esse método também pode injetar no comando recursos que estão presentesapenas no servidor, como o EntityManager mostrado no exemplo, queserve para acessar o banco de dados. Para isso, basta o comando implementar

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 220: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Cenários de aplicação do Command Casa do Código

a interface PrecisaEntityManager.

Listagem �.� - Session Bean para execução remota de comandos:

@Statelesspublic class ExecutorRemoto implements ExecutorComandos{

@PersistenceContextprivate EntityManager em;

public Object executarComando(Comando c){if(c instanceof PrecisaEntityManager){

((PrecisaEntityManager)c).setEntityManager(em);}c.executar();return c.getResultado();

}}

A implementação de um cliente para esse serviço acaba sendo muitosimples. A listagem a seguir mostra um método utilitário que poderia serusado para a execução de comandos em um servidor remoto. As proprieda-des passadas para o método servem, a grosso modo, para a localização doservidor e o parâmetro de nome jndiName para a localização do serviço deexecução de comandos. Como essas informações dependem do servidor e dasua versão, decidiu-se por passá-las como parâmetro. A partir da chamadadesse método o comando será enviado ao servidor e executado, e o resultadode sua execução enviado de volta e retornado.

Listagem �.� - Cliente que acessa o Session Bean para execução de umcomando no servidor:

public static Object executaComandoRemoto(Comando c,String jndiName, Properties props) throws NamingException{

InitialContext ic = new InitialContext(props);ExecutorComandos ec = ic.lookup(jndiName);return ec.executarComando(c);

}

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 221: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Adicionando operações

Aprincipal vantagemdessa solução é que�camuito simples adicionar no-vas funcionalidades para serem executadas no servidor. Normalmente, cadanova operação no servidor precisa da de�nição de um método na interfaceremota e na implementação para que seja consumido por seus clientes. Nasolução apresentada, apenas o método de execução de comandos precisa serexposto remotamente, bastando a criação de um novo comando para a intro-dução de uma operação.

Uma consequência negativa dessa interface mais geral é que isso facilitaa introdução de comandos maliciosos, abrindo uma brecha para execução defuncionalidades diversas no servidor. Apesar da classe do comando precisarestar presente no classpath do servidor para poder ser executada, é aconselhá-vel implementar no servidor funcionalidades de segurança visando autorizaro acesso apenas a comandos conhecidos.

Transações e Logging

O fato de as operações serem representadas por objetos e poderem ser ar-mazenadas permite que seja feito umhistórico dos comandos executados pelosistema. Ele pode ser persistido, servindo como um log do que foi executadopela aplicação. Isso também possibilita que essas operações sejam refeitas nocaso de uma queda do sistema, o que faz com que os comandos sirvam pararepresentar transações duráveis em sistemas de informação.

Para ilustrar o uso do Command para implementação de transações, seráutilizado como exemplo o framework Prevayler (http://prevayler.org) . Esseframework é uma implementação de código aberto que faz a persistência deobjetos em Java. Com a implementação do padrão Prevalent System

[��], ele mantém os objetos persistentes em memória e armazena as transa-ções para uma possível recuperação do sistema. De tempos em tempos, étirado um snapshot do objeto persistido, que é gravado serializado em disco.Nos intervalos entre essas gravações, as transações são armazenadas paraguardar as alterações realizadas.

Considere, por exemplo, que o objeto que está sendo persistido peloPrevayler seja uma lista e que uma das operações que podem ser feitas nessalista seja a inserção de um novo elemento. A listagem a seguir apresentacomo essa transação seria implementada. Observe que ela possui como

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 222: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Cenários de aplicação do Command Casa do Código

parte de sua estrutura de dados o item que será inserido, além do método deexecução de�nido na interface Transaction, no caso executeOn().

Listagem �.� - Exemplo de uma transação do Prevayler para adicionarum item em uma lista:

public classInserirItem implements Transaction<List<Serializable>>{

private Serializable item;

public InserirItem(Serializable item){this.item = item;

}public void executeOn(List<Serializable> lista, Date date){

lista.add(item);}

}

A utilização do Command para a de�nição de transações, nesse caso, per-mite que ela seja armazenada e recuperada se necessário. Caso a lista fossemodi�cada diretamente, seria preciso armazenar essa lista a cada alteraçãopara que elas não fossem perdidas. Como é possível persistir a alteração, épossível armazenar uma versão da lista e todos os comandos executados nelaa partir daquele ponto. Dessa forma, caso seja necessário recuperar sua úl-tima versão, basta recuperar a versão serializada com todas as transações apartir daquele ponto e executar as transações novamente.

Fazer e desfazer

Uma outra aplicação do Command é para dar suporte na aplicação àfuncionalidade de desfazer ações realizadas pelo usuário. Nesse caso, asoperações realizadas por ele devem ser armazenadas para poderem seracessadas e desfeitas. Para que isso seja possível, além de um método paraexecutar o comando, também é necessário um que reverta os seus efeitos. Alistagem a seguir mostra o exemplo de uma interface para a de�nição de umcomando com os métodos fazer() e desfazer().

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 223: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Adicionando operações

Listagem �.�� - Interface para comando que pode ser desfeito:

public interface Comando{public void fazer();public void desfazer();

}

Este cenário é um exemplo no qual uma classe para a execução decomandos se faz necessária. Seu papel é prover um local centralizado paraexecução e armazenamento dos comandos, para viabilizar que eles possamser recuperados e revertidos se necessário. A listagem a seguir apresenta umexecutor de comandos que permite que os comandos executados possam serdesfeitos e que os comandos desfeitos possam ser refeitos.

Listagem �.�� - Executor de comandos que possibilita que comandossejam desfeitos e refeitos:

public class ExecutorComandos {private Queue<Comando> feitas;private Queue<Comando> desfeitas;

public ExecutorComandos() {feitas = new LinkedList<>();desfeitas = new LinkedList<>();

}public void executarComando(Comando c) {

c.fazer();feitas.offer(c);desfeitas.clear();

}public void desfazer() {

if(!feitas.isEmpty()) {Comando c = feitas.pool();c.desfazer();desfeitas.offer(c);

}}public void refazer() {

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 224: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Double Dispatch - Me chama, que eu te chamo! Casa do Código

if(!desfeitas.isEmpty()) {Comando c = desfeitas.pool();c.fazer();feitas.offer(c);

}}

}

Essa classe possui duas pilhas que são chamadas de feitas edesfeitas. Quando um comando é executado, ele é empilhado na pilhafeitas. Quando o método desfazer() é invocado, o comando de cimada pilha feitas é desempilhado, invoca-se nele o método desfazer() eele é colocado na pilha desfeitas. Nesse momento, o usuário ainda tema oportunidade de voltar atrás com a chamada do método refazer(), oqual desempilha um comando da pilha desfeitas, executa o comando eo adiciona na pilha feitas. Porém, como só faz sentido refazer um co-mando logo que foi desfeito, sempre que um novo comando é executado, apilha desfeitas é esvaziada.

Emumexecutor de comandosmais so�sticados, poderia haver comandosque não podem ser defeitos ou comandos inertes, ou seja, que não deveriamser incluídos na pilha de comandos realizados. Com pequenas modi�cações,a implementação apresentada poderia ser adaptada para esse tipo de requi-sito. Nesse caso de haver diversos tipos de comando, diferentes interfaces queexpressam as possíveis operações de cada um deles poderiam ser utilizadaspara diferenciação. Outra alternativa seria a utilização de uma interface únicae de anotações para marcar os comandos que precisam ser tratados de formadistinta.

�.� D����� D������� - M� �����, ��� �� ��������

“Não ligue para nós, nós ligamos para você!”– Princípio de Hollywood

Uma situação que muitas vezes ocorre no cotidiano é uma pergunta serrespondida com outra pergunta. Imagine que uma garota questione o seu

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 225: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Adicionando operações

namorado: “Eu vi você dentro da boate na sexta a noite! O que você estavafazendo lá?” e ele responda: “Se você me viu você também estava lá! O que vocêestá fazendo lá?”. Brincadeiras à parte, uma técnica importante que pode serutilizada em umprojeto orientado a objetos é fazer com que uma chamada demétodo seja respondida com a chamada de outro método no objeto passadocomo parâmetro.

Para ilustrar o problema, vamos retomar o exemplo do carrinho de com-pras, porém considerando que diversos tipos de produtos possam ser adici-onados no carrinho em uma mesma loja. Dependendo do tipo de produto,diferentes propriedades precisam sermodi�cadas. Umamúsica, por exemplo,adicionaria seu preço no valor total e seu tamanho no total para download. Jáum produto físico, além do preço, adicionaria seu peso e volume, que seriamutilizados posteriormente para o cálculo do frete. Além disso, outros tipos deproduto podem surgir, como a venda de passagens aéreas ou entradas paraeventos. Vamos ver nessa seção como seria a solução sem a utilização de pa-drões e depois com o uso do Double Dispatch [��].

Resolvendo o problema do carrinho sem padrões

O primeiro instinto para a implementação da adição de diferentes tiposde produtos seria criar condicionais para selecionar entre os vários tipos deproduto. Nesse caso, o tipo do produto, através da sua classe, seria utilizadopara diferenciá-los. A listagem a seguir mostra o exemplo de como �caria ocódigo dessa solução. A partir dela, a cada novo tipo de produto, um novocondicional precisaria ser adicionado nesse método.

Listagem �.�� - Usando condicionais para identi�car o tipo do produto:

public class CarrinhoCompras {//outros métodos e atributos

public void adicionarProduto(Produto p) {produtos.add(p);if(Produto instanceof ProdutoDigital) {

ProdutoDigital pd = (ProdutoDigital) p;adicionaPropriedade("PRECO", pd.getPreco());adicionaPropriedade("DOWNLOAD", pd.getTamanho());

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 226: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Double Dispatch - Me chama, que eu te chamo! Casa do Código

} else if(Produto instanceof ProdutoFisico) {ProdutoFisico pf = (ProdutoFisico) p;adicionaPropriedade("PRECO", pf.getPreco());adicionaPropriedade("VOLUME", pf.getVolume());adicionaPropriedade("PESO", pf.getPeso());

} else {//mais condicionais para cada tipo de produto

}}

}

Uma forma de eliminar os condicionais seria a criação de um métodopara a adição de cada tipo de produto, como mostrado na próxima listagem.Como em Java pode-se fazer a sobrecarga de métodos, é possível que todospossuam o mesmo nome. Dessa forma, o método seria selecionado deacordo com o tipo de produto passado para ele. Apesar de essa soluçãoeliminar os condicionais, ainda seria necessária a adição de novos métodosna classe CarrinhoCompras para a adição de novos tipos produtos.

Listagem �.�� - Usando um método diferente para cada tipo de produto:

public class CarrinhoCompras {//outros métodos e atributos

public void adicionarProduto(ProdutoDigital pd) {produtos.add(pd);adicionaPropriedade("PRECO", pd.getPreco());adicionaPropriedade("DOWNLOAD", pd.getTamanho());

}public void adicionaProduto(ProdutoFisico pf) {

produtos.add(pf);adicionaPropriedade("PRECO", pf.getPreco());adicionaPropriedade("VOLUME", pf.getVolume());adicionaPropriedade("PESO", pf.getPeso());

}//outros métodos para cada tipo de produto

}

O problema aqui é que a adição de um novo tipo de produto na verdade é

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 227: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Adicionando operações

uma nova operação para a classe CarrinhoCompras, pois cada um possuiuma lógica diferente. Ambas as implementações apresentadas exigem quemais código seja adicionado na classe para a adição ao suporte de um novotipo de produto. Como permitir isso sem alterar a classe?

Aplicando o Double Dispatch no carrinho de compras

Como talvez você descon�e pela introdução, a melhor forma de respon-der essa pergunta é devolver a pergunta! Nesse caso, a pergunta que estásendo feita para o carrinho é o que ele precisa fazer quando um determinadotipo de produto for adicionado. Então, uma forma de devolver essa perguntaseria ele questionar ao produto qual propriedade ele deve adicionar no carri-nho. A listagem a seguir mostra como �caria a classe CarrinhoCompras

com a implementação dessa solução.

Listagem �.�� - Utilizando o Double Dispatch no carrinho de compras:

public class CarrinhoCompras {//outros métodos e atributos

public void adicionarProduto(Produto p) {produtos.add(p);p.adicionaPropriedades(this);

}}

Observe que o método adicionarProduto() recebe uma instân-cia da classe Produto como parâmetro e devolve para essa instância ainvocação do método adicionaPropriedades(), passando-se comoparâmetro. Abaixo segue o exemplo de como seria a implementação daclasse ProdutoDigital, que agora tem a responsabilidade de adicionarsuas responsabilidades no carrinho. Observe que o método recebe o própriocarrinho de compras como parâmetro e adiciona as propriedades adequadas.Caso alguma outra modi�cação no CarrinhoCompras fosse necessária,ela também poderia ser feita nesse método.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 228: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Double Dispatch - Me chama, que eu te chamo! Casa do Código

Listagem �.�� - Exemplo de tipo de produto com o uso do Double Dis-patch:

public class ProdutoDigital extends Produto {//atributos de um produto digital

@Overridepublic void adicionaPropriedades(CarrinhoCompras c) {

c.adicionaPropriedade("PRECO", getPreco());c.adicionaPropriedade("DOWNLOAD", getTamanho());

}}

Essa é justamente a ideia da estrutura do padrão Double Dispatch:a classe devolve a chamada de método para o parâmetro que recebe. Assim,dependendo do tipo passado como parâmetro, o método invocado terá umaimplementação diferente. Observe que antes da implementação do padrão,era necessária a modi�cação da classe CarrinhoCompras, por meio da adi-ção de um método ou da criação de um condicional, para dar suporte a umnovo tipo de produto. Sendo assim, a cada novo tipo de produto, é como setivéssemos adicionando um novo tipo de operação na classe.

Estrutura do Double Dispatch

Se formos traduzir de forma literal do inglês, o nome do padrão Double

Dispatch signi�ca “despacho duplo”. Nesse contexto, a palavra “despacho”signi�ca a delegação de parte da funcionalidade da classe para outra. A adi-ção da palavra “duplo” signi�ca que essa delegação ocorre duas vezes. Sendoassim, o nome do padrão acaba ressaltando sua principal característica, que éa da classe que recebe a chamada do método devolvê-la para um objeto rece-bido comoparâmetro, o qual irá executar uma operação na classe que chamouo método.

A �gura �.� representa a estrutura do padrão. Uma característica que pre-cisa ser ressaltada é a dependência cíclica entre as classes Despachante eElemento, em que cada classe possui um método que recebe a outra comoparâmetro. Apesar disso, é importante notar que mesmo que Despachante

dependa de Elemento, ela não depende de nenhuma de suas implementa-

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 229: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Adicionando operações

ções. Dessa forma, qualquer uma delas pode ser passada como parâmetro enovas implementações podem ser facilmente incorporadas.

Fig. �.�: Estrutura básica do Double Dispatch

Uma visão dinâmica desse processo do Double Dispatch está na�gura �.�. No primeiro caso, quando o elemento é passado como parâ-metro, ele recebe uma chamada da classe Despachante e executa umaoperação nessa classe. No segundo caso, quando um elemento diferenteé passado como parâmetro, operações diferentes são executadas na classeDespachante. Isso faz com que seja possível determinar a operação a serexecutada a partir do parâmetro passado.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 230: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Padrão Visitor Casa do Código

Fig. �.�: Visão dinâmica do funcionamento do Double Dispatch

Cada classe diferente passada como parâmetro é como se fosse um mé-todo diferente sendo executado na classe Despachante. Sendo assim, oDouble Dispatch é um padrão que pode ser utilizado quando se desejaacrescentar novas operações em uma classe de forma dinâmica. Para isso,basta implementar a interface e criar a nova operação no método que é cha-mado de volta pela classe.

�.� P����� V������“O Visitor é um padrão de que você di�cilmente precisa, mas, quando precisa,nenhuma outra solução resolve o problema.”– Joseph Yoder

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 231: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Adicionando operações

O padrão Visitor naminha opinião é um dos mais difíceis de ser com-preendidos e aplicados do GoF [��]. Em cursos sobre modelagem orientadaa objetos que ministro, algumas vezes �z um workshop de padrões, em quecada aluno apresentava umpadrão. Commuita frequência, quemapresentavao Visitor acabava se confundindo e entendendo errado sua idea. Muitosdos exemplos que encontravam na Internet eram tão arti�ciais que acabavamilustrando somente a estrutura, mas não a motivação para o uso do padrão.

O Visitor utiliza em sua estrutura o Double Dispatch, visto naseção anterior. A compreensão desse padrão certamente já é um passo im-portante para a compreensão do Visitor. A próxima seção irá apresentar oexemplo de um problema que di�cilmente teria uma solução e�ciente sem autilização desse padrão. Em seguida serámostrado como ele funciona e comopoderia ser utilizado para resolver o problema.

D��������� �������� ����� �������

Até omomento já vimos diversas formas de relacionamento entre pa-drões. O exemplo mais simples seria quando dois padrões podem serutilizados em conjunto para a solução de um problema. Outro exemploseria quando dois ou mais fornecem soluções alternativas para o mesmoproblema. No caso do Visitor e do Double Dispatch temos umnovo tipo de relação: quando a solução de um padrão é utilizada comoparte da solução de outro.

Por mais que isso possa parecer estranho, esse tipo de relação nomundo dos padrões é natural. Existem padrões mais fundamentais emais gerais que são utilizados por outros que resolvem problemas maisespecí�cos. No livro “Implementation Patterns” [�], por exemplo, sãoapresentadas diversas práticas mais granulares que acabam sendo utili-zadas dentro dos padrões apresentados nesse livro. É importante ressal-tar que ainda existem várias outras formas de relacionamento entre eles[��], além das apresentadas nesse livro.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 232: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Padrão Visitor Casa do Código

Vários relatórios em diversos formatos

O exemplo dessa seção irá utilizar como contexto um so�ware que pre-cisa gerar diversos tipos de relatório em diversos formatos. Em relação aostipos de relatório, cada um possui informações e uma lógica de montagemdiferente. Podemos ter um que sumariza as compras de um cliente e outroque mostra as vendas mensais de um determinado produto. Outras vezes,a mesma informação pode ser exibida de formas diferentes, como com grá-�cos ou com tabelas. Sendo assim, apesar de os elementos de um relatórioserem os mesmos, como texto, tabelas, títulos e grá�cos, suas informações esua estrutura são diferentes.

Em relação ao formato em que o relatório será gerado, também existemvárias possibilidades. Exemplos de formatos que podem ser necessários sãoem HTML para visualização em browsers, em PDF para permitir uma me-lhor leitura e impressão, planilha para permitir uma manipulação posteriordos dados e até mesmo em XML para que o relatório possa ser interpretado eexibido por outras aplicações. Para a geração em cada um dos formatos, umaAPI diferente precisa ser utilizada pela aplicação, como o iText para PDF eo Jakarta POI para planilhas. Sendo assim, mesmo que os elementos dos re-latórios sejam os mesmos, a geração de cada um deles em cada plataforma écompletamente diferente.

A �gura �.� ilustra a relação de muitos para muitos entre relatórios e osformatos. Como cada um possui informações diferentes, �ca difícil reutilizaro código de geração de um para outro. Adicionalmente, como cada API criaos elementos de forma diferente, também não é possível reutilizar o códigode geração para diferentes formatos dentro do mesmo relatório.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 233: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Adicionando operações

Fig. �.�: Relacionamento entre os tipos de relatório e os tipos de saída

Como essa reutilização de código não é direta, a solução comum nessecaso seria cada um dos relatórios possuir ummétodo para a geração em cadaformato, como mostrado na �gura �.�. Dessa forma, a cada novo relatório,precisaria ser criado ummétodo para a sua geração em cada umdos formatos.Se um novo formato fosse introduzido, isso também demandaria a criação deum novo método em cada classe de relatório.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 234: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Padrão Visitor Casa do Código

Fig. �.�: Estrutura da solução do problema do relatório sem o uso de padrões

O problema não para na introdução de novos elementos, mas tambémaparece na manutenção dos formatos e relatórios já existentes. Imagine, porexemplo, que como um novo requisito do usuário seja necessário a adiçãodo logo da empresa no início dos relatórios em PDF. Isso vai demandar umaatualização em todas as classes de relatório. Por outro lado, se fosse neces-sária a introdução de uma nova informação no relatório, todos os métodosprecisariam ser atualizados.

Introduzindo o Visitor

Para começar a explicar o Visitor, vou utilizar uma metáfora que ilus-tra bem como o padrão funciona. Imagine que existam duas pintoras, MariaEduarda e Ana Beatriz, sendo que a primeira possui um estilo de pinturamaisclássico e a segunda um estilo mais cubista. Considere também que elas vãovisitar dois locais diferentes, sendo um deles uma favela e o outro uma casano campo. Quando forem na favela, será pedido a elas “pinte essa favela” e

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 235: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Adicionando operações

quando forem a casa de campo será pedido “pinte essa casa de campo” .A �gura �.� apresenta como foi o resultado das visitas das pintoras aos

locais. Apesar do pedido feito a elas ao visitarem um local ser o mesmo, oresultado do pedido, no caso o quadro, será totalmente diferente. Como cadapintora possui um estilo diferente, a execução domesmo pedido será distinta.Sendo assim, o resultado �nal depende tanto do local que está sendo visitado,quanto da pintora que o está visitando.

Fig. �.�: Visita de pintoras diferentes em locais diferentes

O Visitor funciona de forma análoga ao exemplo das pintoras. Quando

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 236: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Padrão Visitor Casa do Código

esse padrão é utilizado, existe umahierarquia de classes que representa os visi-tantes e uma outra que representa elementos que aceitam visitantes. Quandoum elemento recebe um visitante, solicita uma tarefa a ele, passando-se comoparâmetro ou passando alguma informação que possui internamente. Assim,cada classe que implementa a abstração de visitante irá realizar essa tarefa deum modo diferente e cada implementação de um elemento irá solicitar algodiferente aos visitantes.

A estrutura do Visitor está apresentada na �gura �.�. A interfaceVisitante de�ne quais métodos devem ser implementados pelos visitan-tes. De forma complementar, a interface Elemento de�ne um métodopara aceitar uma visita. Desse modo, durante ela, cada implementação deElemento irá chamar um método de Visitante diferente. De maneiraanáloga ao exemplo das pintoras, o resultado depende tanto do elemento vi-sitado quanto do visitante. É como se cada implementação de Visitante

fosse uma nova operação sendo adicionada para o conjunto de classes queimplementam Elemento.

Fig. �.�: Estrutura do padrão Visitor

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 237: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Adicionando operações

A representação clássica do Visitor é como a apresentada na �gura �.�,na qual a interface Visitante possui um método para cada implementa-ção da interface Elemento. Assim �ca bem claro qual método uma deter-minada implementação irá chamar; porém, para cada nova implementaçãode Elemento, um novo método precisaria ser adicionado em cada um dosvisitantes. Dessa forma, �caria fácil adicionar um novo visitante, mas não umnovo elemento.

Apesar do Visitor ser frequentemente retratado com essa estrutura,nem sempre os métodos de�nidos na interface Visitante precisam re�etiras implementações de Elemento. Essesmétodos podemde�nir as operaçõesque podem ser feitas por um visitante e o que vai diferenciar cada elementoserá a sequência que irão chamar e os parâmetros que irão passar para cadamétodo. Na próxima seção, o exemplo da geração de relatórios em diferentesformatos irá utilizar essa abordagem.

O diagrama de sequência da �gura �.�mostra como um cliente invocariauma operação a partir do Visitor. Inicialmente o cliente criaria a instânciada implementação desejada da interface Visitante e a passaria como parâ-metro para o método aceitar() de um Elemento. Dentro desse método,o método visitar() do Visitante seria invocado passando a própriaclasse ou informações como parâmetro. Esse método por sua vez, invocariamétodos na classe passada como parâmetro. Por esse diagrama de sequência�ca bem claro a utilização do Double Dispatch como parte da solução doVisitor.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 238: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Padrão Visitor Casa do Código

Fig. �.�: Sequência de invocação em um Visitor

A principal diferença entre o Visitor e o Double Dispatch está nahierarquia dos elementos. No Double Dispatch, operações podem seradicionadas através da passagem de uma implementação diferente como pa-râmetro. A �exibilidade está no método do tipo do parâmetro para o qual édelegada a execução. Já no Visitor, existe também diversas possibilidadesde implementação para a classe que aceita o parâmetro. Dessa forma, cadaimplementação pode invocar métodos diferentes no objeto recebido comoargumento.

Fazendo os formatos de saída visitarem os relatórios

Depois de ver como funciona o Visitor, esta seção apresenta como elepode ser utilizado no problema da geração de relatórios em diversos forma-tos. Dentro do contexto do padrão, o relatório representará o elemento, e oformato do relatório será o visitante. Quando o método de geração do relató-rio for chamado passando o visitante como parâmetro, os seus métodos serãochamados com os dados do relatório, de forma a criar a sua estrutura.

A listagem a seguir apresenta a interface que de�ne os métodos que um

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 239: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Adicionando operações

visitante deve implementar. Observe que cada método representa uma seçãodo relatório, com a exceção de getResultado() que é utilizado no �nalpara retornar o resultado da geração. Dessa forma, cada implementaçãodeve criar uma lógica que crie a seção do relatório adequada. Por exemplo,enquanto um relatório irá gerar um título dentro de um documento PDF,o outro irá criar as tags para a representação do título em uma páginaHTML.

Listagem �.�� - Interface implementada pelas classes que representamum formato de arquivo:

public interface FormatoVisitante{public void visitarTitulo(String t);public void visitarSubtitulo(String t);public void visitarParagrafo(String p);public void visitarTabela();public void visitarTabelaCabecalho(String... ct);public void visitarTabelaLinha(Object... o);public void visitarTabelaFim();public void visitarImagem(String path);public Object getResultado();

}

Conforme a listagem a seguir, a interface Relatorio, ao invés depossuir diversos métodos como na solução representada na �gura �.�, agorapossui apenas um método chamado gerarRelatorio(). Esse métodorecebe uma classe do tipo FormatoVisitante como parâmetro, que équem irá determinar o formato de geração do relatório. Enquanto a soluçãoanterior possuía um método para cada formato, nesse caso, cada tipo deFormatoVisitante passado como parâmetro pode ser utilizado como ade�nição de uma nova operação nessa classe.

Listagem �.�� - Interface implementada pelos relatórios:

public interface Relatorio{public Object gerarRelatorio(FormatoVisitante fv);

}

Para ilustrar a geração de um relatório, a listagem a seguir mostra como

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 240: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Padrão Visitor Casa do Código

seria criado um relatório que listasse as compras de um cliente. Observeque a criação das seções do relatório, como título, parágrafo e tabela, se dáa partir da chamada dos métodos do objeto do tipo FormatoVisitante

passado como parâmetro. Dessa forma, cada relatório diferente iria chamaresses métodos na ordem necessária, passando seus dados como parâmetro.Nesse caso, não existe um método especí�co para cada tipo de relatório.

Listagem �.�� - Implementação da geração de um relatório:

public class ComprasCliente implements Relatorio {private Cliente c;private List<Item> items;

//outros métodos

public Object gerarRelatorio(FormatoVisitante fv) {fv.visitarTitulo("Compras de "+c.getNome());fv.visitarParagrafo("CPF "+ c.getCPF());fv.visitarParagrafo("Cliente desde " +

c.getDataCadastro());fv.visitarTabela();fv.visitarTabelaCabecalho("Produto", "Data", "Valor");for(Item i : items) {

fv.visitarTabelaLinha(i.getProduto(),i.getDataCompra(), i.getValor());

}fv.visitarTabelaFim();return fv.getResultado();

}}

Ainda exempli�cando a implementação das interfaces, a listagem a seguirmostra como seria uma implementação de FormatoVisitante para ageração de relatórios em HTML. A variável sb, do tipo StringBuilder, éutilizada para guardar o relatório entre as chamadas demétodo. Dessa forma,a cada chamada de método, uma nova parte do relatório é adicionada aoStringBuilder. De forma similar, outras implementações armazenariamo relatório que estaria sendo gerado em uma variável, e iriam agregando as

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 241: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Adicionando operações

seções a ele a medida que os métodos fossem chamados.

Listagem �.�� - Implementação do formato HTML para relatórios:

public class VisitanteHTML implements FormatoVisitante {private StringBuilder sb = new StringBuilder();

public void visitarTitulo(String t){sb.append("<h1>"+t+"</h1>");

}public void visitarSubtitulo(String t){

sb.append("<h2>"+t+"</h2>");}public void visitarParagrafo(String p){

sb.append("<p>"+t+"</p>");}public void visitarTabela(){

sb.append("<table>");}public void visitarTabelaCabecalho(String... ct){

sb.append("<tr>");for(String s : ct)

sb.append("<th>"+s+"</th>");sb.append("</tr>");

}public void visitarTabelaLinha(Object... obs){

sb.append("<tr>");for(Object o : obs)

sb.append("<td>"+o.toString()+"</td>");sb.append("</tr>");

}public void visitarTabelaFim(){

sb.append("</table>");}public void visitarImagem(String path){

sb.append("<img src=’"+path+"’>");}public Object getResultado(){

return sb.toString();

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 242: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Padrão Visitor Casa do Código

}}

Para �nalizar o exemplo, a listagem a seguir apresenta o código que seriausado para a geração de um relatório. Inicialmente as classes do relatório edo formato precisariam ser criadas. De alguma forma as informações seriamrecuperadas, estando isso fora do escopo do exemplo. E então para a suageração, bastaria a chamada do método gerarRelatorio() passando aimplementação do visitante como parâmetro.

Listagem �.�� - Código cliente para a geração de relatórios:

Relatorio r = new ComprasCliente();//carrega dados no relatório

FormatoVisitante fv = new VisitanteHTML();String resultado = (String) r.gerarRelatorio(fv);

Essa solução permite que novos relatórios sejam facilmente adicionados,não importando o formato de geração, e que novos formatos sejam adicio-nados, já funcionando para todos os relatórios. A grande di�culdade de ma-nutenção do Visitor ocorre quando é necessária a inserção de um novométodo na interface da classe visitante. Nesse caso, isso aconteceria se fossenecessária a geração de um novo tipo de seção para os relatórios. A di�cul-dade ocorreria devido ao fato de ser necessária a criação de um novo métodoque precisaria ser adicionado em todas as implementações.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 243: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Adicionando operações

E�������� ��������� �� V������

Um padrão que se encaixa muito bem com o Visitor é oComposite. Nesse caso, o elemento que recebe o visitante seria com-posto por outros elementos que também podem receber o visitante.Nesse caso, o elemento composto poderia realizar chamadas próprias novisitante e delegar parte da execução para os elementos que o compõe,passando o visitante para que eles realizem as próprias chamadas.

Dentro do exemplo dos relatórios, um deles poderia ser compostopor outros. Dessa forma, uma parte que fosse recorrente, poderia sermodelada como um relatório à parte e utilizada para compor outros re-latórios. Sendo assim, o método que aceita um visitante, além de ser im-plementado pelo elemento principal, também pode ser implementadopelas classes que o compõem.

�.� C������������ ������ �� ��������Esse capítulo abordou um problema importante para o desenvolvimento deaplicações, que é a adição de operações de forma dinâmica. Em alguns casos,a adição de operações emobjetos faz parte do próprio negócio e a estrutura dosistema precisa permitir a sua evolução nesse sentido, sem que seja necessáriaa modi�cação frequente das classes envolvidas.

O primeiro padrão apresentado nesse capítulo foi o Command. Ele con-siste no encapsulamento de uma operação como uma classe, em vez de suarepresentação através de ummétodo. A partir dessa estrutura, é possível com-por as classes dinamicamente com comandos, tornando possível a adição denovas operações. Outra possibilidade trazida por esse padrão é de armazenaros comandos já executados, dessa forma pode-se utilizá-los para a implemen-tação de transações e auditoria, e até mesmo guardá-los para que possam serdesfeitos.

Outros padrões para a adição de operações vistos nesse capítulo foram oDouble Dispatch e o Visitor. Esses dois utilizam o recurso de delegar aexecução, ou parte dela, para o objeto passado como parâmetro. Dessa forma,

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 244: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Considerações �nais do capítulo Casa do Código

o comportamento é determinado pelo parâmetro passado, possibilitando aadição de novas operações a partir da criação de novas abstrações do tipo doparâmetro. Particularmente o Visitor é um padrão complicado e difícil deser implementado, porém nas situações em que ele é aplicável, os ganhos nasua utilização em termos de reúso e manutenção podem ser imensos.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 245: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

C������� �

Gerenciando muitos objetos

“Ninguém é dono da multidão, ainda que creia tê-la dominada.”– Eugène Ionesco

Existem hoje so�wares imensos, commilhões de linhas de código! Algu-mas vezes pergunto às pessoas se elas sabem como alguém consegue trabalharem uma base de código com milhares de classes e milhões de linhas de có-digo. Quando eu faço isso, alguns coçam a cabeça e tentam imaginar que tipode mente consegue visualizar, abstrair e trabalhar com tamanha quantidadede informações. A verdade é que, se o so�ware for bem modelado, ninguémprecisa conhecer tudo, somente a parte em que está trabalhando e as suasinterfaces com o resto.

Porém, mesmo utilizando os padrões que vimos até agora, a quantidadede implementações pode aumentar muito. Isso pode acontecer principal-mente quando temos classes granulares, ou quando padrões como Command,

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 246: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Criando uma fachada para suas classes Casa do Código

Strategy e Observer são utilizados. Nesses padrões, são de�nidas inter-faces que normalmente de�nem apenas um método, que pode acabar pos-suindo muitas implementações pequenas. Para os clientes dessas classes, anecessidade de conhecimento de todas as classes com que ele pode interagirpode se tornar um verdadeiro pesadelo.

Os padrões que serão apresentados nesse capítulo focam em simpli�car etornar escalável o relacionamento entre uma grande quantidade de classes eobjetos do sistema. A ideia principal é simpli�car a interface entre as classes,tornando mais fácil a interação entre elas. Outras vezes o problema não estáexatamente na quantidade de classes, mas na quantidade de objetos que éinstanciada. O último padrão desse capítulomostra como é possível diminuire controlar essa quantidade de objetos, otimizando a memória dispendidacom eles.

�.� C������ ��� ������� ���� ���� �������“A ignorância é uma espécie de bênção.”– John Lennon

É muito comum que uma determinada funcionalidade de um sistema re-sulte da colaboração de diversos componentes. Por exemplo, ao se concluiruma compra em um sistema de e-commerce, pode ser necessário interagircom componentes responsáveis pelo estoque, pelo controle �nanceiro e peloempacotamento e envio de mercadorias. Responsabilizar o cliente desses ser-viços por interagir com essas classes de forma correta pode causar problemas.Se existirem dois tipos de cliente, por exemplo mobile e web, essa mesma ló-gica precisará ser replicada nos dois. Isso di�culta mudança nessa lógica eacopla o cliente a diversos componentes.

Quando os padrões são utilizados na criação de uma solução, muitas ve-zes diversas classes auxiliares vão colaborar. Por exemplo, as classes podemser encapsuladas comum Proxy, podem ser compostas por outras classes, oumesmo receber um Visitor como parâmetro. Ter o conhecimento de quaisclasses devem ser criadas e inseridas na composição muitas vezes é compli-cado para os clientes. Isso não apenas os acopla a implementações especí�cas,

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 247: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Gerenciando muitos objetos

como também à estrutura da solução adotada, di�cultando possíveis refato-rações futuras.

Em outras palavras, o grande problema aqui é complexidade e acopla-mento! Não é desejável que os clientes das classes precisem conhecer todosseus detalhes estruturais ou se acoplar a diversas implementações. O padrãoFacade (pronuncia-se façade por ser uma palavra de origem francesa) pro-põe a criação de uma classe intermediária que serve como uma fachada paraque o cliente possa acessar as funcionalidades desejadas. Essa classe encap-sula a complexidade da interação entre os diversos componentes e desacoplao cliente das implementações.

Estrutura do Facade

A estrutura do Facade é bem simples e está representada na �gura �.�.Deve ser criada uma classe, apresentada na �gura como Fachada, que re-cebe as chamadas dos clientes e delega para as classes apropriadas de um sub-sistema. Por mais que pareça que o Facade é um simples redirecionadorde chamadas, ele pode ter importantes responsabilidades relativas à coorde-nação e orquestração dessas chamadas. Dessa forma, as classes clientes irãointeragir apenas com a fachada, que os isola da complexidade das classes dosubsistema.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 248: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Criando uma fachada para suas classes Casa do Código

Fig. �.�: Estrutura do Facade

Diversos padrões vistos nesse livro deixama estrutura de classesmais bemorganizada, mais �exível e pronta para evoluir. Porém isso tem um custo decomplexidade com que os clientes precisam lidar ao criarem e trabalharemcom essa estrutura de classes. Por mais que os padrões de criação vistos noscapítulos � e � sejam utilizados, os clientes ainda precisam con�gurar as de-pendências ou as fábricas. Nesse contexto, o Facade encapsula essa comple-xidade e fornece um método simples que provê aos clientes a funcionalidadedesejada.

A partir desse padrão é possível isolar um conjunto de classes do restoda aplicação, deixando a fachada como o único ponto de contato. Com essaestrutura de�ne-se um subsistema que pode evoluir de forma independentee inclusive ser reutilizado em outros contextos. Com essa estrutura modula-rizada é possível particionar a arquitetura de uma aplicação, organizando oscomponentes e tornando a manutenção mais simples.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 249: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Gerenciando muitos objetos

I������� API� �������� � ����������

Quandoutilizamos umaAPI externa ouum framework, eles precisamser adaptados às necessidades da aplicação. Por serem mais gerais e comobjetivo de atender um grande número de aplicações, a quantidade decon�gurações que precisam ser feitas e de classes com as quais precisa-seinteragir pode ser maior do que o necessário para as necessidades da suaaplicação. Dessa forma, um Facade pode ser criado para isolar a utiliza-ção dessa API ou framework, provendo para a aplicação apenas métodosnamedida das suas necessidades. Isso desacopla a aplicação da API utili-zada e torna mais simples a interação com aquelas funcionalidades parao resto sistema.

Imagine, por exemplo, uma aplicação que precise acessar um cartãocom certi�cado digital para executar suas funcionalidades. AAPI que in-terage com as funcionalidades de criptogra�a é preparada para lidar comdiferentes tipos de cartão e algoritmos criptográ�cos. Porém, a aplicaçãogeralmente não precisa de toda essa �exibilidade, pois o tipo de cartãoque ela irá utilizar já está pré-estabelecido, assim como o algoritmo crip-tográ�co. Dessa forma, utilizando um Facade para prover os serviçosnecessários para o resto da aplicação, além de permitir que desenvolve-dores possam utilizar a funcionalidade sem conhecer a API, facilita-se amanutenção caso alguma mudança seja necessária nessa parte.

De uma certa forma, a estrutura do Facade se parece umpouco comadoAdapter. Uma das diferenças é que o Facade interage com diversas classesenquanto o Adapter normalmente encapsula apenas uma classe, apesar deisso não ser uma regra obrigatória. Outra diferença é que o Adapter nor-malmente adapta para uma interface conhecida, que o cliente já utiliza porser implementada por outras classes, e o Facade de�ne um novo conjuntode métodos com o objetivo de simpli�car a interação.

Refatorando para o Facade

Anecessidade do Facademuitas vezes não é percebida no início do pro-

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 250: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Criando uma fachada para suas classes Casa do Código

jeto. Isso ocorre porque as interações entre as classes começam de forma sim-ples e objetiva, mas com a evolução do projeto acabam se espalhando paradiversas classes e utilizando diversas classes e métodos. Dessa forma, quandoo código da aplicação começa a se entrelaçar demais com o de um subsistemae isso passa a di�cultar a manutenção, é hora de criar um Facade para isolaruma parte da outra.

O passo básico dessa refatoração é simples e está ilustrado na �gura �.�.O primeiro passo da refatoração é identi�car o ponto na classe cliente que fazacesso aos subsistemas e extrair ummétodo com essa lógica. A princípio essemétodo �ca na própria classe, mas o segundo passo da refatoração é justa-mente movê-lo para o Facade. Dessa forma, o acesso ao subsistema �caráconcentrado na fachada, isolando-os das classes clientes. Por mais que possaparecer simples, esse procedimento precisa ser repetido para todos os pontosem que o subsistema é acessado.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 251: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Gerenciando muitos objetos

Fig. �.�: Refatorando para a Criação de um Facade

Àmedida que o código for sendo movido para a fachada, pode ser neces-sário refatorar esse código, que a princípio estava distribuído entre as classes, eagora está concentrado em apenas uma. Por exemplo, pode ser que dois mé-

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 252: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Criando uma fachada para suas classes Casa do Código

todos movidos para essa classe possuam um código muito parecido. Nessecaso, pode ser que valha a pena introduzir um parâmetro e unir os métodos,ou extrair um método auxiliar comum utilizado pelos dois. A di�culdade darefatoração para a criação de um Facade vai depender de quanto o códigode acesso ao subsistema está espalhado. Para essa tarefa, vale a pena utilizaralguma ferramenta que detecte as dependências entre as classes, para que pos-sam ser localizados os pontos que dependem do subsistema que está sendoisolado.

Criando uma fachada para o gerador de arquivos

O exemplo do componente gerador de arquivos é um caso no qual foramcriadas diversas classes para solucionar o problema de gerar um arquivo apartir de um mapa de propriedades. Para utilizar a solução é preciso sabera subclasse de GeradorArquivo correta para de�nir o tipo de arquivo ecompô-la corretamente com os pós-processadores. É claro que com apenasdois tipos de cada, não é tão difícil assim de conhecer as implementações,porém conforme o número de implementações aumentasse, esse componentepoderia �car difícil de utilizar.

Um exemplo de como poderia ser criada uma Facade para o gerador dearquivos está apresentado na listagem a seguir. Cadamétodo temumobjetivobem especí�co descrito pelo seu nome e, a partir da fachada, as funcionalida-des do gerador de arquivo podem ser invocadas diretamente, sem a necessi-dade de con�gurações e da criação de classes adicionais. No exemplo, foramcriados métodos para a geração das combinações entre os formatos de ar-quivo e os tipos de pós-processamento, sendo que o caso do pós-processadorcomposto não foi incluído. Dentro do contexto de uma aplicação, seriam in-cluídos no Facade somente os métodos que realmente fossem ser utilizadospor ela.

Listagem �.� - Exemplo de fachada para gerador de arquivos:

public class GeradorArquivoFacade{public void gerarXMLCompactado(

String nome, Map<String,Object> propriedades){GeradorArquivo g = new GeradorXML();g.setProcessador(new Compactador());

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 253: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Gerenciando muitos objetos

g.gerarArquivo(nome, propriedades);}

public void gerarPropriedadesCompactado(String nome, Map<String,Object> propriedades){

GeradorArquivo g = new GeradorPropriedades();g.setProcessador(new Compactador());g.gerarArquivo(nome, propriedades);

}

public void gerarXMLCriptografado(String nome, Map<String,Object> propriedades){

GeradorArquivo g = new GeradorXML();g.setProcessador(new Criptografador());g.gerarArquivo(nome, propriedades);

}

public void gerarPropriedadesCriptografado(String nome, Map<String,Object> propriedades){

GeradorArquivo g = new GeradorPropriedades();g.setProcessador(new Criptografador());g.gerarArquivo(nome, propriedades);

}

//outros métodos}

A ideia de se criar uma fachada para encapsular o acesso a um conjuntode classes não é substituir a API original, mas tornar mais simples o acessoàs funcionalidades. No caso do gerador de arquivos, as classes originais con-tinuam existindo e permitindo todo tipo de extensão que era possível ante-riormente. Dessa forma, apenas o acesso às funcionalidades foi simpli�cado,não prejudicando de forma alguma a estrutura e a �exibilidade já existente. Aideia da fachada não é fornecer métodos para cada possibilidade das classes,mas fornecer aquelas necessárias pela aplicação.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 254: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Separando código novo de código legado Casa do Código

D�������� ����������� ���������

O Facade é um padrão importante para a de�nição de componen-tes. A fachada de�nida é a interface externa do componente, que provêserviços para a aplicação através da coordenação da invocação de suasclasses encapsuladas. A de�nição de uma interface como abstração dafachada permite que existam diversas implementações para aquele com-ponente. Se essa prática for combinada com padrões como Dynamic

Factory, Dependency Injection ou Service Locator, é pos-sível que o componente possa ser instanciado dinamicamente. Essa é abase para a de�nição de uma arquitetura construída com componentesplugáveis, na qual cada um deles pode ser substituído e novos compo-nentes podem ser facilmente adicionados.

�.� S�������� ������ ���� �� ������ ������“Deixai-os crescer juntos até a colheita, e, no tempo da colheita, direi aosceifeiros: ajuntai primeiro o joio, atai-o em feixes para ser queimado; mas otrigo, recolhei-o no meu celeiro.”– Mateus ��:��

Em seu livro sobre Domain-driven Design [��], Eric Evans apresenta umpadrão chamado Anti-Corruption Layer. Esse padrão é aplicável emcenários onde é necessário acessar código legado, onde não necessariamenteo modelo de objetos é bem construído e compatível com o da nova aplicação.Esse código legado pode ser um sistema completo, um componente ou atémesmo um conjunto de classes. Um dos riscos nesse caso é de o modeloantigo “contaminar” a nova aplicação, a qual tenta se adaptar ao paradigma eàmodelagem do código antigo. Por outro lado, essa integraçãomuitas vezes éimportante, pois pode proporcionar reaproveitamento de código e gerar umgrande valor para o cliente.

O Anti-Corruption Layer propõe a criação de uma camada entre anova aplicação e o código legado. Ela é responsável por traduzir as chamadasfeitas pela aplicação em invocações para classes do sistema legado. A partir

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 255: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Gerenciando muitos objetos

dessa nova camada, é possível abstrair a existência do código legado do restoda aplicação, oferecendo os serviços de forma consistente com o modelo e amodelagem da nova aplicação.

Na implementação desse padrão, o Facade é normalmente utilizadopara fornecer os serviços necessários do código legado, encapsulando todoacesso a ele. Dentro dessa fachada pode ser necessária utilização de classesresponsáveis por traduzir as entidades do modelo de um dos sistemas parao outro. Isso pode ocorrer tanto com as classes da nova aplicação que serãorecebidas como parâmetro quanto com as classes retornadas pelo sistema le-gado.

A tradução entre duas classes é possível quando o importante forem osdados que ela carrega. Porém, quando a classe retornada possuir métodosque precisam ser invocados, não faz sentido duplicá-los na classe do novosistema. Nesse caso, pode ser utilizado um Adapter que encapsula umaclasse legada para uma interface utilizada pelo novo sistema. Dessa forma,existirá um método no adaptador que irá fazer os ajustes necessários paradelegar a funcionalidade para o método da classe legada.

�.� M������� � ��������� ����� �������“Cada um de nós escolherá o mediador que seleciona a informação segundo oscritérios que preferimos.”– F. Sars�eld Cabral

O Facade é um padrão que pode ser utilizado para o encapsulamentodas funcionalidades fornecidas por um subsistema. Essas funcionalidades sãodisponibilizadas através de uma classe e consumidas por objetos e compo-nentes da aplicação. Porém, nem sempre a interação entre os objetos de umso�ware acontece em apenas uma direção. Os objetos podem se relacionarcom outros de formas complexas, inclusive de forma a haver uma comuni-cação bidirecional entre eles. Por mais que essa interação possa acontecer deforma bem de�nida, podem ser criadas interdependências desestruturadas edifíceis de compreender.

Um contexto em que a lógica de interação entre componentes costumaser tornar complexa é em interfaces grá�cas. Por exemplo, a escolha de um

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 256: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Mediando a interação entre objetos Casa do Código

valor em um combo box pode ser o gatilho para o recarregamento de umalista e aomesmo tempo fazer com que certos campos sejam desabilitados. Emdois campos onde precisam ser entradas a senha e sua veri�cação, a mudançado valor em um dos campos pode disparar uma lógica de validação que faza comparação com o outro. Quando um componente é ligado diretamentecom outros e as interações são complexas, �ca muito difícil entender e darmanutenção nesses relacionamentos, que precisam ser criados um a um, emuma relação muitos-para-muitos.

É justamente para esse tipo de cenário que o Mediator deve ser utili-zado. Esse padrão propõe a criação de uma classe que serve comomediadoraentre os objetos. Dessa forma, ao invés dos objetos enviarem requisições ereceberem requisições de vários outros, ela irá interagir apenas com o me-diador. Essa classe será responsável por receber as requisições dos objetos eenviá-las para os objetos que as devem receber.

Aumentando o número de observadores e observados

Para exempli�car a utilização desse padrão, vamos retomar o exemploapresentado no capítulo � para o padrão Observer. Nesse exemplo, aclasse CarteiraAcoes representa uma carteira de ações que pode pos-suir observadores, sendo que o método addObservador() é utilizadopara a adição de um observador. Cada observador é uma classe que im-plementa a interface Observador, que recebe uma chamada no métodomudancaQuantidade() quando ações são adicionadas ou removidas dacarteira.

Dentro desse cenário, as coisas podem se complicar quando o númerode carteiras e de observadores puder ser grande. Por exemplo, se houver dezcarteiras de ações a serem observadas por dez diferentes observadores emum determinado contexto, serão necessárias cem invocações de método paraadicionar cada um dos observadores em cada uma das carteiras. Essa ligaçãodireta entre os objetos pode tornar difícil o gerenciamento de quais observa-dores estão recebendo eventos de quais carteiras.

A utilização de um Mediator é uma alternativa para gerenciar a relaçãoentre os observadores e as carteiras de ação. A classe GrupoObservacao,cuja listagem está apresentada a seguir, é uma mediadora que representa um

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 257: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Gerenciando muitos objetos

grupo de carteiras que podem ser observadas por um grupo de observadores.Para cumprir esse objetivo, essa classe implementa a interface Observador

e se adiciona como observadora em todas as carteiras adicionadas a ela.Além disso, ela também recebe observadores, os quais são adicionados emuma lista mantida internamente. Assim, quando alguma das carteiras noti�-car omediador de algum evento, esse será repassado a todos os observadores.

Listagem �.� - Mediador para interação entre a carteira de ações e seusobservadores:

public class GrupoObservacao implements Observador {private List<Observador> obs = new ArrayList<>();

public void mudancaQuantidade(String acao, Integer qtd) {for(Observador o: obs)

o.mudancaQuantidade(acao, qtd);}public void addObservador(Observador o) {

obs.add(o);}public void addCarteira(CarteiraAcoes ca) {

ca.addObservador(this);}

}

Cada observador será adicionado apenas na classe GrupoObservacao,que se encarregará de repassar os eventos gerados por todas as carteiras. Deforma similar, cada carteira também terá apenas um observador, que será omediador. Portanto, essa classe age como intermediária para a comunicaçãoentre esses objetos.

O exemplo apresentado mostra um caso simples de Mediator, porémimagine que esse cenário se complique umpoucomais. Considere, por exem-plo, que determinados observadores estejam interessados apenas em eventosa partir de uma certa quantidade ou somente de um grupo de ações. Essepadrão cria um contexto de interação entre os objetos onde esse tipo de regrapode ser introduzida e mais facilmente gerenciada.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 258: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Mediando a interação entre objetos Casa do Código

Estrutura do Mediator

A estrutura do padrão Mediator é bem ilustrada pela metáfora utili-zada em seu nome. Existe uma classe mediadora que serve como interme-diária para interação entre os objetos da aplicação, como mostra a �gura �.�.Nessa estrutura, a classe representada como Mediador recebe chamadas deRealizadorChamadas e, de acordo com a lógica de interação entre os ob-jetos, invoca métodos nas classes do tipo RecebedorChamadas. A classeRealizarRecebedor, representada nodiagrama, ilustra o caso emque umamesma classe recebe e realiza invocações no mediador.

Fig. �.�: Estrutura do padrão Mediator

Os objetos que são invocados pelo mediador, por precisarem ser acessí-veis a ele, normalmente são armazenados em atributos internos. Já as classesque invocam funcionalidades nele precisam, de alguma forma, obter sua re-ferência. Para isso, podem ser aplicadas as estratégias de criação de objetosapresentadas em capítulos anteriores. Por exemplo, se houver apenas umme-diador para toda a aplicação, um Singleton pode ser utilizado para queas classes tenham uma forma centralizada de acessar a sua instância. Uma

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 259: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Gerenciando muitos objetos

outra alternativa, seria o uso de Dependency Injection, de forma que omediador seria injetado nas classes que precisassem se comunicar com ele.

Um dos principais benefícios desse padrão é o desacoplamento entre asclasses que precisam realizar uma chamada e as que devem tratá-las. Como omediador é colocado entre as classes, em nenhummomento existe um acessodireto entre elas. O Mediator centraliza o gerenciamento do relaciona-mento entre objetos, o que simpli�ca a troca das instâncias e a adição de novasno contexto de interação. Em outras palavras, caso um novo objeto preciseinteragir com outros, em vez dele ser ligado a cada objeto, ele só precisa estarligado ao mediador.

O Mediator é indicado quando as relações entre os objetos forem com-plexas o su�ciente, de forma a valer a pena que essa responsabilidade sejaconcentrada em uma classe. Um dos motivos para essa complexidade seriauma grande quantidade de objetos, os quais precisariam estar ligados uns aosoutros. Outromotivo seriam regras para essa interação entre os objetos, comocondições para que um determinado objeto receba uma chamada. Com o usodesse padrão, essas regras migrariam das classes para o mediador. Por exem-plo, ao invés de uma classe �ltrar com quais objetos ela deve interagir ou quaischamadas ela deve tratar, essa responsabilidade seria movida para o media-dor. É importante ressaltar que apesar de as relações tratadas pelo mediadorserem complexas, elas precisam seguir regras bem de�nidas.

O passo básico para a refatoração de adição de ummediador em um con-junto de classes não émuito diferente do apresentado na �gura �.�, para a adi-ção de um Facade. A lógica de interação entre os objetos precisaria ir sendoextraída em métodos, os quais seriam movidos para o mediador em seguida.Porém, àmedida que novas interações fossem sendo adicionadas, omediadorprecisaria ir incorporando as novas regras, sem eliminar as antigas. Uma prá-tica que facilita essa transição são os mediadores implementarem as abstra-ções das classes que recebem as chamadas, pois assim ele poderá ser invocadode forma transparente pelas classes já existentes. Isso foi feito no exemplo daclasse GrupoObservacao, que implementou a interface Observador parapoder ser facilmente adicionada na classe CarteiraAcoes já existente.

Lançando eventos no Spring

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 260: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Mediando a interação entre objetos Casa do Código

Uma forma interessante de se utilizar um Mediator é através de eventos.Todas as requisições são passadas para o mediador na forma de um evento, oqual é tratado pelas classes interessadas naquele tipo de evento. O frameworkSpring, apresentado no capítulo � para o uso de injeção de dependências, pos-sui uma funcionalidade que permite o lançamento e o tratamento de eventospelas classes declaradas no seu contexto.

Essa seção irá apresentar como o exemplo da carteira de ações seriaimplementado utilizando o Spring como mediador. A primeira classe queprecisa ser de�nida é a que representa o evento que será lançado. Essa classeprecisa conter as informações que a classe que cria o evento deseja comunicarpara a que o receberá. A listagem a seguir apresenta a classe MovAcao, querepresenta um evento de movimentação de uma ação. Essa classe precisaestender a classe ApplicationEvent, de�nida pelo próprio Spring.

Listagem �.� - De�nição de um novo tipo de evento:

public class MovAcao extends ApplicationEvent {private String acao;private Integer qtd

public MovAcao(String acao, Integer qtd) {this.acao = acao;this.qtd = qtd;

}public String getAcao() {

return acao;}public Integer getQtd() {

return qtd;}

}

Em seguida é preciso de�nir a classe que irá gerar os eventos e no-ti�car o Spring. No exemplo apresentado, esse seria o caso da classeCarteiraAcoes, apresentada na listagem a seguir, que deve gerar umMovAcao toda vez que a quantidade de uma ação for alterada através dométodo adicionaAcoes(). Para que ela possa gerar um evento, ela pre-

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 261: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Gerenciando muitos objetos

cisa receber do Spring uma injeção de dependências de uma instância deApplicationEventPublisher, cujo método publish() deve ser cha-mado a cada geração de evento. Nesse caso, o framework utiliza a estratégiade Dependency Injection por meio de interfaces, na qual a classe pre-cisa implementar a interface ApplicationEventPublisherAware parareceber a dependência no método setter de�nido por ela.

Listagem �.� - Classe que publica os eventos no Spring:

public classCarteiraAcoes implements ApplicationEventPublisherAware {

private Map<String,Integer> acoes = new HashMap<>();private ApplicationEventPublisher publicador;

public voidsetApplicationEventPublisher(ApplicationEventPublisher p){

this.publicador = p;

}

public void adicionaAcoes(String acao, Integer qtd) {if(acoes.containsKey(acao)){

qtd += acoes.get(acao);}acoes.put(acao, qtd);MovAcao evento = new MovAcao(acao,qtd)publicador.publishEvent(evento)}

}

A classe que estiver interessada em um evento deve implementar a inter-face ApplicationListener, com o tipo genérico igual à classe do eventoque se deseja receber. Dessa forma, toda vez que um evento daquele tipofor lançado, o Spring irá invocar o método onApplicationEvent() na-quela classe. A listagem a seguir mostra o exemplo da classe AcoesLogger

que escreve no console as informações das instâncias de MovAcao recebidas.

Listagem �.� - Classe que recebe os eventos do tipo MovAcao do Spring:

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 262: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Mediando a interação entre objetos Casa do Código

public classAcoesLogger implements ApplicationListener<MovAcao>{

public void onApplicationEvent(MovAcao evento) {System.out.println("Alterada a quantidade da ação "

+ evento.getAcao() + " para " + evento.getQtd());}

}

Para juntar os pedaços e colocar o Spring como mediador das classes, épreciso criar um arquivo de con�guração com a de�nição dessas classes. Noexemplo, esse arquivo se chama beans.xml e seu conteúdo está apresentadona listagem a seguir.

Listagem �.� - Arquivo beans.xml para con�guração dos beans do Spring:

<?xml version="1.0" encoding="UTF-8"?><beans ...>

<bean id="carteira"class="br.com.casadocodigo.CarteiraAcoes"/>

<bean id="logger" class="br.com.casadocodigo.AcoesLogger"/></beans>

Finalmente, para ilustrar como esse código seria utilizado, a próximalistagem mostra um trecho de código que utiliza as classes criadas. Nessecódigo é criado um contexto do Spring através do arquivo XML apresentadoe em seguida a instância da classe CarteiraAcoes é recuperada. Quandoo método adicionaAcoes() é invocado, o evento será gerado e recebidopela classe AcoesLogger.

Listagem �.� - Código que cria o contexto da aplicação e provoca gera-ção de eventos:

ConfigurableApplicationContext contexto =new ClassPathXmlApplicationContext("beans.xml");

CarteiraAcoes c = (CarteiraAcoes) contexto.getBean("carteira");c.adicionaAcoes("ABC", 300);c.adicionaAcoes("XYPLZ", 700);

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 263: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Gerenciando muitos objetos

Essa abordagem baseada em eventos é uma forma interessante de imple-mentação demediadores. Os eventos são lançados pelas classes para o Springsem que elas saibam que outras classes se interessam por eles. Da mesmaforma, os eventos são tratados por classes que não têm conhecimento de ondeforam originados. Essa é uma forma de desacoplar os componentes de umsistema, sem impedir que eles se comuniquem com os outros.

�.� R������������� ���������� ��� F��������“Uma classe deve ser imutável a não ser que tenha uma boa razão para fazê-lamutável.”– Joshua Bloch

As seções anteriores abordaram padrões que visam simpli�car o relacio-namento e a comunicação entre as classes de um sistema. Tanto o Facade

quanto o Mediator simpli�cam as interfaces, tornando a estrutura do so�-ware mais modular. Diferentemente, o Flyweight ataca cenários ondeexiste a criação de muitos objetos de uma mesma classe, que muitas vezesacabam sendo semelhantes. A solução desse padrão consiste no reaproveita-mento da mesma instância para representação de objetos semelhantes, sendoque o que irá diferenciá-la é o contexto no qual ela será inserida.

O clássico exemplo desse padrão utilizado no GoF [��] é o de um editorde texto que precisa representar as letras de um documento. Se cada letrado documento fosse representada por um objeto diferente, a quantidade deobjetos seria muito grande. Imagine, por exemplo, quantas vezes a letra “a”aparece em um texto. Utilizando o Flyweight, cada letra seria representadapor apenas uma instância, que seria utilizada sempre que ela fosse necessária.O que diferenciaria, por exemplo, as diferentes letras “a” do texto, seriam olocal e o contexto em que estariam inseridas. Dessa forma, propriedades quepossam variar, como o tamanho e tipo da fonte, não seriam propriedades daletra e sim do trecho de texto em que estaria inserida.

Por mais que esse exemplo do editor de texto seja bem ilustrativo parao entendimento do padrão, acredito que muito poucos desenvolvedores nosdias de hoje trabalhem com editores de texto. Adicionalmente, a represen-tação de caracteres em Strings e texto é um problema já bem resolvido pelas

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 264: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Reaproveitando instâncias com Flyweight Casa do Código

bibliotecas nativas de linguagens de programação mais modernas, como oJava. Então será que esse padrão ainda é aplicável em sistemas atuais? Emque outros cenários ele poderia ser utilizado? É justamente sobre isso que setrata essa seção!

Status de itens de um pedido em um sistema de E-commerce

Imagine que em um sistema de e-commerce seja necessário controlar ostatus de cada item de um pedido. Cada item da compra é composto comuma instância da classe StatusItem apresentada na listagem a seguir.Além do nome do status, essa classe também possui algumas propriedadesbooleanas que são utilizadas na lógica do sistema para veri�car se algumasações podem ser tomadas pelo usuário. Quando o status de um item éalterado, uma nova instância de StatusItem referente ao novo status écriada e inserida na instância.

Listagem �.� - Classe que representa o status de um item de uma com-pra:

public class StatusItem {private String nome;private boolean podeCancelar;private boolean compraConcluida;

public StatusItem(String nome, boolean podeCancelar,boolean compraConcluida){

this.nome = nome;this.podeCancelar = podeCancelar;this.compraConcluida = compraConcluida;

}public String getNome(){

return nome;}public boolean isCompraConcluida(){

return compraConcluida;}public boolean podeCancelar(){

return podeCancelar;

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 265: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Gerenciando muitos objetos

}}

A perceber que a aplicação estava ocupando um grande espaço em me-mória, foi utilizada uma ferramenta de pro�ling para investigar quais instân-cias estavam ocupando a memória da máquina virtual. Nessa análise, umadas surpresas foi a presença de um número muito grande de objetos da classeStatusItem. Mesmo sendo uma classe pequena, a imensa quantidade deinstâncias ocupava umaparte signi�cativa damemória. Por ser uma aplicaçãoem que diversos usuários acessam de forma simultânea, e pelo fato de cadaordem de compra poder possuir diversos itens, o número de StatusItem

na memória em um determinado momento é sempre alto. Porém, por di-versas instâncias possuírem as mesmas informações, esse ponto foi um dosescolhidos para otimização.

O ��� � ��� ���������� �� ����������

Uma ferramenta de pro�ling é um so�ware utilizado por desenvolve-dores para fazer análise de desempenho de aplicações. Ferramentas depro�ling para Java normalmente se conectam à máquina virtual que estáexecutando a aplicação e colhem diversas informações, como o tempode execução dos métodos e a quantidade de objetos em memória, entreoutras coisas. Sendo assim, é possível saber que método está demorandomais tempo para executar ou qual é a classe cujas instâncias estão ocu-pando mais memória. Se em uma análise dessas for constatado que exis-tem muitas instâncias semelhantes de uma mesma classe na memória, oFlyweight é certamente um padrão candidato para diminuir signi�ca-tivamente esse número.

Apresentando o Flyweight

O Flyweight, cuja tradução seria algo como peso-mosca ou peso-pena,é um padrão cujo objetivo é permitir a representação de um número grandede objetos de forma e�ciente. Ele é aplicável em cenários onde existem diver-sas instâncias da mesma classe em memória que são similares. Dessa forma,

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 266: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Reaproveitando instâncias com Flyweight Casa do Código

a solução proposta pelo padrão é reutilizar a mesma instância em todos oslocais onde objetos semelhantes precisarem ser utilizados.

A �gura �.� apresenta a estrutura do padrão. A classe apresentada como nome FabricaFlyweight é responsável por retornar as instâncias deFlyweight para as classes clientes. Normalmente a fábrica utiliza uma chavepara identi�car a instância que deve ser retornada, que normalmente é umastring ou um número que identi�ca logicamente dentro da aplicação o ob-jeto desejado. A fábrica pode inicializar os objetos de forma “preguiçosa”,criando-os na primeira vez que forem solicitados, armazenando-os interna-mente e retornando amesma instância em solicitações posteriores. Ou, comoalternativa, as instâncias podem ser criadas logo na inicialização da própriafábrica, o que é indicado quando houver poucas instâncias.

Fig. �.�: Funcionamento do Flyweight

Uma característica muito importante do Flyweight é que os objetosrepresentados por ele precisam ser imutáveis. Em outras palavras, depois decriados, o estado interno desses objetos não pode ser mais alterado. O prin-cipal motivo para isso é que a mesma instância estará sendo utilizada em di-versos contextos da aplicação, e se ela for de alguma forma modi�cada, todosos outros contextos serão afetados. Mais à frente nesse mesmo capítulo se-rão apresentados cuidados que se deve ter em Java para a criação de classesimutáveis.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 267: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Gerenciando muitos objetos

Umoutra característica das classes Flyweight, justamente por sua imu-tabilidade, está no fato de seusmétodos algumas vezes dependerem do estadoexterno para sua execução. Toda informação que precisar ser alterada ou quefor dependente do estado da execução não pode ser armazenada dentro doFlyweight. Por isso mesmo, se essa classe precisar executar alguma lógicaque depende do contexto onde está, as informações precisam ser passadascomo parâmetro. Na �gura �.�, esse tipo de método está representado pelométodo executar() na classe Flyweight.

A principal consequência desse padrão é a economia de memória que sepode ter com a reutilização de instâncias emdiversos pontos da aplicação. Emaplicações que tratam requisições de diversos usuários de forma simultânea,como em aplicaçõesWeb, esse padrão pode ter um grande impacto para a re-presentação de objetos muito utilizados. A melhora de desempenho tambémpode ter impacto em processamento, visto que diversas instâncias não pre-cisarão mais ser criadas, e posteriormente coletadas pelo Garbage Collector.Além disso, se a aplicação realmente garantir que existe apenas uma instânciapara cada tipo de objeto daquela classe, as comparações entre eles podem serfeitas utilizando o operador “==”, que é muito mais e�ciente que o métodoequals().

Compartilhando as instâncias que representam o status

Voltando ao problema apresentado anteriormente, as instâncias da classeStatusItem são boas candidatas à implementação do padrão Flyweight.Isso porque são objetos imutáveis no contexto da aplicação, ou seja, um itemirámudar de status,mas não irámodi�car as informações de umdeterminadostatus. Isso permite que as instâncias dessa classe possam ser compartilhadaspor diversos objetos. O fato de haver um número muito grande de instân-cias similares dessa classe na aplicação também motiva a implementação dopadrão.

O primeiro passo para implementá-lo é a criação de uma fábricaresponsável pela criação e retorno dos objetos. No caso foi criada a classeFabricaStatusItem, representada na listagem a seguir. Nesse contexto,deve haver apenas uma instância dessa fábrica para que os objetos deStatusItem sejam armazenados em apenas um local. Para isso, essa classe

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 268: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Reaproveitando instâncias com Flyweight Casa do Código

implementa o padrão Singleton, armazenando a instância no atributoestático instance e retornando-o através do método getInstance().

Listagem �.� - Fábrica para obtenção das instâncias de StatusItem:

public class FabricaStatusItem {private static FabricaStatusItem instance =

new FabricaStatusItem();

public static FabricaStatusItem getInstance() {return instance;

}

private Map<String,StatusItem> mapa;

private FabricaStatusItem(){mapa = new HashMap<>();mapa.put("CARRINHO", new StatusItem(

"CARRINHO", true, false));mapa.put("FECHADO", new StatusItem(

"FECHADO", true, false));mapa.put("PAGO", new StatusItem(

"PAGO", true, true));mapa.put("ENVIADO", new StatusItem(

"ENVIADO", false, true));mapa.put("ENTREGUE", new StatusItem(

"ENTREGUE", false, true));}public StatusItem get(String nome){

if(!mapa.containsKey(nome))throw new RuntimeException("Status inexistente:"+

nome);return mapa.get(nome);

}}

A classe FabricaStatusItem cria todos os StatusItem no constru-tor e os armazena em uma mapa. Essa é uma estratégia válida pois o númerode objetos é pequeno. Outra possibilidade seria ir criando os objetos à me-

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 269: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Gerenciando muitos objetos

dida que forem solicitados. Esse caso é interessante, por exemplo, quando osobjetos são armazenados em uma base de dados. Assim somente os objetosque realmente forem utilizados serão mantidos em memória.

Além da criação da fábrica, é preciso substituir os locais onde a classeStatusItem é instanciada pela invocação da fábrica. Uma forma de ajudara detectar esses pontos é declarar o construtor da classe com o modi�car deacesso default, e colocar somente a fábrica no mesmo pacote que ele. Isso ge-rará umerro de compilação em todas as classes fora desse pacote que tentaremutilizá-lo.

Cuidados para criar objetos imutáveis e únicos

Criar classes que geram objetos imutáveis pode não ser algo tão trivialquanto se imagina. Principalmente em linguagens orientadas a objetos emque as classes podem ser estendidas e comportamentos podem ser sobrepos-tos. Na implementação do Flyweight é importante ter certeza de que asinstâncias compartilhadas realmente não permitem modi�cação, pois issopoderia resultar em um problema crítico e difícil de detectar. Abaixo estãoalgumas diretivas a serem seguidas para a implementação de classes imutá-veis:

• Não adicione métodos setter ou outros métodos que modi�quem osatributos da classe. Isso irá impedir que externamente os clientes doobjeto o modi�quem.

• Crie todos os atributos como private e final. Isso irá impedir queeles sejam inacessíveis a subclasses e que possam ser modi�cados apósa sua primeira atribuição de valor.

• Impeça a criação de subclasses. Isso pode ser feito de forma direta de-clarando a classe como final. Outra alternativa é declarar o constru-tor como private e utilizar um Static Factory Method para acriação das instâncias. Isso é importante para impedir que subclassesadicionem novas características mutáveis.

• Não retorne diretamente atributos com objetos mutáveis. Em outraspalavras, se você retornar um objeto mutável armazenado em um atri-

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 270: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Reaproveitando instâncias com Flyweight Casa do Código

buto, ele poderá sermodi�cado pelos clientes, mesmo estando de�nidocomo final. Caso seja mesmo necessário retorná-lo, crie uma cópiae o retorne.

• Cuidado ao receber objetos mutáveis na construção do objeto, poiseles podem ser modi�cados pelos clientes posteriormente, mesmo nãosendo retornados pela classe. O que recomenda-se nesse caso é a cri-ação de uma cópia do objeto mutável recebido para o armazenamentoem um atributo do objeto imutável.

Analisando a classe StatusItem apresentada anteriormente, é possívelobservar que ela não segue todas as recomendações para a criação de objetosimutáveis. Apesar de não possuir métodos setter para modi�cações noobjeto, os atributos não possuem o modi�cador final e a classe pode serestendida. A listagem a seguir mostra a classe StatusItem modi�cadasegundo as recomendações para objetos imutáveis. Observe que o construtorfoi de�nido como privado e foi de�nido um Static Factory Method

para impedir que sejam de�nidas subclasses. Como nenhum dos atributosdessa classe é mutável, não foi preciso se preocupar com o retorno e com orecebimento deles como parâmetro.

Listagem �.�� - Tornando a classe StatusItem imutável:

public class StatusItem {private final String nome;private final boolean podeCancelar;private final boolean compraConcluida;

static StatusItem criar(String nome,boolean podeCancelar, boolean compraConcluida) {

return new StatusItem(nome, podeCancelar,compraConcluida);

}

private StatusItem(String nome, boolean podeCancelar,boolean compraConcluida) {

this.nome = nome;

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 271: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Gerenciando muitos objetos

this.podeCancelar = podeCancelar;this.compraConcluida = compraConcluida;

}//métodos de acesso as propriedades

}

Um outro cuidado que é preciso ter seria em relação à unicidade dos ob-jetos do Flyweight. Além da existência da fábrica, é preciso ter certeza deque ela está sendo invocada em todos os locais onde o objeto é criado. Naclasse StatusItem dessa seção, o Static Factory Method chamadocriar() é de�nido com o modi�cador de acesso default para que somentea fábrica, localizada no mesmo pacote que ele, possa utilizá-lo.

Porém, somente isso pode não ser su�ciente para garantir essa unicidade.Um caso que precisa de um tratamento especial é quando a classe que con-tém o Flyweight precisa ser serializada. Normalmente isso ocorre quandoela é enviada remotamente como parâmetro ou quando é persistida em umarquivo. Nesse caso, no momento da desserialização uma nova instância doFlyweight será criada. Nesses casos, é preciso sobrepor omecanismo de se-rialização da classe que contém a instância do Flyweight para que ela sejarecuperada da fábrica quando for desserializada.

Para exempli�car esse caso, será considerada a classe Item que é com-posta pela classe StatusItem, e no exemplo precisa ser serializável poisé enviada remotamente como parâmetro para um EJB. Para evitar que umatributo do tipo StatusItem seja incluído de forma inadequada em umaclasse serializável, ela deve ser mantida sem a implementação da interfaceSerializable. Dessa forma, caso haja a tentativa de serializá-la, mesmoque dentro de uma outra, ocorrerá um erro.

Para que seja possível serializar uma classe composta por umStatusItem, é necessário declarar o atributo como transient paraque ele não seja incluído no algoritmo de serialização. Em seguida é precisocustomizar o algoritmo de serialização para que ele de alguma forma guardeo status do objeto e o recupere da fábrica depois. Isso pode ser feito criandoos métodos privados writeObject() e readObject() na classe.No método writeObject(), o método defaultWriteObject() échamado para que o algoritmo de serialização padrão seja executado e em

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 272: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Reaproveitando instâncias com Flyweight Casa do Código

seguida o nome do status, que é utilizado para a recuperação da fábrica,também é armazenado. De forma similar, no método readObject(), ométodo defaultReadObject() é chamado inicialmente e em seguida élida a string com o nome do status. Essa string é utilizada para recuperar oStatusItem da fábrica e o atribuir ao atributo da classe.

Listagem �.�� - Sobrepondo o mecanismo de serialização da classe Item:

public class Item implements Serializable {private transient StatusItem status;

//outras propriedades//métodos getters e setters omitidos

private void writeObject(ObjectOutputStream oos)throws IOException {

oos.defaultWriteObject();oos.writeObject(status.getNome());

}private void readObject(ObjectInputStream ois)

throws ClassNotFoundException, IOException {ois.defaultReadObject();String status = ois.readObject().toString();this.status =

FabricaStatusItem.getInstance().get(status);}

}

O código da listagem a seguir pode ser utilizado para testar se o meca-nismo de serialização está funcionando corretamente. O método chamadocopiarPorSerializacao() cria uma cópia do objeto a partir de sua se-rialização e desserialização. Dessa forma, é possível criar um objeto da classeItem, criar uma cópia e em seguida veri�car se a instância con�gurada navariável transiente, que no caso representa o Flyweight, é a mesma.

Listagem �.�� - Sobrepondo o mecanismo de serialização da classe Item:

public class TesteSerialização {public static void main(String[] args) throws Exception {

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 273: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Gerenciando muitos objetos

Item i1 = new Item();i1.setProduto("Livro Design Patterns");i1.setStatus(FabricaStatusItem.getInstance()

.get("PAGO"));//seta outras propriedades

Item i2 = (Item) copiarPorSerializacao(i1);if(i1.getStatus()==i2.getStatus()){

System.out.println("Mesma instância!");}

}public static Serializable

copiarPorSerializacao(Serializable obj)throws Exception {

ObjectOutputStream output = null;ObjectInputStream input = null;try {

ByteArrayOutputStream byteOutput =new ByteArrayOutputStream();

output = new ObjectOutputStream(byteOutput);output.writeObject(obj);output.flush();byte[] byteArray = byteOutput.toByteArray();ByteArrayInputStream byteInput =

new ByteArrayInputStream(byteArray);input = new ObjectInputStream(byteInput);return (Serializable) input.readObject();

} finally {output.close();input.close();

}}

}

O mesmo cuidado que se tem com a serialização do objeto, tambémé preciso ter com qualquer tipo de persistência. Se a sua aplicação utilizaJPA, por exemplo, se a classe Item for persistente, deve-se tomar cuidadopara que instâncias de StatusItem não sejam criadas de forma automá-tica pelo framework de persistência. A solução para esse problema é simi-

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 274: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

�.�. Considerações �nais do capítulo Casa do Código

lar à apresentada para a serialização. O atributo deve ser marcado com aanotação @Transient e métodos com anotações do tipo @PrePersist

e @PostLoad devem ser criados para respectivamente persistir a chave emumoutro atributo e recuperar o objeto da fábrica depois do carregamento dosdados no objeto.

C��� � F�������� � ����� ���� S������ �� J���

A classe String em Java é um objeto imutável. Todos os méto-dos que modi�cam uma string, como para concatenação ou extração desubstring, retornam uma nova instância de String e não modi�cam aoriginal. Dessa forma, a cadeia de caracteres utilizada internamente pelaString pode ser compartilhada por diversas instâncias dessa classe. Isso�ca bem claro em métodos como substring(), em que a mesma ca-deia é utilizada na nova instância de String retornada. Isso seria umexemplo do uso do Flyweight para a economia de memória na má-quina virtual.

�.� C������������ ������ �� ��������Esse capítulo tratou de questões relacionadas ao gerenciamento do relaciona-mento entre os objetos de uma aplicação. Para entender uma classe, é precisoentender não apenas seus elementos, mas também as interfaces com as quaisela se relaciona. Quando o número de classes aumenta, é essencial que elassejam organizadas e que se busque simpli�car a interface entre elas. Quandoo número de instâncias é muito grande, é importante gerenciar o relaciona-mento entre elas para que seja possível entender o estado do sistema em umdeterminado momento.

O padrão Facade foi apresentado como uma forma de dividir a aplica-ção em subsistemas. Ele cria uma interface única que coordena a invocaçãode diversas classes para fornecer serviços ao resto da aplicação. Como con-sequências principais, as classes encapsuladas �cam desacopladas do resto daaplicação, e a interface para obter sua funcionalidade é simpli�cada. De forma

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 275: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo �. Gerenciando muitos objetos

similar, o Mediator serve comoum intermediário para relação entre objetosdo sistema. Por centralizar e gerenciar os relacionamentos, o mediador retiradas classes essa responsabilidade que �ca espalhada entre elas e desacopla aclasse que gera uma chamada de método da classe que a recebe.

Em seguida, focando na diminuição do número de instâncias similaresde uma mesma classe, o Flyweight propõe que essas instâncias sejam re-aproveitadas em diversos pontos da aplicação. Para isso, é preciso criar umafábrica desses objetos, a qual retorne a mesma instância quando forem neces-sários objetos similares. Também é preciso que essa classe compartilhada sejaimutável, para que alterações realizadas em um contexto não afetem outrosem que a instância estiver sendo reutilizada. Além do padrão, também foramapresentadas questões mais especí�cas da linguagem Java para a implemen-tação desse padrão na prática.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 276: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 277: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

C������� ��

Indo além do básico

“Ao in�nito... E além!”– Buzz Lightyear, Toy Story

Durante todo esse livro trilhamos um caminho que nos apresentou diver-sos padrões que podem ser utilizados para projetar um so�ware orientado aobjetos. Cada capítulo focou em uma técnica ou em tipo de problema, para osquais alguns padrões foram apresentados. Nem todos os padrões do GoF [��]foram mostrados, porém, com o conhecimento dos padrões e técnicas nesselivro, acredito que o leitor tenha uma boa base para a realização e evoluçãodo projeto de uma aplicação. Além disso, com essa visão adquirida, �ca maissimples a compreensão de outros padrões.

Esse último capítulo não apresenta nenhum padrão novo, porém exploradiversos tópicos mais avançados e recentes a respeito da utilização de pa-drões. Ele aborda, por exemplo, sua utilização para o desenvolvimento de

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 278: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

��.�. Frameworks Casa do Código

frameworks e como os tipos genéricos podem ser usados de forma efetiva nasua implementação. Também são discutidas questões a respeito da utiliza-ção de padrões com a técnica de desenvolvimento Test-Driven Developmente como os padrões apresentados se aplicam para problemas mais amplos re-lacionados à arquitetura. Finalmente, o livro e o capítulo �nalizam falandoum pouco sobre a comunidade nacional e internacional de padrões.

��.� F���������“Um framework não é bom pelo que ele faz, mas quando pode ser utilizadopara o que ele não faz.”– Eduardo Guerra

Nos dias de hoje, é muito difícil alguém desenvolver uma aplicação sem autilização de um framework, principalmente na linguagem Java. Ele não ape-nas provê um reuso de seu código, como também o reúso de sua estrutura,de seu design. Dessa forma, além de possibilitar o reaproveitamento de fun-cionalidades acelerando o ritmo de desenvolvimento, ele também direcionaa arquitetura da aplicação à utilização de boas práticas de código.

Um framework pode ser visto como um so�ware incompleto que precisaser preenchido com partes especí�cas de uma aplicação para poder ser execu-tado [��]. Imagine, por exemplo, um framework que faça o agendamento deexecuções. Sozinho, ele não faz nada, pois não tem o que ser agendado sem aexistência de uma aplicação. Ao ser instanciado, ele é completado com classesda aplicação que serão invocadas por ele, e então faz sentido a sua execução.

Uma estrutura abstrata referente a um determinado domínio é apresen-tada pelo framework. Esse domínio pode ser horizontal, referente a uma ca-mada de aplicação, como apresentação ou persistência, ou vertical, referenteao domínio da aplicação, como seguros ou comércio eletrônico. De qualquerforma, o framework captura o conhecimento desse domínio, identi�candoos principais papeis que as classes desempenham e como elas se relacionam.A partir disso, ele provê um estrutura reutilizável que pode ser usada para acriação de aplicações dentro daquele domínio.

Um framework possui pontos com funcionalidade �xa, chamados de fro-zen spots, e pontos com funcionalidade variável, chamados de hotspots. Os

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 279: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo ��. Indo além do básico

hotspots são pontos de extensão disponibilizados, onde a aplicação pode in-serir parte do seu código. Os frozen spots são os que efetivamente proveema funcionalidade do framework e que coordenam a execução dos hotspots.Igualmente importantes, os hotspots e os frozen spots formam sua arquiteturaem conjunto.

Ele propõe uma forma de reúsomuito diferente de uma biblioteca de fun-ções ou de classes. Quando uma biblioteca é utilizada, a aplicação é respon-sável por invocar a funcionalidade das classes, coordenando sua execução eusando-a como um passo de seu processamento. Quando um framework éutilizado, normalmente é ele quem tem o controle sobre a execução e invocaa funcionalidade das classes da aplicação em pontos especí�cos. Isso permiteque a aplicação reutilize o código e a estrutura do framework, especializando-o para suas necessidades.

Um framework também é diferente de padrões de projeto, pois representaefetivamente uma implementação, um so�ware. Os padrões, como já foi ditodurante o livro, já existem como conhecimento, comouma ideia. Porém,mui-tos padrões são utilizados para viabilizar a criação de hot spots na estruturados frameworks. Os hook methods e as hook classes, apresentados respectiva-mente nos capítulos � e �, são exemplos de técnicas que podem ser utilizadas.Através da herança, a aplicação pode estender uma classe do framework einserir funcionalidade em um método. Com composição, a aplicação podeimplementar uma interface do framework e inserir uma classe para ser invo-cada em um determinado ponto da execução.

Os tipos de hot spots apresentados nessa seção não são os únicos que po-dem ser utilizados em frameworks. A utilização de re�exão e metadados,especialmente de�nidos com anotações, tem sido uma prática cada vez maisutilizada em frameworks mais recentes [�]. Porém essas questões já estãoalém do escopo desse livro.

Ainda existem bibliotecas?

Biblioteca de funções é um conceito que veio da programação estrutu-rada, onde o reúso permitido pelo paradigma de programação era mais res-trito. Porém, ainda existem algumas bibliotecas de funções em Java, porexemplo, como as classes Math e Collections. Em aplicações, essa bi-

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 280: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

��.�. Frameworks Casa do Código

blioteca de funções é frequentemente colocada em alguma classe terminadacom “Utils”. Nessa classe, usualmente são incluídosmétodos, normalmenteestáticos, que são de uso geral da aplicação.

Um conceito de biblioteca inserido na programação orientada a objetossão as bibliotecas de classe. Agora, em vez de termos apenas métodos reutili-záveis, temos classes! Um exemplo de biblioteca de classes muito popular emJava é a Collection API. Diferentemente dos frameworks, as bibliotecas sãosimplesmente classes reutilizáveis, que não permitem nenhum tipo de exten-são. O conceito de biblioteca também é aplicável dentro da estrutura de umframework como um conjunto de classes que compartilha de uma mesmaabstração e podem ser inseridas em um de seus hotspots. Por exemplo, emum framework de validação de instâncias em que o algoritmo de validação deuma propriedade é um hotspot, faria sentido uma biblioteca de classes com ostipos mais comum de validação, como formato de string, limites numéricos,entre outros.

Muitas vezes, em aplicações, utilizamos classes como se fossem parte deuma biblioteca, porém se olharmos com calma sua documentação, veremosque ela possui diversos hotspots pelos quais é possível estender o seu compor-tamento, caracterizando-se mais como um framework. Isso tem a ver comum padrão chamado de Low Surface-to-volume Ratio [�], cuja tra-dução do nome seria “baixa proporção da superfície para o volume”. Essepadrão tem a ver com você tentar prover uma série de serviços a partir deuma interface externa compacta. Isso torna o framework mais simples de serutilizado, muitas vezes fazendo parecer que quem está provendo aquele ser-viço é uma simples classe. O Facade é muitas vezes utilizado para se atingiresse objetivo.

Outro fator que faz com quemuitas vezes os frameworks se pareçam comsimples classes vem do fato dos hotspots serem con�gurados por default comclasses mais comuns de serem usadas. Outra alternativa são os hotspots se-rem con�gurados através de Builders que cria e injeta as classes de acordocom parâmetros e con�gurações. Esse tipo de prática propicia uma Gentle

Learning Curve [�], ou curva de aprendizado suave, pois o desenvolvedorusuário do framework não precisa conhecer e con�gurar todos os hotspotspara instanciar o framework. Porém, caso seja necessário, ele pode ir aos

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 281: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo ��. Indo além do básico

poucos aprendendomais sobre a sua estrutura para alterar o comportamentoquando for necessário.

Outros exemplos de frameworks

Se você já desenvolveu alguma aplicação em Java, muito provavelmentejá usou algum framework. Muitas vezes os desenvolvedores acabam não per-cebendo que a classe que estão desenvolvendo faz parte de um contexto maisamplo e é invocada por um framework emum hotspot. Essa seção irá apresen-tar alguns exemplos de framework para que os conceitos apresentados pos-sam ser vistos a partir de uma perspectiva mais concreta.

Vou começar com o exemplo de frameworks para aplicações web, poisalém de ser um domínio familiar para muitos leitores, eles possuem um hots-pot bem óbvio. O que muda de uma requisição para outra? A funcionalidadeque é executada quando a requisição chega no servidor! Dessa forma, a classeresponsável por tratar uma requisição, chamada de controller em ummodeloarquitetural MVC [��], acaba sendo um hotspot nesse framework. Exemplosde classes desse tipo seriam Servlets na API padrão Java EE, Actions no fra-mework Struts e Managed Beans no JSF. Observe que antes da classe da apli-cação receber o controle da requisição, diversas outras funcionalidades sãoexecutadas pelo framework, indo desde a tradução dos parâmetros até con-trole de acesso.

Um outro exemplo interessante de framework é o Quartz(http://quartz-scheduler.org/) , cujo principal objetivo é realizar o agenda-mento de tarefas. É possível perceber que pelo domínio desse framework, suaexecução não faz sentido fora do contexto de uma aplicação. Por mais que elepossua classes implementadas para as mais variadas opções de agendamento,sem a aplicação ele não tem o que agendar. Dessa forma, o principal hotspotdo framework são as tarefas com lógica da aplicação a serem executadasde acordo com o agendamento realizado. A listagem a seguir mostra umexemplo de uma classe para ser executada.

Listagem ��.� - Exemplo de classe usada como hotspot do frameworkQuartz:

public class ExcluiUsuariosInvalidos implements Job {

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 282: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

��.�. Frameworks Casa do Código

public void execute(JobExecutionContext context)throws JobExecutionException{

// lógica específica da aplicação}

}

Um exemplo de framework um pouco diferente é o Log�J, cujo objetivoé fazer o log de uma aplicação. Dentro desse domínio, o Log�J é bastante�exível, permitindo a gravação de diversos tipos de informações, em dife-rentes formatos e em diferentes locais, como no console, em arquivos, embancos de dados e até remotamente. Diferentemente dos outros frameworkscitados, o Log�J já possui uma biblioteca de classes que podem ser utilizadasem seus hotspots. Dessa forma, uma aplicação poderia utilizar o frameworksem implementar nenhum de seus hotspots, porém se for necessário elapode, por exemplo, criar classes para adição de propriedades especí�casno log ou para que seja realizado o log em um local diferente. A partir dascon�gurações do Log�J, apresentadas na listagem a seguir, é possível perce-ber que existem várias propriedades que recebem nomes de classe, que naverdade estão con�gurando implementações para serem usadas nos hotspots.

Listagem ��.� - Exemplo de arquivo de con�guração do Log�J:

# Configura o nível de log para o logger principal como INFO.log4j.rootLogger=INFO, Console, LogFile

# Configuração do log do consolelog4j.appender.Console=org.apache.log4j.ConsoleAppenderlog4j.appender.Console.layout=org.apache.log4j.PatternLayoutlog4j.appender.Console.layout.ConversionPattern=%d{ISO8601}

[%t] %-5p %C - %m%n

# Configuração do log em arquivolog4j.appender.LogFile=org.apache.log4j.DailyRollingFileAppenderlog4j.appender.LogFile.DatePattern=’.’yyyy-MMlog4j.appender.LogFile.file=/path/to/logfile.loglog4j.appender.LogFile.layout=org.apache.log4j.PatternLayoutlog4j.appender.LogFile.layout.ConversionPattern=%d{ISO8601}

[%t] %-5p %C - %m%

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 283: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo ��. Indo além do básico

Enxergando o GeradorArquivo como um framework

O exemplo do gerador de arquivo utilizado ao longo desse livro pode serconsiderado um framework. Isso porque a classe GeradorArquivo nãoapenas pode ser invocada pela aplicação, como também provê pontos em queessa funcionalidade pode ser estendida e especializada por aplicações. A �-gura ��.� apresenta quais seriam os hotspots e os frozen spots do gerador dearquivos. Exemplos de frozen spots estão no algoritmo de geração de arqui-vos e na combinação dos pós-processadores no Composite. Já os hot spotsseriam o formato de geração dos arquivos, as formas de pós-processamentoe a própria ordem de execução dos pós-processadores.

Fig. ��.�: Hotspots e frozen spots do gerador de arquivo

Uma coisa importante de se ressaltar é que nem sempre precisa ser umaclasse da aplicação a ser utilizada no lugar de um hot spot. Um frameworkpode possuir umabiblioteca de classes que representam implementaçõesmaisgerais que podem ser utilizadas pela aplicação em um ponto de extensão. Porexemplo, no gerador de arquivos, o Compactador e o Criptografador

seriam classesmais gerais que poderiam fazer parte da biblioteca de classes doframework. Porém, nada impede que uma aplicação crie um implementaçãoprópria e utilize naquele ponto.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 284: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

��.�. Utilizando tipos genéricos com os padrões Casa do Código

��.� U��������� ����� ��������� ����� �������“Eu apenas quero crescer criativamente e ser inspirado. Eu não quero fazeralgo genérico ou burro.”– Sienns Miller

Os tipos genéricos são uma funcionalidade de linguagem introduzida noJava �.�. A partir dela, é possível parametrizar tipos para que seja possívelsubstituir partes da assinatura de seusmétodos, como retornos e argumentos,pelo parâmetro passado para o tipo. Um cenário onde isso é muito aplicável épara coleções. Uma lista, por exemplo, ao ser instanciada pode receber comoparâmetro o tipo que deve armazenar, de forma que métodos de recuperaçãoretornem aquele tipo e métodos de inserção aceitem como parâmetro apenasaquele tipo.

O suporte de linguagem a tipos genéricos é uma funcionalidade quevisa primeiramente à segurança de código. Antes de existirem tipos ge-néricos, as coleções no Java podiam receber qualquer objeto. O problemaacontecia quando uma instância era recuperada e precisava ser convertidapara um determinado tipo. Caso o objeto não fosse do tipo esperado, umaClassCastException era lançada. Com tipos genéricos, além do código�car mais limpo sem a necessidade de fazer o cast, erros como esse são pegosjá em tempo de compilação.

Os tipos genéricos também podem ser utilizados na de�nição de inter-faces. Dessa forma, a classe que implementar essa interface pode manter otipo genérico para ser de�nido nas instâncias ou já de�nir o tipo utilizadono momento da implementação. A partir disso, uma mesma interface podeser utilizada para implementação de diversas interfaces na qual apenas o tipoem retornos e parâmetros são alterados. Baseado nesse modelo, outras clas-ses podem utilizar o tipo da interface parametrizado para aceitarem apenasimplementações que, por exemplo, retornarem aquele tipo.

Apesar dos padrões serem ideias mais gerais, os tipos genéricos podemser muito úteis para a criação de implementações mais �exíveis e seguras.Por exemplo, em padrões onde existe a colaboração de duas classes, pode-segarantir em tempo de compilação, a partir do contrato entre as classes, queapenas instâncias com o mesmo tipo genérico podem se relacionar.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 285: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo ��. Indo além do básico

Para exempli�car, vamos utilizar o padrão Observer. Nesse padrão,um observador é aquela classe que possui o papel de receber noti�caçõesdos objetos que está observando. Como nessa de�nição, o objeto passadocomo parâmetro na noti�cação pode variar, este acaba sendo um excelentecandidato para ser um parâmetro genérico da interface que de�ne essecontrato. A listagem a seguir apresenta a interface Observador que de�neum parâmetro genérico relativo à classe do evento utilizada para noti�cação.

Listagem ��.� - De�nição de um observador com tipo genérico:

public interface Observador<E>{public void notificar(E evento);

}

Para que o so�ware faça sentido, uma classe deve poder apenas serobservada por uma que consiga entender os eventos gerados por ela. Ouseja, alguém que gera eventos de uma classe só poderia ser observado poralguém que recebe evento dessa classe, ou de alguma de suas abstrações. Alistagem a seguir apresenta a interface Observavel que também de�ne umtipo genérico. No caso, ela aceita no método adicionaObservador(),uma classe que implemente a interface Observador com o tipo genéricoigual ou sendo uma abstração ao seu.

Listagem ��.� - Restringindo os observadores com um tipo genérico:

public interface Observavel<E>{public void adicionaObservador(Observador<? super E> o);

}

A listagem a seguir mostra como as interfaces poderiam ser implemen-tadas e conectadas. Suponha uma aplicação que trabalhe com comprase vendas de ação, na qual existem eventos que representam as operaçõesrealizadas. Dentro dessa aplicação, existe um evento mais abstrato chamadoOperacaoAcao, que possui duas especializações, sendo essas CompraAcaoe VendaAcao.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 286: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

��.�. Utilizando tipos genéricos com os padrões Casa do Código

Listagem ��.� - Criando e conectando as interfaces com tipos genéricos:

public classObservadorOperacao implements Observador<OperacaoAcao>{

public void notificar(OperacaoAcao evento){ ... }}

public classProcessadorCompra implements Observavel<CompraAcao>{

public voidadicionaObservador(Observador<? super CompraAcao> o){

...}

}

ObservadorCompra oc = new ObservadorCompra();ProcessadorCompra pc = new ProcessadorCompra();pc.adicionaObservador(oc);

Nesse contexto, considere uma classe chamada ObservadorCompra

que implemente a interface Observer com o tipo genérico CompraAcao

e uma classe chamada ProcessadorCompra que implemente a interfaceObservavel com o tipo genérico CompraAcao. Nesse caso, o observa-dor poderia ser inserido na classe observável, pois uma classe que recebeeventos do tipo OperacaoAcao vai saber lidar com um evento do tipoCompraAcao, por ser sua superclasse. Porém, uma classe que, por exemplo,implementasse Observavel<VendaAcao> não poderia ser passada para aclasse ProcessadorCompra, por tratar um tipo de evento diferente do ge-rado por essa classe.

A utilização de tipos genéricos para implementação de padrões ajuda nacriação de soluções que permitem umamaior segurança de código. No exem-plo apresentado do padrão Observer, a própria estrutura montada impedeque uma classe não compatível seja passada como parâmetro. Esse tipo derestrição não seria imposta se o método da interface Observador aceitasseum Object como parâmetro. Por outro lado, se fosse preciso restringir otipo observado sem usar tipos genéricos, seria necessária a criação de uma

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 287: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo ��. Indo além do básico

interface para cada caso.O uso do Observer foi apenas um exemplo para mostrar como a uti-

lização de tipos genéricos pode ajudar a amarrar os tipos de uma soluçãocom padrões para evitar inconsistências. Outros padrões também podem serutilizados para se bene�ciar desse recurso de linguagem [�]. O Abstract

Factory, por exemplo, pode utilizar tipos genéricos para assegurar algumrelacionamento entre os tipos retornados e o Command pode se utilizar desserecurso para permitir que as classes declarem no tipo o retorno resultante desua execução.

��.� P������ ��� T���-������D����������“TDD é uma ideia louca que funciona.”– Kent Beck

O Test-driven Development, TDD, é uma técnica de desenvolvimento eprojeto de so�ware na qual os testes são criados antes do código de produção.É uma técnica que vem se tornando cada vezmais popular nomercado emaisatenção da academia. Com essa técnica, as classes vão sendo desenvolvidasincrementalmente em pequenos ciclos, os quais alternam entre a criação deum teste, o desenvolvimento de código de produção para satisfazer o testeadicionado e a refatoração do código criado.

Durante a criação dos testes, o trabalho do desenvolvedor é focado na de-�nição da API externa da classe e do seu comportamento esperado dentro decada cenário de�nido. A partir de cada teste adicionado, a tarefa a seguir éimplementar a solução mais simples possível na classe que está sendo desen-volvida, de forma ao novo teste e os anteriores serem todos satisfeitos. Comonesse momento o objetivo é fazer o teste passar, logo que isso é atingido, se-gue um momento para se re�etir sobre o design e a clareza do código. Dessaforma, antes de seguir o desenvolvimento com a introdução demais um teste,o código é refatorado e a manutenção do comportamento veri�cada com aexecução dos testes.

Existem hoje muitos desenvolvedores que utilizam o TDD mais comouma técnica de desenvolvimento do que uma técnica de projeto. Em outraspalavras, eles projetam como serão as classes e como elas irão se relacionar,

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 288: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

��.�. Padrões com Test-driven Development Casa do Código

e depois implementam a funcionalidade utilizando TDD. Quando ele é uti-lizado também como uma técnica de projeto, a modelagem da aplicação vaiacontecendo de forma evolutiva, incluindo a de�nição das colaborações e doscontratos entre as classes. A seção a seguir fala sobre como o projeto ocorrecom TDD e o papel dos padrões nesse processo.

Projetando com TDD e padrões

Oprojeto de uma classe comTDD acontece em grande parte na de�niçãodo teste. É nele que você vai expressar a API externa da classe e como ela éutilizada pelos seus clientes. O fato dos testes serem criados antes favorececertas características na classe que está sendo desenvolvida, o que favorece asua testabilidade. Uma dessas características é a coesão, pelo fato de que émais simples testar uma classe com uma responsabilidade bem de�nida doque classes que acumulam diversas responsabilidades. Isso ajuda em umme-lhor particionamento das classes para umamelhor divisão de responsabilida-des.

Outra questão que é trabalhada durante os testes é o desacoplamento daclasse que está sendo desenvolvida das suas dependências. Apesar de nãoser obrigatório no TDD, normalmente utilizam-se testes de unidade no de-senvolvimento. Isso é feito para que o teste possa focar exclusivamente nasresponsabilidades daquela classe. Para isso, é necessário inserir objetos falsos(mock objects) na classe testada, e simular diferentes comportamentos dasdependências para a criação dos cenários de teste.

Para possibilitar a inserção dosmock objects, é preciso que a classe possuaummecanismo no qual as dependências possam ser substituídas. Além disso,é necessário que seja de�nido um contrato, normalmente através de uma in-terface, que formalize a interação entre a classe desenvolvida e as dependên-cias. Essas necessidades de teste resultam na utilização de um conjunto deboas práticas que acabam favorecendo o design da aplicação.

Mas será que apenas esses princípios de TDD são su�cientes para mo-delar um so�ware? Na verdade, os testes servem como um mecanismo paraexpressar o design desejado para uma classe, mas se o desenvolvedor não sou-ber qual deve ser a solução para o problema, a utilização dessa técnica �carárestrita. O mesmo se aplica para a modelagem com o uso de UML, pois ela é

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 289: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo ��. Indo além do básico

apenas uma ferramenta para expressar o modelo da aplicação através de di-agrama, e de nada adianta se o desenvolvedor não souber quais classes vairepresentar. Sendo assim, não importa qual a forma ou técnica na qual o de-sign da aplicação está sendo de�nido, os padrões são importantes para que odesenvolvedor possa direcionar sua solução.

No caso do TDD, já no primeiro teste, o desenvolvedor precisa de�nircomo a classe será criada. Nesse momento, já pode entrar em ação o conhe-cimento a respeito de padrões de criação. Será utilizado um construtor ouum Static Factory Method? É preciso utilizar um Singleton para aaplicação ter apenas uma instância dessa classe? Em um segundo momento,caso o processo de criação das classes for complexo o su�ciente, pode-se par-tir para a criação de um Builder, o qual poderia ser desenvolvido em suaprópria classe de teste.

O Dependency Injection é um padrão muito utilizado quando seusa TDD, principalmente quando realmente procura-se isolar a classe testadade suas dependências. Isso é feito para facilitar a substituição da dependênciapor mock objects pelo teste. Porém, como foi visto no Capítulo �, o padrãoService Locator poderia também ser utilizado nessas situações sem pre-juízo à modularidade.

Quando um padrão é utilizado, normalmente existemmais de uma classeenvolvida, sendo cada uma com uma responsabilidade bem de�nida. Ao seoptar por desenvolver uma solução baseada em um padrão a partir de TDD,deve-se a partir dos testes direcionar a interface da classe e de suas depen-dências para o uso desse padrão. Imagine um exemplo em que o padrãoVisitor estivesse sendo utilizado. Nos testes de classes que implementam ainterface visitante, que é passada como parâmetro, seria veri�cado se as cha-madas aos métodos gerariam o efeito esperado. Nos testes das classes visi-tadas, os testes veri�cariam se os métodos do objeto visitante passado comoparâmetro seriam invocados corretamente. Esse é um exemplo no qual o pa-drão escolhido interfere na forma como os testes seriam criados. Mesmo as-sim, a interface que serve como contrato entre essas duas classes poderia irsendo modelada de forma incremental através do TDD.

A refatoração também é uma fase do ciclo de TDD que é muito impor-tante para o projeto do so�ware que está sendo desenvolvido. Como no TDD

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 290: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

��.�. Padrões com Test-driven Development Casa do Código

o projeto ocorre de forma evolutiva e de acordo com as necessidades corren-tes, a reestruturação da solução que está sendo adotada é algo feito com umacerta frequência. Nesse contexto, como nem sempre a necessidade do uso deum padrão acontece na primeira versão da classe, é natural que eles sejamimplementados a partir de refatorações. Principalmente quando uma classecomeça a acumular responsabilidades ou aparece uma duplicação de código.Ter os padrões como um direcionamento para as refatorações, como foi mos-trado no decorrer dos capítulos, é uma excelente forma de dirigir o design daaplicação para boas soluções de forma evolutiva.

Concluindo, pormais que oTDD favoreça a criação de classesmais coesase desacopladas, isso não é o su�ciente para o projeto de uma aplicação comoum todo. Nesse contexto, a utilização de padrões, tanto para direcionar ostestes, quanto para o alvo de refatorações, pode ter um impacto positivo nassoluções desenvolvidas.

Exemplo de TDD usando o padrão Observer

Para ilustrar a utilização de padrões com TDD, vamos imaginar o exem-plo de uma aplicação de e-commerce. Nela, ao se adicionar um produto nocarrinho de compras, existem diversas funcionalidades que devem ser aci-onadas. Por exemplo, deve-se registrar essa informação para as estatísticasdo produto, deve-se adicionar a categoria do produto nos interesses do cli-ente e deve-se criar uma reserva para uma unidade do produto no estoque.A inclusão de todas essas funcionalidades no carrinho de compras poderiasobrecarregar essa classe e torná-la acoplada a diversos subsistemas.

Um padrão adequado para esse tipo de problema é o Observer, pois ocarrinho de compras poderia noti�car as classes interessadas em seus even-tos sem estar acoplado a elas. Nesse caso, a responsabilidade da classe querepresenta o carrinho de compras seria simplesmente noti�car os observado-res dos eventos ocorridos. Baseando-se nisso, os testes dessa classe deveriamapenas focar se as noti�cações estão sendo feitas na hora e com as informa-ções corretas.

O primeiro passo seria a criação de um teste que de�nisse como seria afuncionalidade de noti�cação do carrinho de compras, conforme o códigomostrado na próxima listagem. Observe que é instanciada uma classe,

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 291: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo ��. Indo além do básico

chamada de MockObservador para simular a existência de um observadordurante o teste. Esse mock object é adicionado como um observador naclasse testada e o teste veri�ca se, ao adicionar um produto no carrinho decompras, o observador é noti�cado com o produto adicionado.

Listagem ��.� - Teste que de�ne padrão Observer e veri�ca recebimento denoti�cação:

@Testpublic void testeNotificacao(){

MockObservador mock = new MockObservador();CarrinhoCompras cc = new CarrinhoCompras();cc.addObservador(mock);

Produto p = new Produto("Cabo HDMI", 30.0);cc.adicionar(p);

assertTrue(mock.recebeuNotificacao());assertEquals(p, mock.produtoRecebido());

}

Em seguida, é preciso de�nir a classe MockObservador queestá sendo utilizada no teste. Essa classe deve implementar a inter-face esperada pelo método addObservador() da classe testada,no caso ObservadorCarrinho. Observe que essa classe tambémdeve possuir os métodos utilizados no teste para veri�cação, no casorecebeuNotificacao() e produtoRecebido(). Veja que a imple-mentação dessa classe deve fazer sentido para o teste, ou seja, o métodonotificaProduto() exigido pela interface implementada deve armazenaro resultado para poder ser veri�cado no teste.

Listagem ��.� - Mock object que simula um observador:

public class MockObservador implements ObservadorCarrinho {

private Produto p;

@Override

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 292: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

��.�. Posso aplicar esses padrões na arquitetura? Casa do Código

public void notificaProduto(Produto p){this.p = p;

}public boolean recebeuNotificacao(){

return p != null;}public Produto produtoRecebido(){

return p;}

}

O que é interessante notar nesse caso, é que a interface e os seus métodossão de�nidos no momento em que está sendo criado o teste. O teste é ummecanismo que está sendo utilizado para a de�nição desse contrato, porém éa partir do padrão Observer que a solução foi construída.

Nesse caso, após a de�nição dos testes da classe observada, se-riam criadas as classes observadoras, que implementariam a interfaceObservadorCarrinho. Se o TDD continuasse a ser utilizado nesse desen-volvimento, seria criada uma nova bateria de testes para cada classe. Nessestestes, o método notificaProduto() seria invocado simulando uma no-ti�cação de uma classe que está sendo observada e veri�cando o comporta-mento esperado da classe emquestão. Quando as classes estivessemdesenvol-vidas, um teste de integração, envolvendo o carrinho de compras e as classesobservadoras, também poderia ser criado para veri�car se as funcionalidadesse integram de forma correta.

��.� P���� ������� ����� ������� �� �������-�����

“Não existem regras na arquitetura para um castelo nas nuvens.”– G. K. Chesterton

Muitos dos padrões apresentados nesse livro também podem se aplicarem um contexto mais amplo de uma arquitetura. Nesse caso, em vez de clas-ses, os participantes dos padrões seriam componentes e até mesmo subsiste-mas de um so�ware. Como os padrões são ideias e não estão ligados a im-

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 293: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo ��. Indo além do básico

plementações especí�cas, os problemas e as soluções dos padrões podem seraplicados em uma escala diferente.

Pegando novamente como exemplo o padrão Observer, da mesmaforma que uma classe pode precisar receber noti�cações de outra, um subsis-tema pode precisar receber atualizações de outros subsistemas remotos. En-quanto dentro de um so�ware orientado a objetos os contratos são �rmadosa partir de interfaces e classes abstratas, entre sistemas remotos são utilizadosprotocolos de comunicação. A solução certamente precisa de uma adaptação,mas certamente a mesma ideia pode ser usada.

Porém, a utilização desses padrões deve ser feita commuito cuidado, poisapesar das soluções serem similares, as consequências de seu uso podem serbem diferentes. Por exemplo, em uma arquitetura, quando lidamos com sub-sistemas que estão distribuídos em uma rede, sempre devemos considerar apossibilidade de um dos nós envolvidos falhar. Isso é algo que não precisa-mos considerar quando estamos lidando com classes implantadas em umamesma máquina virtual. Essa é apenas uma das questões que devem ser con-sideradas; existem outras, como a tolerância a esse tipo de falha, questões dedesempenho em relação à forma como é feita a comunicação, entre outras.

Para exempli�car essa questão, considere o padrão Proxy. Como foivisto, ele pode ser utilizado para proteger o acesso a um determinado objeto.Normalmente, quando isso é feito, é adicionada uma funcionalidade nesseacesso, como, por exemplo, alguma veri�cação de segurança ou validação dosdados. Dessa forma, o Proxy �ca entre o cliente e a classe que está sendoacessada.

De forma similar, o Proxy também pode ser aplicado de formamais abs-trata a arquiteturas. Nessa caso, ele irá �car como intermediário entre doissubsistemas. Ele irá disponibilizar os mesmos serviços e utilizar o mesmoprotocolo que o subsistema original, para que seja transparente para o clientea sua existência. Em outras palavras, o cliente deve acessar o Proxy como seestivesse acessando o subsistema original. A �gura ��.�mostra a representa-ção desse padrão implementado no contexto de uma arquitetura. Nesse caso,Proxy também poderia adicionar alguma funcionalidade nesse acesso, comode segurança ou registro de auditoria, damesma forma que o padrão original.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 294: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

��.�. Posso aplicar esses padrões na arquitetura? Casa do Código

Fig. ��.�: Estrutura de um Proxy para Criação de Arquitetura

Porém, como foi dito, o uso do Proxy em uma arquitetura pode ter con-sequências diferentes de seu uso com objetos. Por exemplo, se houver algumproblema com a máquina onde estiver o Proxy, o cliente perderá acessoao serviço, mesmo este estando em funcionamento. Isso adiciona um novopossível ponto de falha na arquitetura, o que é uma consequência negativaem relação à implementação com objetos. Por outro lado, o Proxy tambémpode ser responsável pelo roteamento das mensagens entre o cliente e o ser-vidor, permitindo que o servidor mude sua localização sem afetar os clientes,o que seria uma nova consequência positiva.

Muitas vezes, é complicado separar o que é uma solução de design doque é uma solução arquitetural. O importante é não utilizar as soluções dospadrões cegamente e sempre considerar as consequências antes da sua utiliza-ção. Lembre-se que existem soluções alternativas que podem resolver o pro-blema de uma forma diferente, gerando umoutro conjunto de consequências.Trabalhar com arquitetura é saber fazer trocas, por exemplo, sacri�cando de-sempenho para aumentar a segurança, e escolher as soluções de forma a aten-der os requisitos não-funcionais do sistema.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 295: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo ��. Indo além do básico

��.� C��������� �� �������“Fundamental a qualquer disciplina relacionada a ciência ou engenharia é umvocabulário para expressar seus conceitos, e uma linguagem para os relacionarjuntos.”– Brad Appleton

Após ler esse livro, você pode se perguntar: Como surgem novos pa-drões? Onde eu encontro novos padrões? Como eu faço para trabalhar comisso? O Hillside Group é uma organização internacional sem �ns lucrativoscujo objetivo é fomentar a documentação de padrões para as mais diferentesáreas, principalmente as relacionadas ao desenvolvimento de so�ware. Dessaforma, ela auxilia na organização de diversas conferências focadas empadrõesao redor do mundo e ajuda autores a amadurecerem seus padrões para suapublicação, seja em formato de artigos ou de livros.

As conferências de padrões, conhecidas como PLoP (Pattern Languagesof Program), acontecem ao redor domundo e recebem submissões de artigoscom padrões, linguagens de padrões, ou relativos a utilização de padrões. Aconferência principal acontece todo ano nos EUA e em ���� irá completar�� edições. Pessoalmente já tive o prazer de participar de algumas edições,e inclusive de ser o organizador do PLoP ����, que aconteceu em Tucson,no Arizona. São conferências normalmente para um número pequeno depessoas (normalmente entre �� e �� pessoas) e que são muito diferentes deuma conferência cientí�ca tradicional.

Existem outras conferências PLoP ao redor do mundo como o EuroPLoP(Alemanha), AsianPLoP (Japão), KoalaPLoP (Austrália), VikingPLoP (paísesda Escandinávia), e, o estreante de ����, GuruPLoP (Índia). Existem tambémconferências temáticas, onde pessoas interessadas se reúnempara discutir pa-drões de um domínio especí�co, como o Meta PLoP (metaprogramação), oScrum PLoP (padrões para documentar práticas do Scrum) e o ParaPLoP(programação paralela). No Brasil, a cada dois anos, ocorre o SugarLoafPLoP,o evento latino-americano de padrões, e nos anos intermediários um Mini-PLoP, uma versão reduzida do evento.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 296: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

��.�. Comunidade de padrões Casa do Código

O ��� ��� ���������� �� ��������

Uma linguagem de padrões é um conjunto de padrões que docu-mentam soluções e boas práticas para um determinado domínio. Porexemplo, eu escrevi uma linguagem de padrões para a construção deframeworks que utilizam anotações e metadados. Mais do que simples-mente uma solução pontual, a linguagem de padrões possui soluções quesinergicamente podem ser combinadas e se complementam, sendo umaforma de documentar o conhecimento de modelagem sobre aquele do-mínio.

A submissão de um padrão para um PLoP possui um processo diferentedo que a submissão de artigos para outras conferências. Ao submeter umpadrão, após uma �ltragem inicial, ele é atribuído a um shepherd (pastor). Oshepherd é uma especialista na área de padrões cujo papel é orientar e ajudaro autor a evoluir e amadurecer o trabalho submetido. Sendo assim, durantecerca de um mês e meio, o autor em conjunto com o shepherd irão interagire cooperar para que o trabalho evolua durante esse período. Só depois dissoque o artigo realmente será avaliado pelo comitê e decidido se ele será aceitoou não na conferência.

Na conferência, o autor precisa participar de uma seção chamada deWri-ters Workshop, que é bem diferente de uma seção tradicional onde o autorsimplesmente apresenta o seu trabalho. Nessa seção, um grupo irá discutir otrabalho do autor levando em consideração o entendimento que obtiveramsobre o trabalho, apontando pontos de melhoria e dando sugestões. O autorparticipa da discussão apenas no inicio, quando se lê uma pequena parte doartigo, e no �m, quando pode tirar dúvidas sobre algum comentário. Pormaisangustiante que seja ver as pessoas discutindo sobre seu trabalho e não poderopinar ou explicar alguma coisa, esse é um processo com o qual se tem umgrande feedback a respeito de como outras pessoas enxergam o seu trabalho.

Pessoalmente gosto muito do estilo da comunidade de padrões, que temumespíritomuito colaborativo, focado emajudar aqueles que participamdelaa documentarem seus padrões da melhor forma possível. Nas conferências,além do estilo peculiar das apresentações, ainda existe toda uma cultura que

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 297: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Capítulo ��. Indo além do básico

valoriza muito a interação entre as pessoas. Existem, por exemplo, tradiçõescomo a de jogos para aproximar os participantes e troca de lembranças emque cada um traz normalmente algo típico de sua região ou país.

��.� E ������“Toda vez que você tem medo de fazer alguma coisa e você faz, ela te deixamais forte. Mesmo que você falhe.”– Fred Bartlit

Durante esse livro foi trilhado um caminho que passou por diversos pa-drões que mostram e exempli�cam técnicas para a criação do projeto de umso�ware orientado a objetos. Diferentemente de quando se aprende a usarum novo framework ou uma nova API, quando se está adquirindo conheci-mento, saber projetar um so�ware é uma habilidade. E uma habilidade sóse aprende de verdade com prática, sendo que o conhecimento é necessário,porém não su�ciente.

Sendo assim, o próximo passo agora é tentar colocar em prática os pa-drões e técnicas apresentados neste livro. Quando desenvolver um so�wareprocure olhar para ele com olhar mais crítico, tentando identi�car quais pa-drões ele já implementa, muitas vezes de forma induzida pelos frameworksutilizados. Uma dica pode estar nos próprios nomes das classes, que comfrequência “entregam” o padrão utilizado. Procure raciocinar em cima da-quela solução e identi�car quais foram os motivos para a utilização daquelepadrão e quais benefícios ele está trazendo para o design. Aprendemos muitoobservando soluções prontas e abstraindo o que foi utilizado na sua concep-ção.

Em seguida, veja quais problemas você consegue identi�car e como po-deria ser feito para melhorar. Muitas vezes, um padrão deixou de ser aplicadoem uma situação em que ele era adequado, porém também não é raro encon-trar implementações de padrões incorretas, em que eles foram utilizados nocontexto errado. A duplicação de código, por exemplo, é um indício de quealgo não está cheirando bem naquela parte do so�ware. Veja então como al-gum padrão poderia te ajudar na resolução daquele problema, considerandoas consequências e qual o peso de cada uma para o cenário em questão.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 298: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

��.�. E agora? Casa do Código

Adicionalmente, ao criar uma nova solução, pense em como algum pa-drão poderia te ajudar na resolução dos problemas envolvidos. Procure contera ansiedade de aplicar diversos padrões em sequência sem ter algumamotiva-ção nos requisitos. Também não deixe de combinar os padrões nas situaçõesem que isso for adequado. Projetar um so�ware consiste em saber equilibraros requisitos através da combinação de soluções para os pequenos problemas,de forma a se obter um resultado �nal adequado. Teste suas soluções! Criecenários baseados em histórias do cliente e veja se seu design se encaixa. Useboas práticas e mantenha o código limpo para que seja fácil de refatorá-lofrente a novas necessidades.

Finalmente, desenvolvimento de so�ware é algo que se estuda durantetoda sua carreira! Conheça novos padrões, estude como os padrões se encai-xam nas novas tecnologias, e busque os padrões especí�cos para os domíniosrelacionados a sua arquitetura. Além disso, lembre-se que design de so�wareé uma atividade criativa, então evite formulas prontas, tenha sempre uma vi-são crítica e utilize os padrões como uma ferramenta da sua criatividade.

Boa sorte!

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 299: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Índice Remissivo

Índice Remissivo

Abstract Factory, ���, ���, ���, ���, ���Adapter, ���, ���–���, ���, ���, ���Anti-Corruption Layer, ���

Bridge, ��, ��, ��, ��, ��, ���, ���, ���Builder, ���, ���–���, ���–���, ���,

���, ���

Chain of Responsibility, ��, ��, ��,���, ���, ���, ���, ���, ���, ���,���

Command, ���, ���–���, ���–���, ���,���, ���, ���, ���

Composite, ��–��, ��, ���, ���, ���,���, ���, ���, ���, ���, ���,���, ���, ���

Data Access Object, ��, ���–���Decorator, ���, ���, ���, ���, ���, ���,

���, ���, ���Dependency Injection, ���, ���, ���–

���, ���, ���, ���–���, ���,���, ���, ���, ���

Double Dispatch, ���, ���–���, ���,���, ���

Dynamic Factory, ���, ���, ���, ���–���, ���, ���, ���, ���

Facade, ���–���, ���, ���, ���, ���,���, ���, ���

Factory Method, ��, ��Flyweight, ���, ���–���, ���, ���,

���, ���, ���

Gentle Learning Curve, ���

Low Surface-to-volume Ratio, ���

Mediator, ���–���, ���, ���

Null Object, ��, ��, ��, ��, ��, ��, ���,���

Observer, ��, ��, ��–��, ���, ���, ���,���, ���, ���, ���, ���, ���,���

Prevalent System, ���Proxy, ���, ���–���, ���–���, ���, ���,

���, ���, ���, ���, ���, ���,���, ���, ���, ���, ���, ���,���, ���

Service Locator, ���–���, ���, ���–���,���, ���, ���

Singleton, ���, ���, ���, ���, ���State, ��, ��, ��, ��, ��Static Factory, ���

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 300: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Índice Remissivo Casa do Código

Static Factory Method, ���, ���, ���–���, ���, ���, ���, ���, ���

Strategy, ��–��, ��, ��, ��, ��, ��, ��,��, ���, ���, ���, ���

Template Method, ��, ��–��, ��, ��,��, ��, ��, ��, ���, ���, ���,���, ���

Visitor, ���, ���, ���–���, ���, ���,���

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 301: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Referências Bibliográ�cas

Referências Bibliográ�cas

[�] Alex Marques Campos Alexandre Gazola. Padrões de projeto com ge-nerics - revista mundoj, edição ��.

[�] Kent Beck. Implementation Patterns. Addison-Wesley Professional,����.

[�] Joshua Bloch. E�ective Java, Segunda edição. Addison-Wesley, ����.

[�] Joseph W. Yoder Brian Foote. �e sel�sh class - pattern languages ofprogram design �. ����.

[�] Murray Silverstein Christopher Alexander, Sara Ishikawa. A PatternLanguage: Towns, Buildings, Construction. OxfordUniversity Press, ����.

[�] John Crupi Deepak Alur, Dan Malks. Core J�EE Patterns: Best Practicesand Design Strategies. Prentice Hall, ����.

[�] Scott StanlickDon Brown, ChadMichael Davis. Struts � in Action. ����.

[�] Ralph Johnson Don Roberts. Evolving frameworks: A pattern languagefor developing object-oriented frameworks - proceedings of the thirdconference on pattern languages and programming. ����.

[�] Uirá Kulesza Clovis Fernandes Eduardo Guerra, Felipe Alves. A re-ference architecture for improving the internal structure of metadata-based frameworks - journal of systems and so�ware, volume ��, issue �.����.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 302: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Referências Bibliográ�cas Casa do Código

[��] Ralph Johnson JohnVlissides Erich Gamma, RichardHelm. Design Pat-terns: Elements of Reusable Object-Oriented So�ware. Addison-WesleyProfessional, ����.

[��] Eric Evans. Domain-Driven Design: Tackling Complexity in the Heart ofSo�ware. Addison-Wesley Professional, ����.

[��] Martin Fowler. Refactoring: Improving the Design of Existing Code.Addison-Wesley Professional, ����.

[��] Martin Fowler. Inversion of control containers and the dependency in-jection pattern. http://martinfowler.com/articles/injection.html, ����.

[��] Martin Fowler. Fluent interface. http://martinfowler.com/bliki/FluentInterface.html, ����.

[��] Martin Fowler. Domain-Speci�c Languages. Addison-Wesley Professio-nal, ����.

[��] Joshua Kerievsky. Refactoring to Patterns. Addison-Wesley Professional,����.

[��] JosephW. Yoder LeonWelicki, Rebecca Wirfs-Brock. �e dynamic fac-tory pattern - proceedings of the ��th conference on pattern languagesand programming. ����.

[��] James Noble. Classifying relationships between object-oriented designpatterns - australian so�ware engineering conference. ����.

[��] Cli�on Nock. Data Access Patterns: Database Interactions in Object-Oriented Applications. Addison-Wesley Professional, ����.

[��] Larry O’Brien. Design patterns �� years later: An interview with erichgamma, richard helm, and ralph johnson. http://www.informit.com/articles/article.aspx?p=�������, ����.

[��] Java Community Process. Jsr-��� javatm servlet �.�. http://jcp.org/aboutJava/communityprocess/�nal/jsr���/index.html, ����.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]

Page 303: Design patterns-com-java-projeto-orientado-a-objetos-guiado-por-padroes

Casa do Código Referências Bibliográ�cas

[��] Brian Foote Ralph Johnson. Designing reusable classes - journal ofobject-oriented programming. ����.

[��] Trygve Reenskaug. �e model-view-controller (mvc) - its past andpresent. http://heim.i�.uio.no/~trygver/����/javazone-jaoo/MVC_pattern.pdf, ����.

[��] Nat Pryce Steve Freeman. Evolving an embedded domain-speci�c lan-guage in java - proc. oopsla ����. ����.

[��] Tim Mackinnon Joe Walnes Steve Freeman, Nat Pryce. Mock roles, notobjects - proc. oopsla ����. ����.

[��] Ovidiu Tudosa. Prevalent system. https://wiki.engr.illinois.edu/download/attachments/���������/prevsystem_otudosa�.pdf, ����.

[��] Bobby Woolf. �e null object pattern - pattern languages of programdesign �. ����.

[��] Michel de Champlain Yun Mai. A pattern language to visitors - pro-ceedings of the �th conference on pattern languages and programming.����.

���

E-book gerado especialmente para Rodrigo de Alcantara Hiemer - [email protected]