Vous êtes sur la page 1sur 360

Solues reutilizveis de software orientado a objetos

1994 M. C. Escher / Gordon Art Baam Holland. Todos os direitos reservados.

ERICH GAMMA RICHARD HELM RALPH JOHNSON JOHN VLISSIDES

Design Patterns

Padres de Projeto

P124

Padres de projeto [recurso eletrnico] : solues reutilizveis de software orientado a objetos / Erich Gamma ... [et al.] ; traduo Luiz A. Meirelles Salgado. Dados eletrnicos. Porto Alegre : Bookman, 2007. Editado tambm como livro impresso em 2000. ISBN 978-85-7780-046-9 1. Engenharia de Sistemas Programao de Computadores. I. Gamma, Erich CDU 681.3.02 Catalogao na publicao: Juliana Lagas Coelho CRB 10/1798

ERICH GAMMA RICHARD HELM RALPH JOHNSON JOHN VLISSIDES

Traduo: Luiz A. Meirelles Salgado Consultoria, superviso e reviso tcnica desta edio: Fabiano Borges Paulo, MSc
Consultor em Engenharia de Software

Reimpresso 2008

2000

Obra originalmente publicada sob o ttulo Design patterns elements of reusable object-oriented software Addison Wesley Longman, Inc., 1995 Publicado conforme acordo com a Addison Wesley Longman, Inc. ISBN 0 - 201- 63361- 2 Capa: Mario Rhnelt (com ilustrao de M. C. Escher/Cordon Art/Holanda) Preparao do original: Rafael Corsetti Superviso editorial: Arysinha Jacques Affonso Editorao eletrnica: Laser House

Reservados todos os direitos de publicao, em lngua portuguesa, ARTMED EDITORA S.A. (BOOKMAN COMPANHIA EDITORA uma diviso da ARTMED EDITORA S.A.) Av. Jernimo de Ornelas, 670 - Santana 90040-340 Porto Alegre RS Fone (51) 3027-7000 Fax (51) 3027-7070 proibida a duplicao ou reproduo deste volume, no todo ou em parte, sob quaisquer formas ou por quaisquer meios (eletrnico, mecnico, gravao, fotocpia, distribuio na Web e outros), sem permisso expressa da Editora. SO PAULO Av. Anglica, 1091 - Higienpolis 01227-100 So Paulo SP Fone (11) 3665-1100 Fax (11) 3667-1333 SAC 0800 703-3444 IMPRESSO NO BRASIL PRINTED IN BRAZIL

Para Karin E.G. Para Sylvie R.H. Para Faith R.J Para Dru Ann e Matthew Josu 25:15b J.V.

Prefcio

Este livro no uma introduo tecnologia orientada a objetos ou ao projeto orientado a objetos. J existem muitos livros bons sobre esses tpicos. Este livro pressupe que o usurio seja razoavelmente fluente em pelo menos uma linguagem de programao orientada a objetos e tenha alguma experincia em projeto orientado a objetos. Definitivamente, no deve ser o tipo de usurio que recorre ao dicionrio quando mencionamos tipos e polimorfismo, ou herana de interface por oposio a herana de implementao. Por outro lado, este livro tampouco um tratado tcnico avanado. um livro de padres de projeto (design patterns) que descreve solues simples para problemas especficos no projeto de software orientado a objetos. Padres de projeto captura solues que foram desenvolvidas e aperfeioadas ao longo do tempo. Da os padres de projeto no serem os projetos que as pessoas tendem a gerar inicialmente. Eles refletem modelagens e recodificaes, nunca relatadas, resultado dos esforos dos desenvolvedores por maior reutilizao e flexibilidade em seus sistemas de software. Padres de projeto captura essas solues em uma forma sucinta e facilmente aplicvel. Os padres de projeto no exigem nenhum recurso incomum da linguagem, nem truques de programao surpreendentes para impressionar seus amigos e gerentes. Todos podem ser implementados em linguagens orientadas a objetos comuns, embora possam exigir um pouco mais de trabalho que solues ad hoc. Porm, o esforo adicional invariavelmente traz dividendos na forma de maior flexibilidade. Uma vez que voc tenha compreendido os padres de projeto e tenha exclamado Ah! (e no somente Hum?) na sua experincia com eles, nunca mais pensar sobre projeto orientado a objetos da mesma maneira. Voc ter percepes que tornaro seus projetos mais flexveis, modulares, reutilizveis e compreensveis o que, afinal, a razo pela qual voc est interessado na tecnologia orientada a objetos, certo?

viii

PREFCIO

Uma palavra de advertncia e encorajamento: no se preocupe se no compreender este livro completamente na primeira leitura. Ns tambm no o compreendemos totalmente quando da sua primeira redao! Lembre que este no um livro para ser lido uma vez e deixado numa prateleira. Esperamos que recorra a ele vezes seguidas para solues de projeto e para inspirao. Este livro teve uma longa gestao. Passou por quatro pases, trs casamentos de seus autores e o nascimento de dois descendentes dos mesmos. Muitas pessoas tiveram participao no seu desenvolvimento. Nossos agradecimentos especiais a Bruce Anderson, Kent Beck e Andr Weinand pela inspirao e por aconselhamento. Tambm agradecemos queles que revisaram as minutas do manuscrito: Roger Bielefeld, Grady Booch, Tom Cargill, Marshall Cline, Ralph Hyre, Brian Kernighan, Thomas Laliberty, Mark Loren, Arthur Riel, Doug Schmidt, Clovis Tondo, Steve Vinoski e Rebecca WirfsBrock. Somos gratos ainda equipe da Addison-Wesley pela ajuda e pacincia: Kate Habib, Tiffany Moore, Lisa Raffaele, Pradeepa Siva e John Wait. Agradecimentos especiais a Carl Kessler, Danny Sabbah e Mark Wegman da IBM Research pelo apoio incondicional a este trabalho. Por ltimo, mas certamente no menos importante, agradecemos a todos na Internet e outros lugares que apresentaram comentrios sobre verses dos padres, ofereceram encorajamento e reafirmaram que o que estvamos fazendo valia a pena. Estas pessoas incluem, mas no se limitam somente s mencionadas, Jon Avotins, Steve Berczuk, Julian Berdych, Mathias Bohlen, John Brant, Allan Clarke, Paul Chisholm, Jens Coldewey, Dave Collins, Jim Coplien, Don Dwiggins, Gabriele Elia, Doug Felt, Brian Foote, Denis Fortin, Ward Harold, Hermann Hueni, Nayeem Islam, Bikramjit Kalra, Paul Keefer, Thomas Kofler, Doug Lea, Dan LaLiberte, James Long, Ann Louise Luu, Pundi Madhavan, Brian Marick, Robert Martin, Dave McComb, Carl McConnell, Christine Mingins, Hanspeter Mssenbck, Eric Newton, Marianne Ozkan, Roxsan Payette, Larry Podmolik, George Radin, Sita Ramakrishnan, Russ Ramirez, Alexander Ran, Dirk Riehle, Bryan Rosenburg, Aamod Sane, Duri Schmidt, Robert Seidl, Xin Shu e Bill Walker. No consideramos esta coleo de padres de projeto completa e esttica; ela mais um registro dos nossos pensamentos atuais sobre projetos. Comentrios sero bem-vindos, sejam crticas dos nossos exemplos, das referncias e dos usos conhecidos que esquecemos ou padres que deveramos ter includo. Voc pode nos escrever, aos cuidados da AddisonWesley, ou enviar correio eletrnico para design-patterns@cs.uiuc.edu. Tambm pode obter cpias-fonte para o cdigo nas sees de Exemplos de Cdigos, enviando a mensagem send design pattern source para design-patterns-source@cs.uiuc.edu. E agora existe uma pgina da Web em http://st-www.cs.uiuc.edu/users/patterns/DPBook/ DPBook.html para obter atualizaes. Mountain View, California Montreal, Quebec Urbana, Illinois Hawthorne, New York E.G. R.H. R.J. J.V.

Apresentao

Todas as arquiteturas orientadas a objetos bem-estruturadas esto cheias de padres. De fato, uma das maneiras de medir a qualidade de um sistema orientado a objetos avaliar se os desenvolvedores tomaram bastante cuidado com as colaboraes comuns entre seus objetos. Focalizar-se em tais mecanismos durante o desenvolvimento de um sistema pode levar a uma arquitetura menor, mais simples e muito mais compreensvel do que aquelas produzidas quando estes padres so ignorados. A importncia de padres na criao de sistemas complexos foi h muito tempo reconhecida em outras disciplinas. Christopher Alexander e seus colegas foram, talvez, os primeiros a propor a idia de usar uma linguagem de padres em projetos de edificaes e cidades. Suas idias, e as contribuies de outros, agora assentaram razes na comunidade de software orientado a objetos. Em resumo, o conceito do padro de projeto aplicado a software fornece um meio de auxiliar os desenvolvedores a alavancar o conhecimento de outros arquitetos talentosos e experientes. Neste livro, Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides introduzem os princpios de padres de projetos e fornecem um catlogo dos mesmos. Assim, este livro traz duas importantes contribuies. Primeiro, mostra o papel que padres de projetos podem exercer na arquitetura de sistemas complexos. Segundo, fornece uma referncia muito prtica para um conjunto de padres muito bem concebidos que o desenvolvedor atuante pode aplicar na criao de suas prprias aplicaes. Eu me sinto honrado por ter tido a oportunidade de trabalhar diretamente com alguns dos autores deste livro em projetos de arquitetura de sistemas. Aprendi muito com eles e penso que, ao ler este livro, voc tambm aprender. Grady Booch Cientista-Chefe, Rational Software Corporation

Guia para os leitores

Este livro tem duas partes principais. A primeira parte (Captulos 1 e 2) descreve o que so padres de projeto e como estes ajudam a projetar software orientado a objetos. Ela inclui um estudo de caso de projeto que mostra como os padres de projeto so aplicados na prtica. A segunda parte (Captulos 3, 4, e 5) um catlogo dos padres de projeto propriamente ditos. O catlogo compe a maior parte do livro. Seus captulos dividem os padres de projeto em trs tipos: de criao, estruturais e comportamentais. Voc pode usar o catlogo de vrias maneiras. Pode l-lo do incio ao fim ou apenas examin-lo superficialmente, de padro em padro. Uma outra abordagem estudar inteiramente algum desses captulos. Isso lhe ajudar a ver como padres intimamente relacionados se distinguem uns dos outros. Tambm se pode usar as referncias entre os padres como uma trajetria lgica atravs do catlogo. Essa abordagem lhe dar uma percepo sobre como os padres se relacionam uns com os outros, como podem ser combinados e quais funcionam bem juntos. A figura 1.1 (pgina 27) ilustra essas referncias graficamente. Um outro modo de ler o catlogo usar uma abordagem mais dirigida ao problema. V diretamente seo 1.6 (pgina 27) para ler sobre alguns problemas comuns no projeto de software reutilizvel orientado a objetos; ento, leia os padres que atacam esses problemas. Algumas pessoas lem o catlogo de ponta a ponta primeiro e ento usam uma abordagem direcionada ao problema para aplicar os padres aos seus projetos. Se voc no um projetista com experincia em software orientado a objetos, comece com os padres mais simples e mais comuns: Abstract Factory (pg. 95) Adapter (140)

xii

GUIA PARA OS LEITORES

Composite (160) Decorator (170) Factory Method (112) Observer (274) Strategy (292) Template Method (301)

difcil achar um sistema orientado a objetos que no use pelo menos dois desses padres, e grandes sistemas usam quase todos eles. Esse subconjunto lhe ajudar a compreender padres de projeto em particular e, no geral, a compreender o bom projeto orientado a objetos.

Sumrio

Introduo .............................................................................................................
1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 O que um padro de projeto? .............................................................................. Padres de projeto no MVC do Smalltalk ............................................................ Descrevendo os padres de projeto ...................................................................... O catlogo de padres de projeto .......................................................................... Organizando o catlogo .......................................................................................... Como os padres solucionam problemas de projeto ......................................... Como selecionar um padro de projeto ................................................................ Como usar um padro de projeto ..........................................................................

17
19 20 22 24 25 27 43 44

Um estudo de caso: projetando um editor de documentos ........................


2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 Problemas de projeto ............................................................................................... Estrutura do documento ......................................................................................... Formatao ................................................................................................................ Adornando a interface do usurio ........................................................................ Suportando mltiplos estilos de interao (look-and-feel) .................................. Suportando mltiplos sistemas de janelas ........................................................... Operaes do usurio .............................................................................................. Verificao ortogrfica e hifenizao .................................................................... Resumo ......................................................................................................................

47
47 49 53 56 60 64 70 75 86

Catlogo de padres de projeto

Padres de criao ...............................................................................................


Abstract Factory ................................................................................................................ Builder ................................................................................................................................. Factory Method .................................................................................................................

91
95 104 112

14

SUMRIO

Prototype ............................................................................................................................ Singleton ............................................................................................................................. Discusso sobre padres de criao ...............................................................................

121 130 136

Padres estruturais ..............................................................................................


Adapter ............................................................................................................................... Bridge .................................................................................................................................. Composite ........................................................................................................................... Decorator ............................................................................................................................ Faade ................................................................................................................................. Flyweight ............................................................................................................................ Proxy ................................................................................................................................... Discusso sobre padres estruturais ..............................................................................

139
140 151 160 170 179 187 198 207

Padres comportamentais ..................................................................................


Chain of Responsibility .................................................................................................... Command ........................................................................................................................... Interpreter ........................................................................................................................... Iterator ................................................................................................................................. Mediator ............................................................................................................................. Memento ............................................................................................................................. Observer .............................................................................................................................. State ..................................................................................................................................... Strategy ............................................................................................................................... Template Method .............................................................................................................. Visitor .................................................................................................................................. Discusso sobre padres comportamentais ..................................................................

211
212 222 231 244 257 266 274 284 292 301 305 318

Concluso ..............................................................................................................
6.1 6.2 6.3 6.4 6.5 O que esperar do uso de padres de projeto ....................................................... Uma breve histria ................................................................................................... A comunidade envolvida com padres ................................................................ Um convite ................................................................................................................ Um pensamento final ..............................................................................................

323
323 327 328 330 330

A B

Glossrio ............................................................................................................... Guia para a notao ............................................................................................


B.1 Diagrama de classe .................................................................................................. B.2 Diagrama de objeto .................................................................................................. B.3 Diagrama de interao ............................................................................................

331 335
335 336 338

SUMRIO

15

Classes fundamentais (foundation classes) ...................................................


C1 C2 C3 C4 C5 List (Lista) .................................................................................................................. Iterator (Iterador) ..................................................................................................... ListIterator (IteradordeLista) .................................................................................. Point (Ponto) ............................................................................................................. Rect (Retngulo) .......................................................................................................

341
341 344 344 345 346

Referncias bibliogrficas .......................................................................................... ndice ..............................................................................................................................

347 353

1
Introduo
Projetar software orientado a objetos difcil, mas projetar software reutilizvel orientado a objetos ainda mais complicado. Voc deve identificar objetos pertinentes, fator-los em classes no nvel correto de granularidade, definir as interfaces das classes, as hierarquias de herana e estabelecer as relaes-chave entre eles. O seu projeto deve ser especfico para o problema a resolver, mas tambm genrico o suficiente para atender problemas e requisitos futuros. Tambm deseja evitar o reprojeto, ou pelo menos minimiz-lo. Os mais experientes projetistas de software orientado a objetos lhe diro que um projeto reutilizvel e flexvel difcil, seno impossvel, de obter corretamente da primeira vez. Antes que um projeto esteja terminado, eles normalmente tentam reutiliz-lo vrias vezes, modificando-o a cada vez. Projetistas experientes realizam bons projetos, ao passo que novos projetistas so sobrecarregados pelas opes disponveis, tendendo a recair em tcnicas noorientadas a objetos que j usavam antes. Leva um longo tempo para os novatos aprenderem o que realmente um bom projeto orientado a objetos. Os projetistas experientes evidentemente sabem algo que os inexperientes no sabem. O que ? Uma coisa que os melhores projetistas sabem que no devem fazer resolver cada problema a partir de princpios elementares ou do zero. Em vez disso, eles reutilizam solues que funcionaram no passado. Quando encontram uma boa soluo, eles a utilizam repetidamente. Conseqentemente, voc encontrar padres, de classes e de comunicao entre objetos, que reaparecem freqentemente em muitos sistemas orientados a objetos. Esses padres resolvem problemas especficos de projetos e tornam os projetos orientados a objetos mais flexveis e, em ltima instncia, reutilizveis. Eles ajudam os projetistas a reutilizar projetos bem-sucedidos ao basear os novos projetos na experincia anterior. Um projetista que est familiarizado com tais padres pode aplic-los imediatamente a diferentes problemas de projeto, sem necessidade de redescobri-los.

18

CAPTULO 1 INTRODUO

Uma analogia nos ajudar a ilustrar este ponto. Os novelistas ou autores de roteiros (cinema, teatro, televiso) raramente projetam suas tramas do zero. Em vez disso, eles seguem padres como O heri tragicamente problemtico (Macbeth, Hamlet, etc.) ou A Novela Romntica (um sem-nmero de novelas de romances). Do mesmo modo, projetistas de software orientado a objetos seguem padres como represente estados como objetos e adorne objetos de maneira que possa facilmente acrescentar/remover caractersticas. Uma vez que voc conhece o padro, uma grande quantidade de decises de projeto decorre automaticamente. Todos sabemos o valor da experincia de projeto. Quantas vezes voc j no passou pela experincia do dja vu durante um projeto aquele sentimento de que j resolveu um problema parecido antes, embora no sabendo exatamente onde e como? Se pudesse lembrar os detalhes do problema anterior e de que forma o resolveu, ento poderia reutilizar a experincia em lugar de redescobri-la. Contudo, ns no fazemos um bom trabalho ao registrar experincia em projeto de software para uso de outros. A finalidade deste livro registrar a experincia no projeto de software orientado a objetos sob a forma de padres de projeto. Cada padro de projeto sistematicamente nomeia, explica e avalia um aspecto de projeto importante e recorrente em sistemas orientados a objetos. O nosso objetivo capturar a experincia de projeto de uma forma que as pessoas possam us-la efetivamente. Com esta finalidade em vista, documentamos alguns dos mais importantes padres de projeto e os apresentamos em um catlogo. Os padres de projeto tornam mais fcil reutilizar projetos e arquiteturas bemsucedidas. Expressar tcnicas testadas e aprovadas as torna mais acessveis para os desenvolvedores de novos sistemas. Os padres de projeto ajudam a escolher alternativas de projeto que tornam um sistema reutilizvel e a evitar alternativas que comprometam a reutilizao. Os padres de projeto podem melhorar a documentao e a manuteno de sistemas ao fornecer uma especificao explcita de interaes de classes e objetos e o seu objetivo subjacente. Em suma, ajudam um projetista a obter mais rapidamente um projeto adequado. Nenhum dos padres de projeto descreve projetos novos ou no-testados. Incluimos somente projetos que foram aplicados mais de uma vez em diferentes sistemas. Muitos deles nunca foram documentados antes. So parte do folclore da comunidade de desempenho de software orientado a objetos ou elementos de sistemas orientados a objetos bem-sucedidos em nenhum dos casos fcil para projetistas novatos aprender as lies. Assim, embora esses projetos no sejam novos, ns os capturamos numa forma nova e acessvel: como um catlogo de padres, que tem um formato consistente. Apesar do tamanho do livro, os padres apresentados capturam somente uma frao do que um especialista pode conhecer. No h nenhum padro que lide com concorrncia ou programao distribuda ou programao para tempo real. O livro no tem nenhum padro especfico para um domnio de aplicao. No diz como construir interfaces para usurio, como escrever device drivers, ou como usar um banco de dados orientado a objetos. Cada uma dessas reas tem seus prprios padres, e valeria a pena algum catalog-los tambm.*

N. de R. T: Atualmente, j existe literatura disponvel sobre padres para domnios especficos. Ver Analysis Patterns e Enterprise Integration Patterns ambos de Martin Fowler.

PADRES DE PROJETO

19

1.1 O que um padro de projeto?


Christopher Alexander afirma: cada padro descreve um problema no nosso ambiente e o cerne da sua soluo, de tal forma que voc possa usar essa soluo mais de um milho de vezes, sem nunca faz-lo da mesma maneira [AIS+77, pg. x]. Muito embora Alexander estivesse falando acerca de padres em construes e cidades, o que ele diz verdadeiro em relao aos padres de projeto orientados a objeto. Nossas solues so expressas em termos de objetos e interfaces em vez de paredes e portas, mas no cerne de ambos os tipos de padres est a soluo para um problema num determinado contexto. Em geral, um padro tem quatro elementos essenciais: 1. O nome do padro uma referncia que podemos usar para descrever um problema de projeto, suas solues e conseqncias em uma ou duas palavras. Dar nome a um padro aumenta imediatamente o nosso vocabulrio de projeto. Isso nos permite projetar em um nvel mais alto de abstrao. Ter um vocabulrio para padres permite-nos conversar sobre eles com nossos colegas, em nossa documentao e at com ns mesmos. O nome torna mais fcil pensar sobre projetos e a comunic-los, bem como os custos e benefcios envolvidos, a outras pessoas. Encontrar bons nomes foi uma das partes mais difceis do desenvolvimento do nosso catlogo. 2. O problema descreve em que situao aplicar o padro. Ele explica o problema e seu contexto. Pode descrever problemas de projeto especficos, tais como representar algoritmos como objetos. Pode descrever estruturas de classe ou objeto sintomticas de um projeto inflexvel. Algumas vezes, o problema incluir uma lista de condies que devem ser satisfeitas para que faa sentido aplicar o padro. 3. A soluo descreve os elementos que compem o padro de projeto, seus relacionamentos, suas responsabilidades e colaboraes. A soluo no descreve um projeto concreto ou uma implementao em particular porque um padro como um gabarito que pode ser aplicado em muitas situaes diferentes. Em vez disso, o padro fornece uma descrio abstrata de um problema de projeto e de como um arranjo geral de elementos (classes e objetos, no nosso caso) o resolve. 4. As conseqncias so os resultados e anlises das vantagens e desvantagens (trade-offs) da aplicao do padro. Embora as conseqncias sejam raramente mencionadas quando descrevemos decises de projeto, elas so crticas para a avaliao de alternativas de projetos e para a compreenso dos custos e benefcios da aplicao do padro. As conseqncias para o software freqentemente envolvem balanceamento entre espao e tempo. Elas tambm podem abordar aspectos sobre linguagens e implementao. Uma vez que a reutilizao freqentemente um fator no projeto orientado a objetos, as conseqncias de um padro incluem o seu impacto sobre a flexibilidade, a extensibilidade ou a portabilidade de um sistema. Relacionar essas conseqncias explicitamente ajuda a compreend-las e avali-las. O ponto de vista afeta a interpretao de algum sobre o que , ou no, um padro. O padro de uma pessoa pode ser um bloco de construo primrio para

20

CAPTULO 1 INTRODUO

outra. Neste livro concentramos-nos sobre os padres que esto em um certo nvel de abstrao. Padres de projeto no so projetos, como listas encadeadas e tabelas de acesso aleatrio, que podem ser codificadas em classes e ser reutilizadas tais como esto. Tampouco so projetos complexos, de domnio especfico, para uma aplicao inteira ou subsistema. Padres de projeto, neste livro, so descries de objetos e classes comunicantes que precisam ser personalizadas para resolver um problema geral de projeto num contexto particular. Um padro de projeto nomeia, abstrai e identifica os aspectos-chave de uma estrutura de projeto comum para torn-la til para a criao de um projeto orientado a objetos reutilizvel. O padro de projeto identifica as classes e instncias participantes, seus papis, colaboraes e a distribuio de responsabilidades. Cada padro de projeto focaliza um problema ou tpico particular de projeto orientado a objetos. Ele descreve em que situao pode ser aplicado, se ele pode ser aplicado em funo de outras restries de projeto e as conseqncias, custos e benefcios de sua utilizao. Uma vez que em algum momento devemos implementar nossos projetos, um padro de projeto tambm fornece exemplos em cdigo nesse caso, C++ e, algumas vezes, Smalltalk para ilustrar uma implementao. Embora padres de projeto descrevam projetos orientados a objeto, baseiam-se em solues reais que foram implementadas nas principais linguagens de programao orientadas a objeto, como Smalltalk e C++, em vez de implementaes em linguagens procedurais (Pascal, C, Ada) ou linguagens orientadas a objetos mais dinmicas (CLOS, Dylan, Self). Ns escolhemos Smalltalk e C++ por razes prticas: a nossa experincia do dia a dia foi com estas linguagens e elas esto se tornando cada vez mais populares.* A escolha da linguagem de programao importante porque influencia o ponto de vista do projetista (usurio do pradro): nossos padres assumem recursos de linguagem do nvel do Smalltalk/C++, e essa escolha determina o que pode, ou no, ser implementado facilmente. Se tivssemos assumido o uso de linguagens procedurais, deveramos ter includo padres de projetos como Herana, Encapsulamento e Polimorfismo. De maneira semelhante, alguns dos nossos padres so suportados diretamente por linguagens orientadas a objetos menos comuns. Por exemplo, CLOS tem multimtodos que diminuem a necessidade de um padro como Visitor (pg. 305). De fato, h bastante diferenas entre Smalltalk e C++, o que significa que alguns padres podem ser expressos mais facilmente em uma linguagem que em outra. (Ver Iterator, 244, por exemplo).

1.2 Padres de projeto no MVC de Smalltalk


A trade de classes Modelo/Viso/Controlador (Model-View-Controller) (MVC, [KP88]) usada para construir interfaces com o usurio em Smalltalk -80. Observar os padres de projeto dentro do MVC ajudar a perceber o que queremos dizer com o termo padro. A abordagem MVC composta por trs tipos de objetos. O Modelo o objeto de aplicao, a Viso a apresentao na tela e o Controlador o que define a maneira como a interface do usurio reage s entradas do mesmo. Antes da MVC, os projetos de interface para o usurio tendiam a agrupar esses objetos. A MVC separa esses objetos para aumentar a flexibilidade e a reutilizao.
* N. de R. T: Essa observao foi feita em 1995, quando, de fato, as linguagens C++ e Smalltalk ganharam grande reconhecimento.

PADRES DE PROJETO

21

A abordagem MVC separa Viso e Modelos pelo estabelecimento de um protocolo do tipo insero/notificao (subscribe/notify) entre eles. Uma viso deve garantir que a sua aparncia reflita o estado do modelo. Sempre que os dados do modelo mudam, o modelo notifica as vises que dependem dele. Em resposta, cada viso tem a oportunidade de atualizar-se. Esta abordagem permite ligar mltiplas vises a um modelo para fornecer diferentes apresentaes. Da mesma forma, voc tambm pode criar novas vises para um modelo sem ter de reescrev-lo.
vises

modelo

O seguinte diagrama mostra um modelo e trs vises (por simplificao, deixamos de fora os controladores). O modelo contm alguns valores de dados, e as vises que definem uma planilha, um histograma, e um grfico de pizza, apresentam esses dados de vrias maneiras. Um modelo se comunica com suas vises quando seus valores mudam, e as vises se comunicam com o modelo para acessar esses valores. Tomado no seu sentido direto, esse exemplo reflete um projeto que separa vises e modelos. No entanto, o projeto aplicvel a um problema mais geral: separar objetos de maneira que mudanas ocorridas em um possam afetar um nmero qualquer de outros objetos, sem exigir que o objeto alterado conhea detalhes dos outros. Esse projeto mais geral descrito pelo padro de projeto Observer (pg. 274). Outra caracterstica da MVC que as vises podem ser encaixadas. Por exemplo, um painel de controle de botes pode ser implementado como uma viso complexa contendo vises encaixadas compostas de botes. A interface do usurio para um objeto inspetor pode consistir em vises encaixadas, que podem ser reutilizadas em um depurador (debugger). A abordagem MVC suporta vises encaixadas com a classe CompositeView, uma subclasse de View. Os objetos de CompositeView funcionam exatamente como objetos de View; uma viso composta pode ser usada em qualquer lugar que uma viso possa ser usada, mas ela tambm contm e administra vises encaixadas. Novamente, poderamos pensar nesse projeto como sendo um projeto que nos permite tratar uma viso composta tal como tratamos um dos seus componentes. Mas o projeto aplicvel a um problema mais geral, que ocorre sempre que queremos agrupar objetos e tratar o grupo como um objeto individual. Este projeto mais geral descrito pelo padro Composite (160). Ele permite criar uma hierarquia de classes na qual algumas subclasses definem objetos primitivos (por exemplo, Button) e

22

CAPTULO 1 INTRODUO

outras classes definem objetos compostos (CompositeView), que agrupam os primitivos em objetos mais complexos. A abordagem MVC tambm permite mudar a maneira como uma viso responde s entradas do usurio sem mudar sua apresentao visual. Por exemplo, voc pode querer mudar a forma de como ela responde ao teclado ou fazer com que use um menu pop-up em lugar de teclas de comandos. A MVC encapsula o mecanismo de resposta em um objeto Controlador. Existe uma hierarquia de classes de Controllers, tornando fcil criar um novo controlador como uma variante de um existente. Uma viso usa uma instncia de uma subclasse de Controller para implementar uma estratgia particular de respostas; para implementar uma estratgia diferente, simplesmente substitua a instncia por um tipo diferente de controlador. possvel mudar o controlador de uma viso em tempo de execuo, para mudar a maneira como responde s entradas do usurio. Por exemplo, uma viso pode ser desabilitada de maneira que simplesmente no aceite entradas, fornecendo um controlador que ignora os eventos de entrada. O relacionamento View-Controller um exemplo do padro Strategy (292). Um Strategy um objeto que representa um algoritmo. Ele til quando voc quer substituir o algoritmo tanto esttica como dinamicamente, quando h muitas variantes do algoritmo, ou quando o algoritmo tem estruturas de dados complexas que voc deseja encapsular. A abordagem MVC usa outros padres de projeto, tais como Factory Method (112), para especificar por falta (by default) a classe controladora para uma viso e Decorator (170), para acrescentar capacidade de rolagem (scrolling) a uma viso. Mas os principais relacionamentos na MVC so fornecidos pelos padres Observer, Composite e Strategy.

1.3 Descrevendo os padres de projeto


Como ns descrevemos padres de projeto? As notaes grficas, embora sejam importantes e teis, no so suficientes. Elas simplesmente capturam o produto final do processo de projeto como relacionamentos entre classes e objetos. Para reutilizar o projeto, ns tambm devemos registrar decises, alternativas e anlises de custos e benefcios que levaram a ele. Tambm so importantes exemplos concretos, porque ajudam a ver o projeto em ao. Ns descrevemos padres de projeto usando um formato consistente. Cada padro dividido em sees de acordo com o gabarito a seguir. O gabarito fornece uma estrutura uniforme s informaes, tornando os padres de projeto mais fceis de aprender, comparar e usar.

Nome e classificao do padro


O nome do padro expressa a sua prpria essncia de forma sucinta. Um bom nome vital, porque ele se tornar parte do seu vocabulrio de projeto. A classificao do padro reflete o esquema que introduziremos na Seo 1.5.

Inteno e objetivo
uma curta declarao que responde s seguintes questes: o que faz o padro de projeto? Quais os seus princpios e sua inteno? Que tpico ou problema particular de projeto ele trata?

PADRES DE PROJETO

23

Tambm conhecido como


Outros nomes bem conhecidos para o padro, se existirem.

Motivao
Um cenrio que ilustra um problema de projeto e como as estruturas de classes e objetos no padro solucionam o problema. O cenrio ajudar a compreender as descries mais abstratas do padro que vm a seguir.

Aplicabilidade
Quais so as situaes nas quais o padro de projeto pode ser aplicado? Que exemplos de maus projetos ele pode tratar? Como voc pode reconhecer essas situaes?

Estrutura
Uma representao grfica das classes do padro usando uma notao baseada na Object Modeling Technique (OMT) [RBP+ 91]*. Ns tambm usamos diagramas de interao [JCJO92, Boo94] para ilustrar seqncias de solicitaes e colaboraes entre objetos. O Apndice B descreve estas notaes em detalhes.

Participantes
As classes e/ou objetos que participam do padro de projeto e suas responsabilidades.

Colaboraes
Como as classes participantes colaboram para executar suas responsabilidades.

Conseqncias
Como o padro suporta a realizao de seus objetivos? Quais so os seus custos e benefcios e os resultados da sua utilizao? Que aspecto da estrutura de um sistema ele permite variar independentemente?

Implementao
Que armadilhas, sugestes ou tcnicas voc precisa conhecer quando da implementao do padro? Existem consideraes especficas de linguagem?

Exemplo de cdigo
Fragmentos ou blocos de cdigo que ilustram como voc pode implementar o padro em C++ ou Smalltalk.

Usos conhecidos
Exemplos do padro encontrados em sistemas reais. Ns inclumos pelo menos dois exemplos de domnios diferentes.

N. de R. T: Atualmente, os padres j so descritos por praticantes em UML. Quando o livro foi escrito, OMT era uma das tcnicas mais adotadas para a modelagem orientada a objetos.

24

CAPTULO 1 INTRODUO

Padres relacionados
Que padres de projeto esto intimamente relacionados com este? Quais so as diferenas importantes? Com quais outros padres este deveria ser usado? Os apndices fornecem informao de apoio que ajudar a compreender os padres e as discusses que os envolvem. O apndice A um glossrio da terminologia que usamos. J mencionamos que o apndice B apresenta as vrias notaes usadas. Descreveremos tambm aspectos das notaes, medida que as introduzirmos nas prximas discusses. Finalmente, o apndice C contm o cdigo-fonte para as classes fundamentais (foundation classes) que usamos nos nossos exemplos de cdigos.

1.4 O catlogo de padres de projeto


O catlogo que comea na pgina 89 contm 23 padres de projeto. Seus nomes e intenes so listados a seguir para dar uma viso geral. O nmero em parnteses aps cada padro indica a pgina onde ele descrito (seguiremos esta conveno por todo o livro). Abstract Factory (95): Fornece uma interface para criao de famlias de objetos relacionados ou dependentes sem especificar suas classes concretas. Adapter (140): Converte a interface de uma classe em outra interface esperada pelos clientes. O Adapter permite que certas classes trabalhem em conjunto, pois de outra forma seria impossvel por causa de suas interfaces incompatveis. Bridge (151): Separa uma abstrao da sua implementao, de modo que as duas possam variar independentemente. Builder (104): Separa a construo de um objeto complexo da sua representao, de modo que o mesmo processo de construo possa criar diferentes representaes. Chain of Responsibility (212): Evita o acoplamento do remetente de uma solicitao ao seu destinatrio, dando a mais de um objeto a chance de tratar a solicitao. Encadeia os objetos receptores e passa a solicitao ao longo da cadeia at que um objeto a trate. Command (222): Encapsula uma solicitao como um objeto, desta forma permitindo que voc parametrize clientes com diferentes solicitaes, enfileire ou registre (log) solicitaes e suporte operaes que podem ser desfeitas. Composite (160): Compe objetos em estrutura de rvore para representar hierarquias do tipo partes-todo. O Composite permite que os clientes tratem objetos individuais e composies de objetos de maneira uniforme. Decorator (170): Atribui responsabilidades adicionais a um objeto dinamicamente. Os decorators fornecem uma alternativa flexvel a subclasses para extenso da funcionalidade. Faade (179): Fornece uma interface unificada para um conjunto de interfaces em um subsistema. O Faade define uma interface de nvel mais alto que torna o subsistema mais fcil de usar. Factory Method (112): Define uma interface para criar um objeto, mas deixa as subclasses decidirem qual classe a ser instanciada. O Factory Method permite a uma classe postergar (defer) a instanciao s subclasses.

PADRES DE PROJETO

25

Flyweight (187): Usa compartilhamento para suportar grandes quantidades de objetos, de granularidade fina, de maneira eficiente. Interpreter (231): Dada uma linguagem, define uma representao para sua gramtica juntamente com um interpretador que usa a representao para interpretar sentenas nessa linguagem. Iterator (244): Fornece uma maneira de acessar seqencialmente os elementos de uma agregao de objetos sem expor sua representao subjacente. Mediator (257): Define um objeto que encapsula a forma como um conjunto de objetos interage. O Mediator promove o acoplamento fraco ao evitar que os objetos se refiram explicitamente uns aos outros, permitindo que voc varie suas interaes independentemente. Memento (266): Sem violar o encapsulamento, captura e externaliza um estado interno de um objeto, de modo que o mesmo possa posteriormente ser restaurado para este estado. Observer (274): Define uma dependncia um-para-muitos entre objetos, de modo que, quando um objeto muda de estado, todos os seus dependentes so automaticamente notificados e atualizados. Prototype (121): Especifica os tipos de objetos a serem criados usando uma instncia prototpica e criar novos objetos copiando esse prottipo. Proxy (198): Fornece um objeto representante (surrogate), ou um marcador de outro objeto, para controlar o acesso ao mesmo. Singleton (130): Garante que uma classe tenha somente uma instncia e fornece um ponto global de acesso para ela. State (284): Permite que um objeto altere seu comportamento quando seu estado interno muda. O objeto parecer ter mudado de classe. Strategy (292): Define uma famlia de algoritmos, encapsula cada um deles e os torna intercambiveis. O Strategy permite que o algoritmo varie independentemente dos clientes que o utilizam. Template Method (301): Define o esqueleto de um algoritmo em uma operao, postergando a definio de alguns passos para subclasses. O Template Method permite que as subclasses redefinam certos passos de um algoritmo sem mudar sua estrutura. Visitor (305): Representa uma operao a ser executada sobre os elementos da estrutura de um objeto. O Visitor permite que voc defina uma nova operao sem mudar as classes dos elementos sobre os quais opera.

1.5 Organizando o catlogo


Os padres de projeto variam na sua granularidade e no seu nvel de abstrao. Como existem muitos padres de projeto, necessitamos de uma maneira de organiz-los. Esta seo classifica os padres de projeto de maneira que possamos nos referir a famlias de padres relacionados. A classificao ajuda a aprender os padres mais rapidamente, bem como direcionar esforos na descoberta de novos. Ns classificamos os padres de projeto por dois critrios (Tabela 1.1). O primeiro critrio, chamado finalidade, reflete o que um padro faz. Os padres podem ter finalidade de criao, estrutural ou comportamental. Os padres de criao se preocupam com o processo de criao de objetos. Os padres estruturais

26

CAPTULO 1 INTRODUO

Tabela 1.1 O espao dos padres de projeto


Propsito De criao Escopo Classe Objeto
Factory Method (112) Abstract Factory (95) Builder (104) Prototype (121) Singleton (130)

Estrutural
Adapter (class) (140) Adapter (object) (140) Bridge (151) Composite (160) Decorator (170) Faade (179) Flyweight (187) Proxy (198)

Comportamental
Interpreter (231) Template Method (301) Chain of Responsibility (212) Command (222) Iterator (244) Mediator (257) Memento (266) Observer (274) State (284) Strategy (292) Visitor (305)

lidam com a composio de classes ou de objetos. Os padres comportamentais caracterizam as maneiras pelas quais classes ou objetos interagem e distribuem responsabilidades. O segundo critrio, chamado escopo, especifica se o padro se aplica primariamente a classes ou a objetos. Os padres para classes lidam com os relacionamentos entre classes e suas subclasses. Esses relacionamentos so estabelecidos atravs do mecanismo de herana, assim eles so estticos fixados em tempo de compilao. Os padres para objetos lidam com relacionamentos entre objetos que podem ser mudados em tempo de execuo e so mais dinmicos. Quase todos utilizam a herana em certa medida. Note que a maioria est no escopo de Objeto. Os padres de criao voltados para classes deixam alguma parte da criao de objetos para subclasses, enquanto que os padres de criao voltados para objetos postergam esse processo para outro objeto. Os padres estruturais voltados para classes utilizam a herana para compor classes, enquanto que os padres estruturais voltados para objetos descrevem maneiras de montar objetos. Os padres comportamentais voltados para classes usam a herana para descrever algoritmos e fluxo de controle, enquanto que os voltados para objetos descrevem como um grupo de objetos coopera para executar uma tarefa que um nico objeto no pode executar sozinho. H outras maneiras de organizar os padres. Alguns padres so freqentemente usados em conjunto. Por exemplo, o Composite freqentemente usado com o Iterator ou o Visitor. Alguns padres so alternativos: o Prototype freqentemente uma alternativa para o Abstract Factory. Alguns padres resultam em projetos semelhantes, embora tenham intenes diferentes. Por exemplo, os diagramas de estrutura de Composite e Decorator so semelhantes. Uma outra maneira, ainda, de organizar padres de projeto de acordo com a forma com que eles mencionam outros padres nas sees Padres Relacionados. A figura 1.1 ilustra estes relacionamentos graficamente. Existem, claramente, muitas maneiras de organizar os padres de projeto. Ter mltiplas maneiras de pensar a respeito deles aprofundar sua percepo sobre o que fazem, como se comparam e quando aplic-los.

PADRES DE PROJETO

27

Memento Builder
salvando o estado da iterao

Proxy Adapter

criando compostos

Iterator

evitando histerese

Bridge

acrescentando responsabilidades a objetos

enumerando filhos

usando composto

Command

Composite Decorator
compartilhando compostos definindo percursos definindo a cadeia

adicionando operaes definindo a gramtica

Flyweight
mudando o exterior versus o interior compartilhando estratgias

Visitor

Interpreter

adicionando operaes

Chain of Responsibility

Strategy

compartilhando smbolos terminais compartilhando estados

Mediator

administrao de dependncias complexas

Observer

definindo os passos do algoritmo

State Template Method


usos freqentes

Prototype
configurar a fbrica dinamicamente

Factory Method
implementa usando

Abstract Factory
instncia nica instncia nica

Facade

Singleton

Figura 1.1 Relacionamentos entre padres de projeto.

1.6 Como os padres solucionam problemas de projeto


Os padres de projeto solucionam muitos dos problemas que os projetistas enfrentam no dia a dia, e de muitas maneiras diferentes. Aqui apresentamos vrios desses problemas e como os padres de projeto os solucionam.

Procurando objetos apropriados


Programas orientados a objetos so feitos de objetos. Um objeto empacota tanto os dados quanto os procedimentos que operam sobre eles. Os procedimentos so

28

CAPTULO 1 INTRODUO

tipicamente chamados de mtodos ou operaes. Um objeto executa uma operao quando ele recebe uma solicitao (ou mensagem) de um cliente. As solicitaes (requests) so a nica maneira de conseguir que um objeto execute uma operao. As operaes so a nica maneira de mudar os dados internos de um objeto. Por causa destas restries, diz-se que o estado interno de um objeto est encapsulado; ele no pode ser acessado diretamente e sua representao invisvel do exterior do objeto. A parte difcil sobre projeto orientado a objetos a decomposio de um sistema em objetos. A tarefa difcil porque muitos fatores entram em jogo: encapsulamento, granularidade, dependncia, flexibilidade, desempenho, evoluo, reutilizao, e assim por diante. Todos influenciam a decomposio, freqentemente de formas conflitantes. As metodologias de projeto orientado a objetos favorecem muitas abordagens diferentes. Voc pode escrever uma descrio de um problema, separar os substantivos e verbos e criar as classes e operaes correspondentes. Ou voc pode se concentrar sobre as colaboraes e responsabilidades no seu sistema. Ou ainda, poder modelar o mundo real e, na fase de projeto, traduzir os objetos encontrados durante a anlise. Sempre haver desacordo sobre qual a melhor abordagem. Muitos objetos num projeto provm do modelo de anlise. Porm, projetos orientados a objetos freqentemente acabam tendo classes que no tm contrapartida no mundo real. Algumas dessas so classes de baixo nvel, como vetores. Outras esto em um nvel muito mais alto. Por exemplo, o padro Composite (160) introduz uma abstrao para o tratamento uniforme de objetos que no tm uma contrapartida fsica. A modelagem estrita do mundo real conduz a um sistema que reflete as realidades atuais, mas no necessariamente as futuras. As abstraes que surgem durante um projeto so as chaves para torn-lo flexvel. Os padres de projeto ajudam a identificar abstraes menos bvias bem como os objetos que podem captur-las. Por exemplo, objetos que representam um processo ou algoritmo no ocorrem na natureza, no entanto, eles so uma parte crucial de projetos flexveis. O padro Strategy (292) descreve como implementar famlias de algoritmos intercambiveis. O padro State (284) representa cada estado de uma entidade como um objeto. Esses objetos so raramente encontrados durante a anlise, ou mesmo durante os estgios iniciais de um projeto; eles so descobertos mais tarde, durante o processo de tornar um projeto mais flexvel e reutilizvel.

Determinando a granularidade dos objetos


Os objetos podem variar tremendamente em tamanho e nmero. Podem representar qualquer coisa indo para baixo at o nvel do hardware ou seguindo todo o caminho para cima at chegarmos a aplicaes inteiras. Como decidimos o que deve ser um objeto? Os padres de projeto tambm tratam desse tpico. O padro Faade (179) descreve como representar subsistemas completos como objetos, e o padro Flyweight (187) descreve como suportar enormes quantidades de objetos nos nveis de granularidade mais finos. Outros padres de projeto descrevem maneiras especficas de decompor um objeto em objetos menores. O Abstract Factory (95) e o Builder (104) fornecem objetos cujas nicas responsabilidades so criar outros objetos. O Visitor (305) e o Command (222) fornecem objetos cujas nicas responsabilidades so implementar uma solicitao em outro objeto ou grupo de objetos.

PADRES DE PROJETO

29

Especificando interfaces de objetos


Cada operao declarada por um objeto especifica o nome da operao, os objetos que ela aceita como parmetros e o valor retornado por ela. Isso conhecido como a assinatura da operao. O conjunto de todas as assinaturas definido pelas operaes de um objeto chamado de interface do objeto. A interface de um objeto caracteriza o conjunto completo de solicitaes que lhe podem ser enviadas. Qualquer solicitao que corresponde a uma assinatura na interface do objeto pode ser enviada para o mesmo. Um tipo um nome usado para denotar uma interface especfica. Quando dizemos que um objeto tem o tipo Janela, significa que ele aceita todas as solicitaes para as operaes definidas na interface chamada Janela. Um objeto pode ter muitos tipos, assim como objetos muito diferentes podem compartilhar um mesmo tipo. Parte da interface de um objeto pode ser caracterizada por um tipo, e outras partes por outros tipos. Dois objetos do mesmo tipo necessitam compartilhar somente partes de suas interfaces. As interfaces podem conter outras interfaces como subconjuntos. Dizemos que um tipo um subtipo de outro se sua interface contm a interface do seu supertipo. Freqentemente dizemos que um subtipo herda a interface do seu supertipo. As interfaces so fundamentais em sistemas orientados a objetos. Os objetos so conhecidos somente atravs das suas interfaces. No existe nenhuma maneira de saber algo sobre um objeto ou de pedir que faa algo sem intermdio de sua interface. A interface de um objeto nada diz sobre sua implementao diferentes objetos esto livres para implementar as solicitaes de diferentes maneiras. Isso significa que dois objetos que tenham implementaes completamente diferentes podem ter interfaces idnticas. Quando uma mensagem enviada a um objeto, a operao especfica que ser executada depende de ambos mensagem e objeto receptor. Diferentes objetos que suportam solicitaes idnticas, podem ter diferentes implementaes das operaes que atendem a estas solicitaes. A associao em tempo de execuo de uma solicitao a um objeto e a uma das suas operaes conhecida como ligao dinmica (dynamic binding). O uso da ligao dinmica significa que o envio de uma solicitao no o prender a uma implementao particular at o momento da execuo. Conseqentemente, voc poder escrever programas que esperam um objeto com uma interface em particular, sabendo que qualquer objeto que tenha a interface correta aceitar a solicitao. Alm do mais, a ligao dinmica permite substituir uns pelos outros objetos que tenham interfaces idnticas. Essa capacidade de substituio conhecida como polimorfismo e um conceito-chave em sistemas orientados a objetos. Ela permite a um objeto-cliente criar poucas hipteses sobre outros objetos, exceto que eles suportam uma interface especfica. O polimorfismo simplifica as definies dos clientes, desacopla objetos entre si e permite a eles variarem seus inter-relacionamentos em tempo de execuo. Os padres de projeto ajudam a definir interfaces pela identificao de seus elementos-chave e pelos tipos de dados que so enviados atravs de uma interface. Um padro de projeto tambm pode lhe dizer o que no colocar na interface. O padro Memento (266) um bom exemplo. Ele descreve como encapsular e salvar o estado interno de um objeto de modo que o objeto possa ser restaurado quele estado mais tarde. O padro estipula que objetos Memento devem definir duas interfaces: uma restrita, que permite aos clientes manterem e copiarem mementos, e uma privilegia-

30

CAPTULO 1 INTRODUO

da, que somente o objeto original pode usar para armazenar e recuperar estados no Memento. Os padres de projeto tambm especificam relacionamentos entre interfaces. Em particular, freqentemente exigem que algumas classes tenham interfaces similares, ou colocam restries sobre interfaces de algumas classes. Por exemplo, tanto Decorator (170) quanto Proxy (198) exigem que as interfaces de objetos Decorator como Proxy sejam idnticas aos objetos decorados e representados. Em Visitor (305), a interface de Visitor deve refletir todas as classes de objetos que visitors (visitantes) podem visitar.

Especificando implementaes de objetos


At aqui dissemos pouco sobre como efetivamente definimos um objeto. Uma implementao de um objeto definidada por sua classe. A classe especifica os dados internos do objeto e de sua representao e define as operaes que o objeto pode executar. Nossa anotao, baseada na OMT (e resumida no apndice B), ilustra uma classe como um retngulo com o nome da classe em negrito. As operaes aparecem em tipo normal abaixo do nome da classe. Quaisquer dados que a classe defina vm em seguida s operaes. O nome da classe separado das operaes por linhas, da mesma forma que as operaes dos dados:
ClassName Operation1() Type Operation2() ... instanceVariable1 Type instanceVariable2 ...

Tipos de Retorno e tipos de variveis de instncia so opcionais, uma vez que no assumimos uma linguagem de implementao estaticamente tipificada. Os objetos so criados por instanciao de uma classe. Diz-se que o objeto uma instncia da classe. O processo de instanciar uma classe aloca memria para os dados internos do objeto (compostos de variveis de instncia) e associa as operaes a estes dados. Muitas instncias semelhantes de um objeto podem ser criadas pela instanciao de uma classe. Uma flecha tracejada indica uma classe que instancia objetos de outra classe. A flecha aponta para a classe dos objetos instanciados.
Instanciador Instanciado

Novas classes podem ser definidas em termos das classes existentes, usando-se herana de classe. Quando uma subclasse herda de uma classe-me, ela inclui as definies de todos os dados e operaes que a classe-me define. Os objetos que so instncias das subclasses contero todos os dados definidos pela subclasse e suas classes-me, e eles sero capazes de executar todas as operaes definidas por esta subclasse e seus ancestrais. Ns indicamos o relacionamento de subclasse com uma linha vertical e um tringulo:

PADRES DE PROJETO

31

ParentClass Operation()

Subclass

Uma classe abstrata uma classe cuja finalidade principal definir uma interface comum para suas subclasses. Uma classe abstrata postergar parte de, ou toda, sua implementao para operaes definidas em subclasses; portanto, uma classe abstrata no pode ser instanciada. As operaes que uma classe abstrata declara, mas no implementa, so chamadas de operaes abstratas. As classes que no so abstratas so chamadas de classes concretas. As subclasses podem refinar e redefinir comportamentos das suas classes ancestrais (me, av, etc.). Mais especificamente, uma classe pode redefinir uma operao definida por sua classe-me. A redefinio d s subclasses a oportunidade de tratar solicitaes em lugar das suas classes ancestrais. A herana de classe permite definir classes simplesmente estendendo outras classes, tornando fcil definir famlias de objetos que tm funcionalidade relacionada. Os nomes das classes abstratas aparecem em itlico para distingui-las das classes concretas. O tipo em itlico tambm usado para denotar operaes abstratas. Um diagrama pode incluir pseudocdigo para implementao de uma operao; neste caso, o cdigo aparecer em uma caixa com um canto dobrado, conectada por uma linha pontilhada operao que ele implementa.
AbstractClass Operation()

ConcreteSubclass Operation() implementation pseudocode

Uma classe mixin aquela cuja inteno oferecer uma interface ou funcionalidade opcional a outras classes. semelhante a uma classe abstrata no aspecto de que no se destina a ser instanciada. As classes mixin exigem herana mltipla.
ExistingClass ExistingOperation()

Mixin MixinOperation()

AugmentedClass ExistingOperation() MixinOperation()

32

CAPTULO 1 INTRODUO

Herana de classe versus herana de interface importante compreender a diferena entre a classe de um objeto e seu tipo. A classe de um objeto define como ele implementado. A classe define o estado interno do objeto e a implementao de suas operaes. Em contraste a isso, o tipo de um objeto se refere somente sua interface o conjunto de solicitaes s quais ele pode responder. Um objeto pode ter muitos tipos, e objetos de diferentes classes podem ter o mesmo tipo. Naturalmente, existe um forte relacionamento entre classe e tipo. Uma vez que uma classe define as operaes que um objeto pode executar, ela tambm define o tipo do objeto. Quando dizemos que o objeto uma instncia de uma classe, queremos dizer que o objeto suporta a interface definida pela classe. Linguagens como C++ e Eiffel utilizam classes para especificar tanto o tipo de um objeto como sua implementao. Os programas em Smalltalk no declaram os tipos de variveis; conseqentemente, o compilador no verifica se os tipos dos objetos atribudos a uma varivel so subtipos do tipo da varivel. Enviar uma mensagem exige a verificao de que a classe do receptor implementa a mensagem, mas no exige a verificao de que o receptor seja uma instncia de uma classe especfica. tambm importante compreender a diferena entre herana de classe e herana de interface (ou subtipificao). A herana de classe define a implementao de um objeto em termos da implementao de outro objeto. Resumidamente, um mecanismo para compartilhamento de cdigo e de representao. Diferentemente disso, a herana de interface (ou subtipificao) descreve quando um objeto pode ser usado no lugar de outro. fcil confundir esses dois conceitos porque muitas linguagens no fazem uma distino explcita. Em linguagens como C++ e Eiffel, herana significa tanto herana de interface como de implementao. A maneira-padro de herdar uma interface em C++ herdar publicamente de uma classe que tem apenas funes-membro virtuais. Herana pura de interface assemelha-se em C++ a herdar publicamente de classes abstratas puras. A herana pura de implementao, ou herana de classe, pode ser assemelhada com a herana privada. Em Smalltalk, herana significa somente herana de implementao. Voc pode atribuir instncias de qualquer classe a uma varivel, contanto que essas instncias apoiem a operao executada sobre o valor da varivel. Embora muitas linguagens de programao no apoiem a distino entre herana de interface e de implementao, as pessoas fazem a distino na prtica. Os programadores Smalltalk usualmente pensam como se as subclasses fossem subtipos (embora existam algumas excees bem conhecidas [ Coo92]); programadores C++ manipulam objetos atravs de tipos definidos por classes abstratas. Muitos dos padres de projeto dependem desta distino. Por exemplo, os objetos numa Chain of Responsibility (212) devem ter um tipo em comum, mas usualmente no compartilham uma implementao. No padro Composite (160), o Component define uma interface comum, porm Composite freqentemente define uma implementao em comum. O Command (222), o Observer (274), o State (284) e o Strategy (292) so freqentemente implementados com classes abstratas que so puramente interfaces.

PADRES DE PROJETO

33

Programando para uma interface, no para uma implementao A herana de classe basicamente apenas um mecanismo para estender a funcionalidade de uma aplicao pela reutilizao da funcionalidade das classes ancestrais. E permite definir rapidamente um novo tipo de objeto em termos de um existente. Ele permite obter novas implementaes quase de graa, herdando a maior parte do que voc necessita de classes existentes. Contudo, a reutilizao de implementao somente metade da histria. A habilidade da herana para definir famlias de objetos com interfaces idnticas (usualmente por herana de uma classe abstrata) tambm importante. Por qu? Porque o polimorfismo depende dela. Quando a herana usada cuidadosamente (alguns diro apropriadamente), todas as classes derivadas de uma classe abstrata compartilharo sua interface. Isto implica que uma subclasse meramente acrescenta ou substitui operaes da classeme, e no oculta operaes dela. Todas as subclasses podem ento responder a solicitaes na interface da classe abstrata, tornando-se, todas, subtipos desta. Existem dois benefcios na manipulao de objetos exclusivamente em termos da interface definida por classes abstratas: 1. Os clientes permanecem sem conhecimento dos tipos especficos dos objetos que eles usam, contanto que os objetos tenham aderncia interface que os clientes esperam; 2. Os clientes permanecem sem conhecimento das classes que implementam estes objetos. Os clientes somente tm conhecimento da (s) classe(s) abstrata(s) que define(m) a interface. Isso reduz to enormemente as dependncias de implementao entre subsistemas que conduz ao seguinte princpio de projeto reutilizvel orientado a objetos: Programe para uma interface, no para uma implementao. No declare variveis como instncias de classes concretas especficas. Em vez disso, prenda-se somente a uma interface definida por uma classe abstrata. Voc ver que isto um tema comum aos padres de projeto neste livro. Naturalmente, voc tem que instanciar classes concretas (isto , especificar uma particular implementao) em algum lugar do seu sistema, e os padres de criao permitem fazer exatamente isso (Abstract Factory (95), Builder (104), Factory Method (112), Prototype (121) e Singleton (130). Ao abstrair o processo de criao de objetos, estes padres lhe do diferentes maneiras de associar uma interface com sua implementao de forma transparente no momento da instanciao. Os padres de criao asseguram que seu sistema esteja escrito em termos de interfaces, no de implementaes.

Colocando os mecanismos de reutilizao para funcionar


Muitas pessoas podem compreender conceitos como objetos, interfaces, classes e herana. O desafio reside em aplic-los construo de software flexvel e reutilizvel, e os padres de projeto podem mostrar como faz-lo.

34

CAPTULO 1 INTRODUO

Herana versus composio As duas tcnicas mais comuns para a reutilizao de funcionalidade em sistemas orientados a objetos so herana de classe e composio de objetos. Como j explicamos, a herana de classe permite definir a implementao de uma classe em termos da implementao de outra. A reutilizao por meio de subclasses freqentemente chamada de reutilizao de caixa branca (ou aberta). O termo caixa branca se refere visibilidade: com herana, os interiores das classes ancestrais so freqentemente visveis para as subclasses. A composio de objetos uma alternativa herana de classe. Aqui, a nova funcionalidade obtida pela montagem e/ou composio de objetos, para obter funcionalidades mais complexas. A composio de objetos requer que os objetos que esto sendo compostos tenham interfaces bem definidas. Esse estilo de reutilizao chamado reutilizao de caixa preta, porque os detalhes internos dos objetos no so visveis. Os objetos aparecem somente como caixas pretas. A herana e a composio tm, cada uma, vantagens e desvantagens. A herana de classes definida estaticamente em tempo de compilao e simples de usar, uma vez que suportada diretamente pela linguagem de programao. A herana de classe tambm torna mais fcil modificar a implementao que est sendo reutilizada. Quando uma subclasse redefine algumas, mas no todas as operaes, ela tambm pode afetar as operaes que herda, assumindo-se que elas chamam as operaes redefinidas. Porm, a herana de classe tem tambm algumas desvantagens. Em primeiro lugar, voc no pode mudar as implementaes herdadas das classes ancestrais em tempo de execuo, porque a herana definida em tempo de compilao. Em segundo lugar, e geralmente isso o pior, as classes ancestrais freqentemente definem pelo menos parte da representao fsica das suas subclasses. Porque a herana expe para uma subclasse os detalhes da implementao dos seus ancestrais, freqentemente dito que a herana viola o encapsulamento [Sny86]. A implementao de uma subclasse, dessa forma, torna-se to amarrada implementao da sua classe-me que qualquer mudana na implementao desta forar uma mudana naquela. As dependncias de implementao podem causar problemas quando se est tentando reutilizar uma subclasse. Se algum aspecto da implementao herdada no for apropriado a novos domnios de problemas, a classe-me deve ser reescrita ou substituda por algo mais apropriado. Esta dependncia limita a flexibilidade e, em ltima instncia, a reusabilidade. Uma cura para isto herdar somente de classes abstratas, uma vez que elas normalmente fornecem pouca ou nenhuma implementao. A composio de objetos definida dinamicamente em tempo de execuo pela obteno de referncias a outros objetos atravs de um determinado objeto. A composio requer que os objetos respeitem as interfaces uns dos outros, o que por sua vez exige interfaces cuidadosamente projetadas, que no impeam voc de usar um objeto com muitos outros. Porm, existe um ganho. Como os objetos so acessados exclusivamente atravs de suas interfaces, ns no violamos o encapsulamento. Qualquer objeto pode ser substitudo por outro em tempo de execuo, contanto que tenha o mesmo tipo. Alm do mais, como a implementao de um objeto ser escrita em termos de interfaces de objetos, existiro substancialmente menos dependncias de implementao.

PADRES DE PROJETO

35

A composio de objetos tem um outro efeito sobre o projeto de um sistema. Dar preferncia composio de objetos herana de classes ajuda a manter cada classe encapsulada e focalizada em uma nica tarefa. Suas classes e hierarquias de classes se mantero pequenas, com menor probabilidade de crescerem at se tornarem monstros intratveis. Por outro lado, um projeto baseado na composio de objetos ter mais objetos (embora menos classes), e o comportamento do sistema depender de seus inter-relacionamentos ao invs de ser definido em uma classe. Isto nos conduz ao nosso segundo princpio de projeto orientado a objetos: Prefira a composio de objeto herana de classe. Idealmente, voc no deveria ter que criar novos componentes para obter reutilizao. Deveria ser capaz de conseguir toda a funcionalidade de que necessita simplesmente montando componentes existentes atravs da composio de objetos. Mas este raramente o caso, porque o conjunto de componentes disponveis nunca exatamente rico o bastante na prtica. A reutilizao por herana torna mais fcil criar novos componentes que podem ser obtidos pela composio de componentes existentes. Assim, a herana e a composio de objetos trabalham juntas. No entanto, nossa experincia mostra que os projetistas abusam da herana como uma tcnica de reutilizao, e que freqentemente os projetos tornam-se mais reutilizveis (e mais simples) ao preferir a composio de objetos. Voc ver a composio de objetos aplicada repetidas vezes nos padres de projeto. Delegao Delegao uma maneira de tornar a composio to poderosa para fins de reutilizao quanto herana [Lie86, JZ91]. Na delegao, dois objetos so envolvidos no tratamento de uma solicitao: um objeto receptor delega operaes para o seu delegado; isto anlogo postergao de solicitaes enviadas s subclasses para as suas classes-me. Porm, com a herana, uma operao herdada pode sempre se referir ao objeto receptor atravs da varivel membro this, em C++ e self em Smalltalk. Para obter o mesmo efeito com o uso de delegao, o receptor passa a si mesmo para o delegado para permitir operao delegada referenciar o receptor. Por exemplo, em vez de fazer da classe Window uma subclasse de Rectangle (porque janelas so retangulares), a classe Window deve reutilizar o comportamento de Rectangle, conservando uma varivel de instncia de Rectangle, e delegando o comportamento especfico de Rectangle para ela. Em outras palavras, ao invs de uma Window ser um Rectangle ela teria um Rectangle. Agora, Window deve encaminhar as solicitaes para sua instncia Rectangle explicitamente, ao passo que antes ela teria herdado essas operaes. O seguinte diagrama ilustra a classe Window delegando sua operao rea a uma instncia de Rectangle.

36

CAPTULO 1 INTRODUO

Window Area()

Rectangle rectangle Area() width height

return rectangle>Area()

return width * height

Uma flecha com uma linha cheia indica que um objeto de uma classe mantm uma referncia para uma instncia de outra classe. A referncia tem um nome opcional, nesse caso, rectangle. A principal vantagem da delegao que ela torna fcil compor comportamentos em tempo de execuo e mudar a forma como so compostos. A nossa Window pode se tornar circular em tempo de execuo, simplesmente pela substituio da sua instncia Rectangle por uma instncia de Circle, assumindo-se que Rectangle e Circle tenham o mesmo tipo. A delegao tem uma desvantagem que ela compartilha com outras tcnicas que tornam o software mais flexvel atravs da composio de objetos: o software dinmico, altamente parametrizado, mais difcil de compreender do que o software mais esttico. H tambm ineficincias de tempo de execuo, mas as ineficincias humanas so mais importantes a longo prazo . A delegao uma boa escolha de projeto somente quando ela simplifica mais do que complica. No fcil estabelecer regras que lhe digam exatamente quando usar delegao, porque o quo efetiva ela ser depender das condies do contexto e de quanta experincia voc tem com o seu uso. A delegao funciona melhor quando usada em formas altamente estilizadas isto , em padres catalogados. Diversos padres de projeto usam delegao. Os padres State (284), Strategy (292) e Visitor (305) dependem dela. No padro State, um objeto delega solicitaes para um objeto State que representa o seu estado atual. No padro Strategy, um objeto delega uma solicitao especfica para um objeto que representa uma estratgia para executar a solicitao. Um objeto ter somente um estado, mas ele pode ter muitas estratgias para diferentes solicitaes. A finalidade de ambos os padres mudar o comportamento de um objeto pela mudana dos objetos para os quais ele delega solicitaes. Em Visitor, a operao que executada em cada elemento da estrutura de um objeto sempre delegada para o objeto Visitor. Outros padres utilizam delegao menos intensamente. O Mediator (257) introduz um objeto para intermediar a comunicao entre outros objetos. Algumas vezes, o objeto Mediador simplesmente implementa operaes passando-as adiante para outros objetos; outras vezes, ele passa junto uma referncia para si prprio, e assim usa a delegao no seu sentido exato. O Chain of Responsibility (212) trata as solicitaes passando-as para frente, de um objeto para o outro, ao longo de uma cadeia de objetos. Algumas vezes essa solicitao carrega consigo uma referncia ao objeto original que recebeu a solicitao, e nesse caso o padro est usando delegao. O Bridge (151) desacopla uma abstrao de sua implementao. Se a abstrao e uma particular implementao so muito parecidas, ento a abstrao pode simplesmente delegar operaes para aquela implementao.

PADRES DE PROJETO

37

A delegao um exemplo extremo da composio de objetos. Ela mostra que voc pode sempre substituir a herana pela composio de objetos como um mecanismo para a reutilizao de cdigo. Herana versus tipos parametrizados Uma outra tcnica (que no estritamente orientada a objetos) para reutilizao de funcionalidade o uso de tipos parametrizados, tambm conhecido como generics (Ada, Eiffel) e templates (C++). Essa tcnica permite definir um tipo sem especificar todos os outros tipos que ele usa. Os tipos no-especificados so fornecidos como parmetros no ponto de utilizao. Por exemplo, uma classe Lista pode ser parametrizada pelo tipo de elementos que contm. Para declarar uma lista de inteiros, voc fornece o tipo integer como parmetro para o tipo parametrizado Lista. Para declarar uma lista de objetos String, voc fornece o tipo String como parmetro. A implementao da linguagem ir criar uma verso customizada do gabarito (template) da classe Lista para cada tipo de elemento. Tipos parametrizados nos do uma terceira maneira de efetuar a composio de comportamentos em sistemas orientados a objetos (alm da herana de classe e da composio de objetos). Muitos projetos podem ser implementados usando qualquer uma dessas trs tcnicas. Para parametrizar uma rotina de classificao (sort) pelo sistema que ela usa para comparar elementos, ns poderamos fazer a comparao 1. Uma operao implementada por subclasses (uma aplicao de Template Method, 301)); 2. A responsabilidade de um objeto que passado para uma rotina de classificao (Strategy, 292)); ou 3. Um argumento de um template de C++ ou um generic em Ada que especifica o nome da funo a ser chamada para comparar os elementos. Existem importantes diferenas entre estas tcnicas. A composio de objetos permite mudar o comportamento que est sendo composto em tempo de execuo, mas ela tambm requer endereamento indireto e pode ser menos eficiente. A herana permite fornecer implementaes por falta (by default) para operaes e deixa subclasses redefini-las. Os tipos parametrizados permitem mudar os tipos que uma classe pode usar. Porm, nem a herana nem os tipos parametrizados podem mudar em tempo de execuo. A melhor abordagem depende do projeto e das restries de implementao. Nenhum dos padres deste livro trata de tipos parametrizados, embora os utilizemos em uma ocasio para customizar a implementao de um padro em C++. Tipos parametrizados so totalmente desnecessrios em uma linguagem como Smalltalk, que no tem verificao de tipo em tempo de compilao.

Relacionando estruturas de tempo de execuo e de tempo de compilao


A estrutura em tempo de execuo de um programa orientado a objetos freqentemente apresenta pouca semelhana com sua estrutura de cdigo. A estrutura de cdigo congelada em tempo de compilao; ela consiste de classes em relacionamento de herana fixos. A estrutura de tempo de execuo de um programa consiste de redes em rpida mudana de objetos comunicantes. De fato, as duas estruturas so bastante independentes. Tentar entender uma a partir da outra como tentar

38

CAPTULO 1 INTRODUO

entender o dinamismo dos ecossistemas vivos a partir da taxonomia esttica de plantas e animais, e vice-versa. Considere a distino entre agregao e associao (acquaintance*) de objetos e como diferentemente elas se manifestam nos tempos de compilao e de execuo. A agregao implica que um objeto possui, ou responsvel por, outro objeto. Geralmente dizemos que um objeto tem ou parte de outro objeto. Agregao implica que um objeto agregado e seu proprietrio tm idnticos tempos de vida. Associao implica que um objeto meramente tem conhecimento de outro objeto. Algumas vezes, chamada de relacionamento usa. Objetos que se conhecem podem solicitar operaes uns do outros, mas eles no so responsveis um pelo outro. A associao um relacionamento mais fraco do que a agregao e sugere um acoplamento muito menor entre objetos. Em nossos diagramas, uma flecha de linha cheia denota a associao. Uma flecha com um losango em sua base denota a agregao:
Aggregator
aggregateInstance

Aggregatee

fcil de confundir agregao com associao, porque elas so freqentemente implementadas da mesma forma. Em Smalltalk, todas as variveis so referncias para outros objetos. No h distino na linguagem de programao entre agregao e associao. Em C++, a agregao pode ser implementada pela definio de variveis-membro, que so instncias reais, porm, mais comum defini-las como apontadores ou referncias para instncias. A associao tambm implementada com apontadores e referncias. Em ltima instncia, a associao e a agregao so determinadas mais pela inteno do que por mecanismos explcitos da linguagem. A distino pode ser difcil de ver na estrutura de tempo de compilao, porm, significativa. Os relacionamentos de agregao tendem a ser em menor nmero e mais permanentes que as associaes. Em contraste, as associaes so feitas e refeitas mais freqentemente, algumas vezes existindo somente para a durao de uma operao. Tambm, as associaes so mais dinmicas, mais difceis de distinguir no cdigo-fonte. Com tal disparidade existente entre as estruturas de um programa em tempo de execuo e tempo de compilao, claro que o cdigo no revelar tudo acerca de como o sistema funcionar. A estrutura de um sistema em tempo de execuo deve ser imposta mais pelo projetista do que pela linguagem. Os relacionamentos entre objetos e seus tipos devem ser projetados com grande cuidado porque eles determinam quo boa ou ruim a estrutura em tempo de execuo. Muitos padres de projeto (em particular aqueles que tm escopos de objeto) capturam explicitamente a distino entre estruturas de tempo de compilao e tempo de execuo. Composite (160) e Decorator (170) so especialmente teis para a construo de estruturas complexas de tempo de execuo. Observer (274) envolve estruturas de tempo de execuo que so freqentemente difceis de compreender, a menos que voc conhea o padro. Chain of Responsibility (212) tambm resulta em padres de comunicao que a herana no revela. Em geral, as estruturas de tempo de execuo no esto claras no cdigo, a menos que voc compreenda os padres.

N. de T.: Conhecimento ou relacionamento social em ingls: aqui, a idia de conhecimento de um objeto por outro. O termo associao utilizado no Brasil.

PADRES DE PROJETO

39

Projetando para mudanas


A chave para maximizao da reutilizao est na antecipao de novos requisitos e mudanas nos requisitos existentes e em projetar sistemas de modo que eles possam evoluir de acordo. Para projetar o sistema de maneira que seja robusto face a tais mudanas, voc deve levar em conta como o sistema pode necessitar mudar ao longo de sua vida. Um projeto que no leva em considerao a possibilidade de mudanas est sujeito ao risco de uma grande reformulao no futuro. Essas mudanas podem envolver redefinies e reimplementaes de classes, modificao de clientes e retestagem do sistema. A reformulao afeta muitas partes de um sistema de software e, invariavelmente, mudanas no-antecipadas so caras. Os padres de projeto ajudam a evitar esses problemas ao garantirem que o sistema possa mudar segundo maneiras especficas. Cada padro de projeto permite a algum aspecto da estrutura do sistema variar independentemente de outros aspectos, desta forma tornando um sistema mais robusto em relao a um tipo particular de mudana. Aqui apresentamos algumas causas comuns de reformulao de projeto, junto com o(s) padro(es) que as tratam: 1. Criando um objeto pela especificao explcita de uma classe. Especificar um nome de uma classe quando voc cria um objeto faz com que se comprometa com uma implementao em particular, em vez de se comprometer com uma determinada interface. Este compromisso pode complicar futuras mudanas. Para evit-lo, crie objetos indiretamente. Padres de projeto: Abstract Factory (95), Factory Method (112), Prototype (121). 2. Dependncia de operaes especficas. Quando voc especifica uma operao em particular, se compromete com uma determinada maneira de atender a uma solicitao. Evitando solicitaes codificadas inflexivelmente (hard-coded), voc torna mais fcil mudar a maneira como uma solicitao atendida, tanto em tempo de compilao como em tempo de execuo. Padres de projeto: Chain of Responsibility (212), Command (222). 3. Dependncia da plataforma de hardware e software. As interfaces externas do sistema operacional e as interfaces de programao de aplicaes (APIs) so diferentes para diferentes plataformas de hardware e software. O software que depende de uma plataforma especfica ser mais difcil de portar para outras plataformas. Pode ser at mesmo difcil mant-lo atualizado na sua plataforma nativa. Portanto, importante projetar o seu sistema para a limitar suas dependncias de plataformas. Padres de projeto: Abstract Factory (95), Bridge (151). 4. Dependncia de representaes ou implementaes de objetos. Clientes que precisam saber como um objeto representado, armazenado, localizado ou implementado podem necessitar ser alterados quando esse objeto muda. Ocultar essas informaes dos clientes evita a propagao de mudanas em cadeia. Padres de projeto: Abstract Factory (95), Bridge (151), Memento (266), Proxy (198); 5. Dependncias algortmicas. Os algoritmos so freqentemente estendidos, otimizados e substitudos durante desenvolvimento e reutilizao. Os objetos que dependem de algoritmos tero que mudar quando o algoritmo

40

CAPTULO 1 INTRODUO

mudar. Portanto os algoritmos que provavelmente mudaro deveriam ser isolados. Padres de projeto: Builder (104), Iterator (244), Strategy (292), Template Method (301), Visitor (305). 6. Acoplamento forte. Classes que so fortemente acopladas so difceis de reutilizar isoladamente, uma vez que dependem umas das outras. O acoplamento forte leva a sistemas monolticos, nos quais voc no pode mudar ou remover uma classe sem compreender e mudar muitas outras classes. O sistema torna-se uma massa densa difcil de aprender, portar e manter. Um acoplamento fraco aumenta a probabilidade de que uma classe possa ser usada por si mesma e de que um sistema possa ser aprendido, portado, modificado e estendido mais facilmente. Os padres de projeto usam tcnicas como acoplamento abstrato e projeto em camadas para obter sistemas fracamente acoplados. Padres de projeto: Abstract Factory (95), Bridge (151), Chain of Responsibility (212), Command (222), Faade (179), Mediator (257), Observer (274). 7. Estendendo a funcionalidade pelo uso de subclasses. Customizar ou adaptar um objeto atravs do uso de subclasses no costuma ser fcil. Cada classe nova tem um custo adicional (overhead) de inicializao, finalizao etc. Definir uma subclasse exige uma compreenso profunda da classe-me. Por exemplo, redefinir uma operao pode exigir a redefinio de outra (em outro lugar do cdigo). Uma operao redefinida pode ser necessria para chamar uma operao herdada. E o uso de subclasses pode levar a uma exploso de classes, porque voc pode ter que introduzir muitas subclasses novas, at mesmo para uma extenso simples. A composio de objetos, em geral, e a delegao, em particular, fornecem alternativas flexveis herana para a combinao de comportamentos. Novas funcionalidades podem ser acrescentadas a uma aplicao pela composio de objetos existentes de novas maneiras, em vez de definir novas subclasses a partir das classes existentes. Por outro lado, o uso intenso da composio de objetos pode tornar os projetos menos compreensveis. Muitos padres de projeto produzem arquiteturas (designs) nas quais voc pode introduzir uma funcionalidade customizada simplesmente pela definio de uma subclasse e pela composio de suas instncias com as existentes. Padres de projeto: Bridge (151), Chain of Responsibility (212), Composite (160), Decorator (170), Observer (274), Strategy (292). 8. Incapacidade para alterar classes de modo conveniente. Algumas vezes voc tem que modificar uma classe que no pode ser convenientemente modificada. Talvez necessite do cdigo-fonte e no disponha do mesmo (como pode ser o caso em se tratando de uma biblioteca comercial de classes). Ou, talvez, qualquer mudana possa requerer a modificao de muitas subclasses existentes. Padres de projeto oferecem maneiras para modificaes de classes em tais circunstncias. Padres de projeto: Adapter (140), Decorator (170), Visitor (305). Estes exemplos refletem a flexibilidade que os padres de projeto podem ajudlo a incorporar ao seu software. Quo crucial tal flexibilidade depende do tipo de software que voc est construindo. Vamos dar uma olhada no papel que os padres de projeto desempenham no desenvolvimento de trs grandes classes de software: programas de aplicao, toolkits e frameworks.

PADRES DE PROJETO

41

Programas de aplicao Se voc est construindo um programa de aplicao tal como um editor de documentos ou uma planilha, ento as prioridades mais altas so reutilizabilidade interna, facilidade de manuteno e de extenso. A reutilizabilidade interna garante que voc no projete, nem implemente, mais do que necessita. Os padres de projeto que reduzem dependncias podem aumentar a reusabilidade interna. O acoplamento mais fraco aumenta a probabilidade de que uma classe de objetos possa cooperar com vrias outras. Por exemplo, quando voc elimina dependncias de operaes especficas, pelo isolamento e encapsulamento de cada operao, torna mais fcil a reutilizao de uma operao em contextos diferentes. A mesma coisa tambm pode acontecer quando voc remove dependncias algortmicas e de representao. Os padres de projeto tambm tornam uma aplicao mais fcil de ser mantida quando so usados para limitar dependncias de plataforma e dar uma estrutura de camadas a um sistema. Eles melhoram a facilidade de extenso ao mostrar como estender hierarquias de classes e explorar a composio de objetos. O acoplamento reduzido tambm melhora a facilidade de extenso. Estender uma classe isoladamente mais fcil se a classe no depender de muitas outras classes. Toolkits (Bibliotecas de classes) Freqentemente uma aplicao incorporar classes de uma ou mais bibliotecas de classes pr-definidas, chamadas toolkits. Um toolkit um conjunto de classes relacionadas e reutilizveis, projetadas para fornecer uma funcionalidade til e de finalidade geral. Um exemplo de um toolkit um conjunto de classes de colees para listas, tabelas associativas, pilhas e outras coisas do tipo. A biblioteca I/O stream de C++ um outro exemplo. Os toolkits no impem um projeto especfico sua aplicao; simplesmente fornecem funcionalidades que podem auxiliar sua aplicao a executar o seu trabalho. Eles permitem a voc, como implementador, evitar a recodificao de funcionalidades comuns. Os toolkits enfatizam a reutilizao de cdigo. Eles so o equivalente em orientao a objetos a bibliotecas de sub-rotinas. O projeto de toolkits consideravelmente mais difcil que o projeto de aplicaes, porque os toolkits devem funcionar em muitas aplicaes para serem teis. Alm do mais, o autor do toolkit no est numa posio que lhe permita saber quais sero essas aplicaes ou suas necessidades especiais. Isso torna ainda mais importante evitar suposies e dependncias que possam limitar a flexibilidade do toolkit e conseqentemente sua aplicabilidade e sua efetividade. Frameworks (Arcabouos de classes) Um framework um conjunto de classes cooperantes que constroem um projeto reutilizvel para uma determinada categoria de software [Deu89,JF88]. Por exemplo, um framework pode ser orientado construo de editores grficos para diferentes domnios, tais como desenho artstico, composio musical e sistemas de CAD para mecnica [VL90,Joh92]. Um outro framework pode lhe ajudar a construir compiladores para diferentes linguagens de programao e diferentes processadores [ JML92]. Um outro, ainda, pode ajudar a construir aplicaes para modelagem financeira [BE93]. Voc customiza um framework para uma aplicao especfica atravs da criao de subclasses especficas para a aplicao, derivadas das classes abstratas do framework.

42

CAPTULO 1 INTRODUO

O framework dita a arquitetura da sua aplicao. Ele ir definir a estrutura geral, sua diviso em classes e objetos e em conseqncia as responsabilidades-chave das classes de objetos, como estas colaboram, e o fluxo de controle. Um framework predefine esses parmetros de projeto, de maneira que voc, projetista/implementador da aplicao, possa se concentrar nos aspectos especficos da sua aplicao. Um framework captura as decises de projeto que so comuns ao seu domnio de aplicao. Assim, frameworks enfatizam reutilizao de projetos em relao reutilizao de cdigo, embora um framework, geralmente, inclua subclasses concretas que voc pode utilizar diretamente. A reutilizao neste nvel leva a uma inverso de controle entre a aplicao e o software sobre a qual ela est baseada. Quando voc usa um toolkit (ou, pelos mesmos motivos, uma biblioteca convencional de sub-rotinas) escreve o corpo principal da aplicao e chama o cdigo que quer reutilizar. Quando usa um framework, voc reutiliza o corpo principal e escreve o cdigo que este chama. Voc ter que escrever operaes com nomes e convenes de chamada j especificadas; porm isso reduz as decises de projeto que voc tem que tomar. Como resultado, voc pode no somente construir aplicaes mais rapidamente, como tambm constru-las com estruturas similares. Elas so mais fceis de manter e parecem mais consistentes para seus usurios. Por outro lado, voc perde alguma liberdade criativa, uma vez que muitas decises de projeto j tero sido tomadas por voc. Se as aplicaes so difceis de projetar, e os toolkits so ainda mais difceis, os frameworks, ento, so os mais difceis entre todos. O projetista de um framework aposta que uma arquitetura funcionar para todas as aplicaes do domnio. Qualquer mudana substancial no projeto do framework reduziria seus benefcios consideravelmente, uma vez que a principal contribuio de um framework para uma aplicao a arquitetura que ele define. Portanto, imperativo projetar o framework de maneira que ele seja to flexvel e extensvel quanto possvel. Alm disso, porque as aplicaes so to dependentes do framework para o seu projeto, elas so particularmente sensveis a mudanas na interface do framework. medida que um framework evolui, as aplicaes tm que evoluir com ele. Isso torna o acoplamento fraco ainda mais importante; de outra maneira, mesmo pequenas mudanas no framework teriam grandes repercusses. Os tpicos de projeto que acabamos de discutir so os mais crticos para o projeto de um framework. Um framework que os trata atravs do uso de padres de projeto tem muito maior probabilidade de atingir altos nveis de reusabilidade de projeto e cdigo, comparado com um que no usa padres de projeto. Frameworks maduros comumente incorporam vrios padres de projeto. Os padres ajudam a tornar a arquitetura do framework adequada a muitas aplicaes diferentes, sem necessidade de reformulao. Um benefcio adicional obtido quando o framework documentado com os padres de projeto que ele usa [BJ94]. Pessoas que conhecem os padres obtm rapidamente uma compreenso do framework. Mesmo pessoas que no os conhecem podem se beneficiar da estrutura que eles emprestam sua documentao. A melhoria da documentao importante para todos os tipos de software, mas particularmente importante para frameworks. Os frameworks tm uma curva de aprendizado acentuada, que tem que ser percorrida antes que eles se tornem teis. Embora os padres de projeto no possam achatar a curva de aprendizado completamente, podem torn-la mais suave, ao fazer com que os elementos-chave do projeto do framework se tornem mais explcitos.

PADRES DE PROJETO

43

Como padres e frameworks tm algumas similaridades, as pessoas freqentemente se perguntam em que esses conceitos diferem. Eles so diferentes em trs aspectos principais. 1. Padres de projeto so mais abstratos que frameworks. Os frameworks podem ser materializados em cdigo, mas somente exemplos de padres podem ser materializados em cdigo. Um ponto forte dos frameworks que podem ser escritos em uma linguagem de programao, sendo no apenas estudados, mas executados e reutilizados diretamente. Em contraposio, os padres de projeto deste livro tm que ser implementados cada vez que eles so usados. Os padres de projeto tambm explicam as intenes, custos e benefcios (trade-offs) e conseqncias de um projeto. 2. Padres de projeto so elementos de arquitetura menores que frameworks. Um framework tpico contm vrios padres de projeto, mas a recproca nunca verdadeira. 3. Padres de projeto so menos especializados que frameworks. Os frameworks sempre tm um particular domnio de aplicao. Um framework para um editor grfico poderia ser usado na simulao de uma fbrica, mas ele no seria confundido com um framework para simulao. Em contraste, os padres de projeto, neste catlogo, podem ser usados em quase qualquer tipo de aplicao. Embora padres de projeto mais especializados que os nossos sejam possveis (digamos, padres para sistemas distribudos ou programao concorrente), mesmo estes no ditariam a arquitetura de uma aplicao da maneira como um framework o faz. Os frameworks esto se tornando cada vez mais comuns e importantes. Eles so a maneira pela qual os sistemas orientados a objetos conseguem a maior reutilizao. Aplicaes orientadas a objetos maiores terminaro por consistir-se de camadas de frameworks que cooperam uns com os outros. A maior parte do projeto e do cdigo da aplicao vir dos frameworks que ela utiliza ou ser influenciada por eles.

1.7 Como selecionar um padro de projeto


Com mais de 20 padres de projeto no catlogo para se escolher, pode ser difcil encontrar aquele que trata um problema de projeto particular, especialmente se o catlogo novo e estranho para voc. Aqui apresentamos diversas abordagens para encontrar o padro de projeto correto para o seu problema: Considere como padres de projeto solucionam problemas de projeto. A seo 1.6 discute como padres de projeto ajudam a encontrar objetos apropriados, determinar as suas granularidades, especificar interfaces e vrias outras formas pelas quais padres de projeto solucionam problemas de projeto. A consulta a essas discusses pode ajud-lo a guiar sua busca pelo padro correto; Examine as sees Inteno. A seo 1.4 (pgina 24) lista as sees de Inteno de todos os padres do catlogo. Leia toda a seo Inteno de cada padro para encontrar um ou mais padres que paream relevantes para o seu problema. Voc pode usar o esquema de classificao apresentado na Tabela 1.1 (pgina 26) para focalizar sua busca;

44

CAPTULO 1 INTRODUO

Estude como os padres se interrelacionam. A figura 1.1 (pgina 26) mostra graficamente relacionamentos entre os padres de projetos. O estudo desses relacionamentos pode ajudar a direcion-lo para o padro, ou grupo de padres, adequado; Estude padres de finalidades semelhantes. O catlogo (pgina 89) tem trs captulos: um para padres de criao, outro para padres estruturais, e um terceiro para padres comportamentais. Cada captulo comea com comentrios introdutrios sobre os padres e conclui com uma seo que os compara e contrasta. Esta seo dar uma viso sobre as semelhanas e diferenas de padres com propsitos similares; Examine uma causa de reformulao de projeto. Veja as causas de reformulao, comeando na pgina 41, para ver se o seu problema envolve uma ou mais delas. Ento olhe os padres que lhe ajudam a evitar essas causas; Considere o que deveria ser varivel no seu projeto. Esta abordagem o oposto de se focalizar nas causas de reformulao. Ao invs de considerar o que pode forar uma mudana em um projeto, considere o que voc quer ser capaz de mudar sem reprojetar. O foco, aqui, posto sobre o encapsulamento do conceito que varia, um tema de muitos padres de projeto. A Tabela 1.2 lista o(s) aspecto(s) do projeto que os padres de projeto lhe permitem variar independentemente. Dessa forma, eles podem ser mudados sem necessidade de reformulao de projeto.

1.8 Como usar um padro de projeto


Uma vez que tenha escolhido um padro de projeto, como voc o utiliza? Apresentamos aqui uma abordagem passo a passo para aplicar um padro de projeto efetivamente: 1. Leia o padro por inteiro uma vez, para obter sua viso geral. Preste ateno em particular s sees Aplicabilidade e Conseqncias, para assegurar-se de que o padro correto para o seu problema; 2. Volte e estude as sees Estrutura, Participantes e Colaboraes. Assegure-se de que compreende as classes e objetos no padro e como se relacionam entre si; 3. Olhe a seo Exemplo de Cdigo, para ver um exemplo concreto do padro codificado. O estudo do cdigo ajuda a aprender como implementar o padro; 4. Escolha nomes para os participantes do padro que tenham sentido no contexto da aplicao. Os nomes para os participantes dos padres de projeto so, geralmente, muito abstratos para aparecerem diretamente numa aplicao. No entanto, til incorporar o nome do participante no nome que aparecer na aplicao. Isso ajudar a tornar o padro mais explcito na implementao. Por exemplo, se voc usa o padro Strategy para um algoritmo de composio de textos, ento poder ter classes como Simple Layout Strategy ou TeXLayoutStrategy; 5. Defina as classes. Declare suas interfaces, estabelea os seus relacionamentos de herana e defina as variveis de instncia que representam dados e referncias a objetos. Identifique as classes existentes na sua aplicao que sero afetadas pelo padro e modifique-as de acordo;

PADRES DE PROJETO Tabela 1.2 Aspectos do projeto que o uso de padres permite variar

45

Propsito De Criao

Padro Abstract Factory (95) Builder (104) Factory Method (112) Prototype (121) Singleton (130)

Aspecto(s) que pode(m) variar famlias de objetos-produto como um objeto composto criado subclasse de objeto que instanciada classe de objeto que instanciada a nica instncia de uma classe interface para um objeto implementao de um objeto estrutura e composio de um objeto responsabilidade de um objeto sem usar subclasses interface para um subsistema custos de armazenamento de objetos como um objeto acessado; sua localizao objeto que pode atender a uma solicitao quando e como uma solicitao atendida gramtica e interpretao de uma linguagem como os elementos de um agregado so acessados, percorridos como e quais objetos interagem uns com os outros que informao privada armazenada fora de um objeto e quando nmero de objetos que dependem de um outro objeto; como os objetos dependentes se mantm atualizados estados de um objeto um algoritmo passos de um algoritmo operaes que podem ser aplicadas a (um) objeto(s) sem mudar sua(s) classe(s)

Estruturais

Adapter (140) Bridge (151) Composite (160) Decorator (170) Faade (179) Flyweight (187) Proxy (198)

Comportamentais

Chain of Responsibility (212) Command (222) Interpreter (231) Iterator (244) Mediator (257) Memento (266) Observer (274)

State (284) Strategy (292) Template Method (301) Visitor (305)

6. Defina nomes especficos da aplicao para as operaes no padro. Aqui novamente, os nomes em geral dependem da aplicao. Use as responsabilidades e colaboraes associadas com cada operao como guia. Seja consistente, tambm, nas suas convenes de nomenclatura. Por exemplo, voc pode usar consistentemente o prefixo Criar- para denotar um mtodo fbrica (factory method); 7. Implemente as operaes para suportar as responsabilidades e colaboraes presentes no padro. A seo de Implementao oferece sugestes para gui-lo na implementao. Os exemplos na seo Exemplo de Cdigo tambm podem ajudar.

46

CAPTULO 1 INTRODUO

Estas so apenas diretrizes para inici-lo nesta tcnica. Ao longo do tempo voc desenvolver sua prpria maneira de trabalhar com padres de projeto. Nenhuma discusso sobre como usar padres de projeto estaria completa sem algumas palavras sobre como no us-los. Os padres de projeto no devem ser aplicados indiscriminadamente. Freqentemente eles obtm flexibilidade e variabilidade pela introduo de nveis adicionais de endereamento indireto, e isso pode complicar um projeto e/ou custar algo em termos de desempenho. Um padro de projeto dever apenas ser aplicado quando a flexibilidade que ele oferece realmente necessria. As sees Conseqncias so muito teis quando avaliamos os custos e benefcios de um padro.

2
Um estudo de caso: projetando um editor de documentos
Este captulo apresenta um estudo de caso do projeto de um editor de documentos chamado Lexi 1, que apresenta caracterstica de um editor do tipo O-que-voc-v-o-que-voc-obtm(ou WYSIWYG)*. Ns veremos como os padres de projeto capturam solues para os problemas de projeto do Lexi e aplicaes semelhantes. No fim deste captulo, voc ter adquirido experincia com oito padres, aprendendo-os atravs do exemplo. A figura 2.1 ilustra a interface para usurio do Lexi. Uma representao WYSIWYG do documento ocupa a grande rea retangular no centro. O documento pode misturar textos e grficos livremente, em vrios estilos de formatao. Circundando o documento, esto os menus pull-down usuais e as barras de rolagem, mais uma coleo de cones de pgina, para saltar para uma pgina especfica no documento.

2.1 Problemas de projeto


Ns examinaremos sete problemas no projeto do Lexi: 1. Estrutura do documento. A escolha da representao interna para o documento afeta quase todos os aspectos do projeto do Lexi. Toda a edio, formatao, exibio e anlise de texto exigir percorrer a representao. A maneira como organizamos essa informao ter impacto sobre o projeto do resto da aplicao; 2. Formatao. Como que o Lexi efetivamente arranja textos e grficos em linhas e colunas? Que objetos so responsveis pela execuo de diferentes procedimentos de formatao? Como estes procedimentos interagem com a representao interna do documento?

* N. de R. T: Do ingls What you see is what you get.

48

CAPTULO 2 UM ESTUDO DE CASO: PROJETANDO UM EDITOR DE DOCUMENTOS

Figura 2.1 A interface de usurio de Lexi.

PADRES DE PROJETO

49

3. Adornando a interface de usurio. A interface de usurio do Lexi inclui barras de rolagem, bordas e sombreamentos que adornam a interface de documentos WYSIWYG. Tais adornos provavelmente mudaro, medida que a interface para o usurio do Lexi evoluir. Da a importncia de poder acrescentar e remover adornos facilmente, sem afetar o resto da aplicao; 4. Suportando mltiplos padres de interao (look-and-feel). O Lexi deve adaptarse facilmente a diferentes padres de interao, tais como o Motif e o Presentation Manager (PM), sem maiores modificaes; 5. Suportando mltiplos sistemas de janelas. Diferentes padres de interao so usualmente implementados em diferentes sistemas de janelas. O projeto do Lexi deve ser to independente quanto possvel do sistema de janelas; 6. Operaes do usurio. Os usurios controlam Lexi atravs de vrias interfaces de usurio, incluindo botes e menus pull-down. A funcionalidade por trs dessas interfaces est espalhada por todos dos objetos na aplicao. O desafio aqui fornecer um mecanismo uniforme tanto para o acesso desta funcionalidade espalhada como para poder desfazer seus efeitos; 7. Verificao ortogrfica e hifenizao. Como o Lexi suporta operaes analticas tais como a verificao de palavras grafadas incorretamente e determinao de pontos de hifenizao? Como podemos minimizar o nmero de classes que temos de modificar para acrescentar uma nova operao analtica? Ns discutimos esses problemas de projetos nas sees que se seguem. Cada problema tem um conjunto associado de objetivos, mais restries sobre como podemos atingir estes objetivos. Explicamos os objetivos e restries em detalhe, antes de propor uma soluo especfica. O problema e sua soluo ilustraro um ou mais padres de projeto. A discusso de cada problema culminar em uma breve introduo aos padres relevantes.

2.2 Estrutura do documento


Um documento , em ltima instncia, somente um conjunto de elementos grficos bsicos, tais como caracteres, linhas, polgonos e outras formas. Estes elementos capturam o contedo total de informao do documento. E, no entanto, o autor de um documento freqentemente v estes elementos no em termos grficos, mas sim em termos da estrutura fsica do documento linhas, colunas, figuras, tabelas e outras subestruturas.2 Por sua vez, estas subestruturas tm subestruturas prprias, e assim por diante. A interface para o usurio do Lexi deve permitir aos usurios manipular essas subestruturas diretamente. Por exemplo, um usurio deve ser capaz de tratar um diagrama como uma unidade, ao invs de uma coleo de elementos grficos primitivos individuais. O usurio deve ser capaz de referenciar uma tabela como um todo, e no como uma massa de texto e de grficos no-estruturada. Isso ajuda a tornar a interface simples e intuitiva. Para dar implementao de Lexi qualidades similares, escolheremos uma representao interna que corresponde estrutura fsica do documento. Em particular, a representao interna deve suportar o seguinte:

50

CAPTULO 2 UM ESTUDO DE CASO: PROJETANDO UM EDITOR DE DOCUMENTOS

manter a estrutura fsica do documento, isto , o arranjo de textos e grficos, em linhas, colunas, tabelas, etc; gerar e apresentar o documento visualmente; mapear posies no display a elementos na representao interna. Isto permite ao Lexi determinar a que o usurio est se referindo quando ele aponta para algo na representao visual. Alm desses objetivos h algumas restries. Primeiro, devemos tratar texto e grficos de maneira uniforme. A interface da aplicao permite ao usurio embutir texto dentro dos grficos livremente, e vice-versa. Devemos evitar tratar grficos como um caso especial de texto ou texto como um caso especial de grficos; caso contrrio, acabaremos tendo mecanismos redundantes para formatao e manipulao. Um conjunto de mecanismos deve ser suficiente tanto para textos como para grficos. Segundo, nossa implementao no dever ter que distinguir entre elementos simples e grupos de elementos na representao interna. Lexi deve ser capaz de tratar elementos simples e complexos uniformemente, desta maneira permitindo a criao de documentos arbitrariamente complexos. Por exemplo, o dcimo elemento na linha cinco da coluna dois poderia ser um carter ou um diagrama intricado com muitos subelementos. Tendo em vista que este elemento pode desenhar a si mesmo e especificar suas dimenses, sua complexidade no tem implicao sobre como e onde ele deve aparecer na pgina. Entretanto, opondo-se segunda restrio, h a necessidade de analisar o texto para coisas como erros de ortografia e potenciais pontos de hifenizao. Freqentemente ignoramos se o elemento de uma linha um objeto simples ou complexo, mas algumas vezes uma anlise depende dos objetos que esto sendo analisados. No faz sentido, por exemplo, verificar a ortografia de um polgono ou procurar fazer sua hifenizao. O projeto da representao interna deve levar em conta essa e outras restries potencialmente conflitantes.

Composio recursiva
Uma forma comum de representar informaes hierarquicamente estruturadas atravs do uso de uma tcnica chamada composio recursiva, a qual permite a construo de elementos progressivamente mais complexos a partir de elementos mais simples. A composio recursiva nos d uma maneira de compor um documento a partir de elementos grficos simples. Como primeiro passo, ns podemos colocar em quadros um conjunto de caracteres e grficos da esquerda para a direita, formando uma linha no documento. Ento mltiplas linhas podero ser arranjadas para formar uma coluna, mltiplas colunas podero formar uma pgina, e assim por diante (ver figura 2.2). Podemos representar esta estrutura fsica dedicando um objeto a cada elemento importante. Isso inclui no somente os elementos visveis, como caracteres e grficos, mas tambm os elementos invisveis, estruturais as linhas e as colunas. O resultado a estrutura de objeto mostrada na figura 2.3. Ao utilizar um objeto para cada caractere e elemento grfico no documento promovemos a flexibilidade nos nveis de detalhe mais finos do projeto do Lexi. Podemos tratar textos e grficos uniformemente com respeito maneira como eles so desenhados, formatados e embutidos uns nos outros. Podemos estender o Lexi para suportar novos conjuntos de caracteres sem afetar outras funcionalidades. A estrutura de objeto do Lexi imita a estrutura fsica do documento.

PADRES DE PROJETO
caracteres espao imagem composto (linha)

51

Gg

composto (coluna)

Figura 2.2 Composio recursiva de texto e grficos.

composto (coluna)

composto (linha)

composto (linha)

G g

espao

Figura 2.3 Estrutura de objetos para a composio recursiva de textos e de grficos.

Essa abordagem tem duas importantes implicaes. A primeira bvia: os objetos necessitam de classes correspondentes. A segunda implicao, que pode ser menos bvia, que essas classes devem ter interfaces compatveis, porque queremos tratar os objetos uniformemente. A maneira de tornar interfaces compatveis em uma linguagem como C++ relacionar as classes atravs da herana.

52

CAPTULO 2 UM ESTUDO DE CASO: PROJETANDO UM EDITOR DE DOCUMENTOS

Glyphs
Definiremos uma classe abstrata de nome Glyph para todos os objetos que podem aparecer na estrutura de um documento3. Suas subclasses definem tanto elementos grficos primitivos (como caracteres e imagens) quanto elementos estruturais (como linhas e colunas). A figura 2.4 ilustra uma parte representativa da hierarquia da classe Glyph, e a tabela 2.1 apresenta a interface bsica de glyph em mais detalhes usando a notao C++. 4

Glyph
Draw(Window) Intersects(point) Insert|Glyph, int) ...

Character Draw(Window w) Intersects(Point p) char c

Rectangle Draw(...) Intersects(...)

children Row Draw(Window w) Intersects(Point p) Insert(Glyph g, int i)

Polygon return true if point p intersects this character Draw(...) Intersects(...) return g into children at position i for all c in children if c>Intersects(p) return true forall c in children ensure c is positioned correctly; c>Draw(w)

w>DrawCharacter(c)

Figura 2.4 Hierarquia parcial da classe Glyph.

Tabela 2.1 Interface bsica de glyph (ou glifo)


Responsabilidade aparncia deteco de acerto estrutura Operaes

virtual void Draw (Window*) virtual void Bounds (Rect&) virtual bool Intersects (const Point&) virtual virtual virtual virtual void Insert (Glyph*, int) void Remove (Glyph*) Glyph* Child(int) Glyph* Parent ()

Os glyphs tm trs responsabilidades bsicas. Eles sabem: (1) como desenhar a si prprios; (2) que espao ocuparo; (3) quais so seus filhos e seus pais. As subclasses glyph redefinem a operao Draw para representarem a si prprios em uma janela. passada para eles uma referncia para um objeto Window na chamada para Draw. A classe Window define operaes grficas para a represen-

PADRES DE PROJETO

53

tao de texto e formas bsicas em uma janela na tela. Uma subclasse Rectangle (retngulo) de Glyph pode redefinir Draw como segue:

Em que _x0, _y0, _x1, _y1 so dados-membro de Rectangle que definem duas arestas opostas do retngulo. DrawRect a operao de Window que faz com que o retngulo aparea na tela. Um glifo (glyph) freqentemente necessita saber quanto espao um glifo filho ocupa, por exemplo, para arranjar a ele e a outros glifos em uma linha de maneira que nenhum se sobreponha (conforme mostrado na figura 2.2). A operao Bounds retorna rea retangular que o glifo ocupa. Ela retorna os cantos opostos do menor retngulo que contm o glifo. As subclasses de Glyph redefinem esta operao para retornar rea retangular na qual elas desenham. A operao Intersects indica se um ponto especificado intercepta o glifo. Sempre que o usurio clica sobre algum lugar do documento, o Lexi chama essa operao para determinar qual glifo ou estrutura de glifo est sob o mouse. A classe Rectangle redefine essa operao para computar a interseo do retngulo e o ponto dado. Como glifos podem ter descendentes, necessitamos de uma interface comum para acrescentar, remover e acessar esses descendentes. Por exemplo, os descendentes de um Row so os glifos que ela arruma em uma linha. A operao Insert insere um glifo na posio especificada por um ndice inteiro.5 A operao Remove remove um glifo especificado, se ele for de fato um descendente. A operao Child retorna o descendente (se houver) na posio especificada pelo ndice dado. Glifos com Row, que podem ter descendentes, devem usar Child internamente, ao invs de acessar a estrutura de dados do descendente diretamente. Desta maneira, voc no tem que modificar operaes como Draw para iterar atravs dos descendentes quando muda a estrutura de dados de um vetor para uma lista encadeada. Similarmente, Parent fornece uma interface padro para os pais do glifo, se houver. Os glifos em Lexi armazenam uma referncia para os seus ancestrais, e suas operaes Parent simplesmente retornam essa referncia.

O padro Composite
A composio recursiva boa no somente para documentos. Podemos us-la para representar qualquer estrutura hierrquica potencialmente complexa. O padro Composite (160) captura a essncia da composio recursiva em termos orientados a objetos. Esta uma boa ocasio para examinar esse padro e estud-lo, referindo-se a este cenrio quando necessrio.

2.3 Formatao
Ns estabelecemos uma forma para representar a estrutura fsica do documento. Em seguida, necessitamos imaginar como construir uma estrutura fsica que corresponda a um documento adequadamente formatado. A representao e a formatao so distintas. A habilidade de capturar a estrutura fsica de um documento no nos diz como chegar a uma determinada estrutura. Essa responsabilidade fica principalmen-

54

CAPTULO 2 UM ESTUDO DE CASO: PROJETANDO UM EDITOR DE DOCUMENTOS

te para o Lexi. Ele deve quebrar texto em linhas, linhas em colunas, e assim por diante, levando em conta os objetivos do usurio em nvel conceitual mais alto. Por exemplo, o usurio pode querer variar larguras de margens, indentaes, e tabulaes; espao simples ou duplo; e provavelmente muitas outras restries de formatao.6 O algoritmo de formatao do Lexi deve levar tudo isso em conta. A propsito, restringiremos o significado do termo formatao ao processo de quebrar uma coleo de glifos em linhas. De fato, usaremos os termos formatao e quebrar em linhas com o mesmo significado. As tcnicas que discutiremos se aplicam igualmente a quebrar linhas em colunas e a quebrar colunas em pginas.
Tabela 2.2 Compositor bsico de interface
Responsabilidade o que formatar quando formatar Operaes

void SetComposition (Composition*) virtual void Compose ()

Encapsulando o algoritmo de formatao


O processo de formatao, com todas as suas restries e detalhes, no fcil de automatizar. Existem muitas abordagens para o problema, e muitas pessoas apresentaram uma variedade de algoritmos de formatao com diferentes pontos fortes e fracos. Como o Lexi um editor WYSIWYG, um compromisso importante a ser considerado o equilbrio entre a qualidade da formatao e a sua velocidade. Geralmente, queremos um bom tempo de resposta do editor sem sacrificar a aparncia do documento. Esse compromisso est sujeito a muitos fatores, os quais nem todos podem ser avaliados em tempo de compilao. Por exemplo, o usurio pode tolerar um tempo de resposta ligeiramente mais lento em troca de uma melhor formatao. Esse compromisso pode tornar um algoritmo de formatao totalmente diferente e mais apropriado que o atualmente escolhido. Outra anlise custo-benefcio mais voltada implementao confronta velocidade de formatao e consumo da memria: possvel diminuir o tempo de formatao mantendo mais informao em memria (caching). Como os algoritmos de formatao tendem a ser complexos, tambm desejvel mant-los bem confinados ou melhor ainda completamente independentes da estrutura do documento. Idealmente, ns deveramos poder acrescentar um novo tipo de subclasse de Glyph sem nos preocuparmos com o algoritmo de formatao. Reciprocamente, a adio de um novo algoritmo de formatao no deveria exigir a modificao dos glifos existentes. Essas caractersticas sugerem que devemos projetar o Lexi de maneira a ser fcil mudar o algoritmo de formatao pelo menos em tempo de compilao, seno tambm em tempo de execuo. Podemos isolar o algoritmo e torn-lo ao mesmo tempo facilmente substituvel, encapsulando-o em um objeto. Mais especificamente, definiremos uma hierarquia de classes separada para objetos que encapsulam algoritmos de formatao. A raiz da hierarquia definir uma interface que suporta uma grande variedade de algoritmos de formatao, e cada subclasse implementar a interface para executar um determinado algoritmo. Ento poderemos introduzir uma subclasse Glifo que estruturar seus descendentes automaticamente, usando um determinado objeto-algoritmo.

PADRES DE PROJETO

55

Compositor e Composition
Definiremos uma classe Compositor para objetos que podem encapsular um algoritmo de formatao. A interface (tabela 2.2) possibilitar ao compositor saber quais glifos formatar e quando efetuar a formatao. Os glifos que ele formata so os descendentes de uma subclasse especial de Glypho chamada Composition. Uma Composition obtm uma instncia de uma subclasse Compositor (especializada em um algoritmo especfico de quebra de linhas) quando ela criada e diz ao Compositor para executar a operao Compose sobre os seus glifos, quando necessrio; por exemplo, quando o usurio muda um documento. A figura 2.5 ilustra os relacionamentos entre as classes Composition e Compositor.
Glyph

Insert(Glyph, int)

children

Composition Insert(Glyph g, int i)

compositor composition

Compositor

Compose() SetComposition()

Glyph::Insert(g, i) compositor.Compose()

ArrayCompositor Compose()

TeXCompositor Compose()

SimpleCompositor Compose()

Figura 2.5 Relacionamentos entre as classes Composition e Compositor.

Um objeto Composition no-formatado contm somente os glifos visveis que compem o contedo bsico do documento. Ele no contm glifos que determinam a estrutura fsica do documento, tais como Row e Column. A composio est neste estado logo aps ter sido criada e inicializada, com os glifos que ela deve formatar. Quando a composio necessita de uma formatao, ela chama a operao Compose do seu compositor. O Compositor, por sua vez, itera pelos descendentes da composio, e insere novos glifos de Row e Column de acordo com o seu algoritmo de quebra de linha.7 A figura 2.6 mostra a estrutura do objeto resultante. Os glifos que o compositor criou e inseriu na estrutura do objeto aparecem com fundos cinza na figura. Cada subclasse Compositor pode implementar um algoritmo diferente de quebra de linha. Por exemplo, um SimpleCompositor pode fazer um passo rpido, sem se preocupar com tais aspectos esotricos como a cor do documento. Uma boa colorao significa ter uma distribuio uniforme de texto e espaos em branco. Um TeXCompositor implementaria o algoritmo TEX completo [Knu84], o qual leva em conta coisas como cor, em troca de tempos de formatao mais longos. A separao das classes Compositor-Composition garante uma forte separao entre o cdigo que suporta a estrutura fsica do documento e o cdigo para diferentes algoritmos de formatao. Ns podemos acrescentar novas subclasses Compositor sem tocar as classes glyph, e vice-versa. De fato, podemos trocar o algoritmo de quebra de linhas acrescentando uma nica operao SetCompositor interface bsica de glifos de Composition.

56

CAPTULO 2 UM ESTUDO DE CASO: PROJETANDO UM EDITOR DE DOCUMENTOS

Glifos gerados pelo compositor

composition

column

compositor

row

row

G g
O padro Strategy

space

Figura 2.6 Estrutura de objetos refletindo quebra de linha dirigida por Compositor.

O encapsulamento de um algoritmo em um objeto a inteno do padro Strategy (292). Os participantes-chave no padro so os objetos Strategy (que encapsulam diferentes algoritmos) e o contexto no qual eles operam. Os objetos Compositor so estratgias, eles encapsulam diferentes algoritmos de formatao. Um objeto Composition o contexto para uma estratgia Compositor. A chave para aplicao do padro Strategy projetar, para a estratgia e seu contexto, interfaces genricas o bastante para suportar uma variedade de algoritmos. Voc no deve ter que mudar a interface da estratgia ou do contexto para suportar um novo algoritmo. No nosso exemplo, o suporte da interface bsica de Glyph para acesso a descendentes, sua insero e remoo, geral o bastante para permitir s subclasses Compositor mudar a estrutura fsica do documento, independentemente do algoritmo que elas utilizem para faz-lo. Da mesma forma, a interface de Compositor fornece aos objetos Composition tudo que eles necessitam para iniciar a formatao.

2.4 Adornando a interface do usurio


Consideraremos dois adornos na interface de usurio do Lexi. O primeiro acrescenta uma borda em volta da rea de edio de texto para demarcar a pgina de texto. O segundo acrescenta barras de rolamento que permitem ao usurio ver diferentes partes da pgina. Para tornar fcil o acrscimo e remoo desses adornos (especialmente em tempo de execuo), no devemos usar a herana para acrescent-los interface do usurio. Obteremos o mximo de flexibilidade se outros objetos da interface do usurio nem mesmo souberem que os adornos esto l. Isso nos permitir acrescentar e remover os adornos sem mudar outras classes.

Fecho transparente
Do ponto de vista da programao, adornar a interface do usurio envolve estender cdigo existente. O uso da herana para fazer tal extenso impede de arranjar os

PADRES DE PROJETO

57

adornos em tempo de execuo, mas um problema igualmente srio a exploso do nmero de classes que pode resultar de uma soluo baseada na herana. Poderamos acrescentar uma borda a Composition, criando subclasses suas para obter uma classe BorderedComposition. Ou poderamos acrescentar uma interface para rolamento da mesma forma, para obter uma subclasse Scrollable Composition. Se quisermos ter tanto barras de rolamentos como uma borda, podemos produzir uma subclasse BorderedScrollableComposition, e assim por diante. Isso levado ao extremo, far com que acabemos tendo uma classe para cada possvel combinao de adornos, uma soluo que se tornar rapidamente intratvel quando a variedade de adornos cresce. A composio de objetos oferece um mecanismo potencialmente mais tratvel e de extenso flexvel . Mas que objetos ns compomos? Uma vez que sabemos que estamos adornando um glifo existente, poderamos tornar o adorno em si um objeto (digamos, uma instncia da classe Border). Isto nos d dois candidatos para a composio, o glifo e a borda. O prximo passo decidir quem compe quem. Poderamos ter a borda contendo o glifo, o que faz sentido uma vez que a borda envolver o glifo na tela. Ou poderamos fazer o oposto colocar a borda no glifo mas, ento, teremos que fazer modificaes correspondente subclasse de Glyph para torn-la ciente da borda. Nossa primeira escolha, compor o glifo dentro da borda, mantm o cdigo de desenho da borda inteiramente na classe Border, deixando as outras classes inalteradas. Com o que a classe Border se parece? O fato de bordas terem uma aparncia, sugere que elas na realidade poderiam ser glifos; isto , Border deveria ser uma subclasse de Glyph. Mas h uma razo mais forte para fazer isto: os clientes no deveriam se preocupar se os glifos tm bordas ou no. Eles deveriam tratar glifos de maneira uniforme. Quando os clientes pedem a um glifo simples, sem bordas, para desenhar a si prprio, ele deve fazer isso sem acrescentar adornos. Se aquele glifo est composto numa borda, os clientes no deveriam ter que tratar a borda contendo o glifo de forma diferente; eles simplesmente pedem para ele desenhar a si prprio, da mesma maneira que fizeram anteriormente com o glifo simples. Isto implica que a interface Border corresponde interface de Glyph. Tornamos Border uma subclasse de Glyph para garantir este relacionamento. Tudo isto nos leva ao conceito de fecho transparente, que combina as noes de: (1) composies de descendente-nico (ou single-component composition) e (2) interfaces compatveis. Geralmente, os clientes no conseguem dizer se esto tratando com o componente ou com o seu fecho (isto , o pai do descendente), especialmente se o fecho simplesmente delega todas as suas operaes para seu componente. Mas o fecho tambm pode aumentar o comportamento do componente, executando trabalho ele mesmo antes e/ou aps delegar uma operao. Mas, efetivamente, o fecho tambm pode acrescentar estados ao componente. Ns veremos como em seguida.

Monoglyph
Podemos aplicar o conceito de fecho transparente a todos os glifos que adornam outros glifos. Para tornar este conceito concreto, definiremos uma subclasse de Glyph chamada MonoGlyph para servir como uma classe abstrata para glifos de adorno, como por exemplo Border (ver figura 2.7). MonoGlyph armazena uma referncia para um componente e encaminha-lhe todas as solicitaes.

58

CAPTULO 2 UM ESTUDO DE CASO: PROJETANDO UM EDITOR DE DOCUMENTOS

Glyph

Draw(Window)

MonoGlyph component Draw(Window)

Border Draw(Window) DrawBorder(Window)

Scroller Draw(Window)

Figura 2.7 Relacionamentos de classe de MonoGlyph.

Isso torna MonoGlyph, por omisso (by default), totalmente transparente para os clientes. Por exemplo, MonoGlyph implementa a operao Draw como segue:

As subclasses de MonoGlyph reimplementam pelo menos uma destas operaes passadas para frente. Border::Draw, por exemplo, primeiro invoca a operao da classe-me MonoGlyph: Draw no componente, para deixar o componente fazer a sua parte isto , desenhar qualquer coisa, exceto a borda. Ento Border: Draw desenha a borda chamando uma operao privada de nome DrawBorder, os detalhes da qual ns omitiremos:

Repare como Border::Draw efetivamente estende a operao da classe-me para desenhar a borda. Isso contrasta com meramente substituir a operao da classeme, o que omitiria a chamada para MonoGlyph: Draw. Uma outra subclasse de MonoGlyph aparece na figura 2.7. O Scroller um MonoGlyph que desenha seu componente em diferentes localizaes, baseado nas posies de duas barras de rolamento, que acrescenta como adornos. Quando Scroller desenha seu componente, pede ao sistema grfico para pod-lo de maneira que se mantenha dentro dos seus limites. Podar partes do componente que foram roladas para fora de vista impede que elas apaream na tela. Agora temos todas as peas de que necessitamos para acrescentar uma borda e uma interface de rolagem rea de edio de texto do Lexi. Comporemos a instncia existente de Composition em uma instncia de Scroller para acrescentar a interface de rolagem e comporemos essa instncia de Scroller numa instncia de Border. A estrutura do objeto resultante aparece na figura 2.8.

PADRES DE PROJETO

59

border

scroller

composition

column

row

row

G g

space

Figura 2.8 Estrutura de objeto adornado.

Note que podemos inverter a ordem da composio, colocando a composio com bordas na instncia de Scroller. Nesse caso, a borda ser rolada junto com o texto, o que pode ou no ser desejvel. O ponto importante aqui que o fecho transparente torna fcil experimentar diferentes alternativas e mantm os clientes livres de cdigo de adorno. Note tambm que a borda compe um glifo, no dois ou mais. Isso diferente das composies que definimos at aqui, nas quais os objetos-pais podiam ter um nmero arbitrrio de descendentes. Aqui, colocando uma borda em volta de algo implica que esse algo singular. Ns poderamos atribuir um significado ao adorno de mais de um objeto por vez, mas ento teramos que misturar muitos tipos de composies com a noo de adorno: adorno de linha, adorno de coluna, e assim por diante. Isso no nos ajudaria, uma vez que j temos classes para esses tipos de composies. Assim, melhor usar classes existentes para a composio e acrescentar novas classes para adornar o resultado. Mantendo o adorno independente de outros tipos de composio tanto simplifica as classes de adorno como reduz o seu nmero. Isso tambm nos evita a replicao da funcionalidade de composies existentes.

60

CAPTULO 2 UM ESTUDO DE CASO: PROJETANDO UM EDITOR DE DOCUMENTOS

O padro Decorator
O padro Decorator (170) captura relacionamentos de classe e objeto que suportam adornos atravs de fecho transparente. O termo adorno, na realidade, tem um significado mais amplo do que aquele que consideramos at aqui. No padro Decorator, adorno se refere a qualquer coisa que acrescenta responsabilidades a um objeto. Podemos pensar, por exemplo, em adornar uma rvore sinttica abstrata com aes semnticas, um autmato de estados finitos com novas transies, ou uma rede de objetos persistentes com rtulos de atributos. O Decorator generaliza a abordagem que usamos no Lexi para torn-la mais amplamente aplicvel.

2.5 Suportando mltiplos estilos de interao


A obteno de portabilidade para diferentes plataformas de hardware e software um dos maiores problemas no projeto de sistemas. Redirecionar o Lexi para uma nova plataforma no deveria exigir uma reviso geral, pois, ento, no valeria a pena o redirecionamento. Deveramos tornar a portabilidade to fcil quanto possvel. Um obstculo portabilidade a diversidade de padres de interao, que tem por objetivo impor uniformidade entre aplicaes. Esses padres definem diretrizes sobre como as aplicaes apresentam-se e reagem ao usurio. Embora os padres existentes no sejam to diferentes uns dos outros, as pessoas certamente no confundiro um com o outro as aplicaes em Motif no parecem com, e no reagem exatamente como, seus equivalentes em outras plataformas, e vice-versa. Uma aplicao que roda em mais de uma plataforma deveria seguir o guia de estilo da interao do usurio de cada plataforma. Nossos objetivos de projeto so de tornar o Lexi aderente a mltiplos estilos de interao e tornar fcil acrescentar suporte a novos padres medida que eles (invariavelmente) surgirem. Queremos tambm que o nosso projeto suporte a ltima palavra em flexibilidade: mudar o estilo de interao do Lexi em tempo de execuo.

Abstraindo a criao de objetos


Tudo o que vemos e com o que interagimos na interface de usurio do Lexi um glifo composto de outros glifos invisveis como Row e Column. Os glifos invisveis compem os visveis, como boto e caractere, e os dispem na tela de maneira apropriada. Os guias de estilo tm muito a dizer sobre interao (look-and-feel) dos assim chamados widgets*, outro termo para glifos visveis, como botes, barras de rolamento e menus que funcionam como elementos de controle em uma interface de usurio. Os widgets podem usar glifos mais simples, como caracteres, crculos, retngulos e polgonos para apresentar dados. Assumiremos que temos dois conjuntos de classes de glifos widgets com os quais podemos implementar mltiplos padres de interao: 1. Um conjunto de subclasses abstratas de Glyph para cada categoria de glifos widgets. Por exemplo, uma classe abstrata ScrollBar aumentar a interface bsica de glifos para acrescentar operaes gerais de rolagem; Buttom uma classe abstrata que acrescenta operaes orientadas para botes, e assim por diante.
* N. de R.T: Widget uma expresso cunhada, e diz-se que vem da juno de WINDOW GADGET, designando elementos de sistemas de interface de usrio.

PADRES DE PROJETO

61

2. Um conjunto de subclasses concretas para cada subclasse abstrata que implementa diferentes padres de interao. Por exemplo, ScrollBar pode ter como subclasses MotifScrollBar e PMScrollBar que implementam barras de rolamento no estilo do Motif e do Presentation Manager, respectivamente. Lexi deve distinguir entre glifos widgets para diferentes estilos de padres de interao. Por exemplo, quando Lexi necessita colocar um boto na sua interface, ele deve instanciar uma subclasse de Glyph para o estilo correto de boto. (MotifButton, PMButton, MacButton, etc.). Est claro que a implementao do Lexi no pode fazer isso diretamente, digamos, usando uma chamada para um constructor em C++. Isso seria codificar de forma rgida (hard-code) o boto de um estilo particular, tornando impossvel de selecionar o estilo em tempo de execuo. Teramos, tambm, que registrar, acompanhar e mudar cada uma dessas chamadas para um construtor para poder portar o Lexi para uma outra plataforma. E botes so somente uma das variedades de widgets na interface do usurio do Lexi. Encher o nosso cdigo com chamadas para construtor, uma para cada conjunto de classes especficas de um estilo de interao, leva- nos a um pesadelo de manuteno esquea somente uma e voc pode acabar com um menu de Motif no meio da sua aplicao Mac. O Lexi necessita uma maneira de determinar o padro de interao para o qual est sendo direcionado, de forma a poder criar os widgets apropriados. No somente devemos evitar fazer chamadas explcitas para um constructor como tambm devemos ser capazes de substituir um conjunto inteiro de widgets facilmente. Podemos conseguir as duas coisas abstraindo o processo de criao de objetos. Um exemplo ilustrar o que queremos dizer.

Classes-fbrica e Classes-produto
Normalmente, podemos criar uma instncia de um glifo de uma barra de rolamento Motif com o seguinte cdigo C++:

Este o tipo de cdigo a ser evitado se voc quiser minimizar as dependncias de padres de interao do Lexi. Mas suponha que inicializemos sb, como segue:

em que guiFactory uma instncia de uma classe MotifFactory. A operao CreateScrollBar retorna uma nova instncia da subclasse ScrollBar apropriada para o estilo de interao desejado, neste caso Motif. Tanto quanto interessa aos clientes, o efeito o mesmo que chamar diretamente o constructor de MotifScrollBar. Mas h uma diferena crucial: no existe mais nada no cdigo que menciona Motif por nome. O objeto guiFactory abstrai o processo de criao no somente de barras de rolamento Motif, mas de barras de rolamento para qualquer padro de interao. E guiFactory no est limitado a produzir somente barras de rolamento. Ela pode criar uma gama completa de glifos widget, incluindo barras de rolamento, botes, campos de entrada, menus, e assim por diante. Tudo isso possvel porque MotifFactory uma subclasse de GUIFactory, uma classe abstrata que define uma interface geral para criao de glifos widget. Ela inclui operaes como CreateScrollBar e CreateButton para instanciar diferentes tipos

62

CAPTULO 2 UM ESTUDO DE CASO: PROJETANDO UM EDITOR DE DOCUMENTOS

de glifos widget. As subclasses de GUIFactory implementam essas operaes para retornar glifos, tais como MotifScrollBar e PMButtom, que implementam estilos especficos de interao. A figura 2.9 mostra a hierarquia de classe resultante para objetos guiFactory. Dizemos que fbricas (ou factories) criam objetos-produto. Alm do mais, os produtos que uma fbrica produz esto relacionados uns aos outros; neste caso, os produtos so todos widgets para o mesmo padro de apresentao e resposta. A figura 2.10 mostra algumas das classes-produto necessrias para fazer fbricas funcionarem para glifos widget.

GUIFactory

CreateScrollBar() CreateButton() CreateMenu() ...

MotifFactory CreateScrollBar() CreateButton() CreateMenu() ...

PMFactory CreateScrollBar() CreateButton() CreateMenu() ...

MacFactory CreateScrollBar() CreateButton() CreateMenu() ...

return new MotifMenu

return new PMMenu

return new MacMenu

return new MotifButton

return new PMButton

return new MacButton

return new MotifScrollBar

return new PMScrollBar

return new MacScrollBar

Figura 2.9 Hierarquia de classe de GUIFactory.


Glyph

ScrollBar

Button

Menu

ScrollTo(int)

Press()

Popup()

MotifScrollBar ScrollTo(int)

MacScrollBar ScrollTo(int)

MotiButton Press()

MacButton Press()

MotifMenu Popup()

MacMenu Popup()

PMScrollBar ScrollTo(int)

PMButton Press()

PMMenu Popup()

Figura 2.10 Classes-produto abstratas e subclasses concretas.

PADRES DE PROJETO

63

A ltima questo que temos que responder de onde veio a instncia de


GUIFactory? A resposta : de qualquer lugar que seja conveniente. A varivel guiFactory poderia ser uma varivel global, um membro esttico de uma classe

bem conhecida, ou mesmo uma varivel local se toda a interface do usurio for criada dentro de uma classe ou funo. Existe mesmo um padro de projeto, Singleton (130), para administrar objetos bem conhecidos, nicos do seu tipo, como este. Entretanto, o importante a fazer inicializar a guiFactory em um ponto do programa antes que ela seja usada alguma vez para criar widgets, mas depois que estiver claro qual o estilo de interao desejado. Se estilo de interao conhecido em tempo de compilao, ento a guiFactory pode ser inicializada como uma simples atribuio de uma nova instncia do tipo fbrica no comeo do programa:

Se o usurio pode especificar o estilo de interao como um nome de um string em tempo de ativao, (ou tempo de startup), ento o cdigo para criar a fbrica pode ser

usurio ou ambiente fornece esse valor no incio da execuo

H maneiras mais sofisticadas de selecionar a fbrica em tempo de execuo. Por exemplo, voc pode manter um registro que mapeia strings a objetos-fbrica. Isso permite registrar instncias de novas subclasses-fbricas sem modificar o cdigo existente, como a abordagem precedente exige. Tambm no preciso linkeditar todas as fbricas especficas para cada plataforma aplicao. Isso importante porque no possvel linkeditar uma MotifFactory em uma plataforma que no suporta o Motif. Mas o ponto importante aqui que uma vez configurada a aplicao com objetofbrica correto, seu estilo de interao estabelecido a partir de ento. Se mudarmos de idia, podemos reinicializar a guiFactory como uma fbrica para estilo de interao diferente e, ento, reconstruir a interface. Independente de como e quando decidimos inicializar a guiFactory, sabemos que uma vez que o fizermos, a aplicao pode criar o estilo de interao apropriado sem modificao.

O padroAbstract Factory
Fbricas e produtos so os participantes-chave no padro Abstract Factory (95). Esse padro captura o processo de criar famlias de objetos-produto relacionados sem instanciar classes diretamente. Ele mais apropriado quando o nmero e tipos gerais

64

CAPTULO 2 UM ESTUDO DE CASO: PROJETANDO UM EDITOR DE DOCUMENTOS

de objetos-produto mantm-se constantes, e existem diferenas entre famlias especficas de produtos. Escolhemos entre as famlias instanciando uma certa fbrica concreta e depois disso a usamos consistentemente para criar produtos. Podemos tambm trocar famlias inteiras de produtos substituindo a fbrica concreta por uma instncia de uma fbrica diferente. A nfase dos padres Abstract Factory em famlias de produtos os distingue de outros padres de criao que envolvem somente um tipo de objeto-produto.

2.6 Suportando mltiplos sistemas de janelas


O estilo de interao somente um dos muitos tpicos referentes portabilidade. Um outro tpico o ambiente de janelas no qual o Lexi executado. O sistema de janelas de uma plataforma cria a iluso de mltiplas janelas superpostas em um display bitmapeado. Ele administra o espao da tela para as janelas e direciona as entradas para as mesmas, provenientes do teclado e do mouse. Hoje existem vrios sistemas de janelas importantes e grandemente incompatveis (por exemplo: Macintosh, Presentation Manager, Windows, X). Gostaramos que o Lexi pudesse ser executado em tantos deles quanto possvel, exatamente pelas mesmas razes que ns suportamos mltiplos estilos de interao.

Podemos usar um Abstract Factory?


primeira vista, isso pode parecer uma outra oportunidade para aplicar o padro Abstract Factory, mas as restries sobre a portabilidade para sistemas de janela diferem significativamente daquelas para a independncia de estilos de interao. Ao aplicar o padro Abstract Factory, assumimos que iremos definir as classes concretas de glifos widgets para cada estilo de interao. Isso significa que poderamos derivar cada produto concreto para um padro particular (por exemplo: MotifScrollBar e MacScrollBar) de uma classe-produto abstrata (por exemplo, ScrollBar). Mas suponha que j temos diversas hierarquias de classes de fornecedores diferentes, uma para cada estilo de interao. Naturalmente, altamente improvvel que estas hierarquias sejam compatveis em todos os aspectos. Logo, ns no teremos uma classe abstrata comum de produto para cada tipo de widget (ScrollBar, Buttom, Menu, etc.) e o padro Abstract Factory no funcionar sem essas classes cruciais. Temos que fazer com que as diferentes hierarquias de widgets tenham aderncia a um conjunto comum de interfaces abstratas de produtos. Somente ento poderemos declarar as operaes Create. . . adequadamente na interface da nossa fbrica abstrata. Havamos resolvido este problema para widgets atravs do desenvolvimento de nossas prprias classes abstratas e concretas de produto. Agora estamos frente a um problema semelhante ao tentar fazer o Lexi funcionar nos sistemas de janelas existentes: isto , diferentes sistemas de janelas tm interfaces de programao incompatveis. Desta vez, as coisas so um pouco mais difceis porque no podemos nos dar ao luxo de implementar nosso prprio sistema de janela no-padronizado (pessoal e particular). Mas existe uma salvao. Do mesmo modo que estilos de interao, as interfaces de sistemas de janelas no so radicalmente diferentes umas das outras, porque todos os sistemas de janela fazem em geral a mesma coisa. Necessitamos de um conjunto

PADRES DE PROJETO

65

uniforme de abstraes de janelas que nos permita usar as diferentes implementaes dos sistemas de janelas e introduzir cada uma delas debaixo de uma interface comum.

Encapsulando dependncias de implementao


Na seo 2.2, introduzimos uma classe Window para exibir um glifo, ou estrutura de glifo, no display. No especificamos o sistema de janelas com o qual esse objeto funcionava, porque a verdade que ele no provm de nenhum sistema de janelas em particular. A classe Window encapsula as coisas que as janelas tendem a fazer em todos os sistemas de janelas: elas fornecem operaes para desenhar formas geomtricas bsicas; podem minimizar e maximizar a si mesmas; podem redimensionar a si mesmas; Podem (re)desenhar seus contedos conforme a necessidade, por exemplo, quando so maximizadas, ou quando uma parte sobreposta e obscurecida do seu espao na tela torna-se visvel.

A classe Window deve abranger a funcionalidade das janelas de diferentes sistemas de janelas. Vamos considerar duas filosofias opostas: 1. Interseo de funcionalidade. A interface da classe Window fornece somente funcionalidade que comum a todos sistemas de janelas. O problema com esta abordagem que a nossa interface de Window acaba sendo somente to poderosa quanto o sistema de janelas menos capaz. No podemos tirar proveito de recursos mais avanados, mesmo que a maioria (mas no todos) dos sistemas de janela os suportem. 2. Unio de funcionalidade. Criar uma interface que incorpora as capacidades de todos os sistemas existentes. O problema aqui que a interface resultante pode ser bem grande e incoerente. Alm disso, teremos que alter-la (e o Lexi, que depende dela) toda vez que um fornecedor revisar a interface do seu sistema de janelas. Nenhum dos extremos uma soluo vivel; assim, o nosso projeto ficar mais ou menos entre ambos. A classe Window fornecer uma interface conveniente que suporte a maioria dos recursos populares para janelas. Como o Lexi tratar com essa classe diretamente, a classe Window tambm dever suportar as coisas de que o Lexi tem conhecimento, ou seja, glifos. Isso significa que a interface de Window deve incluir um conjunto bsico de operaes grficas que permitem aos glifos desenharem-se a si prprios na janela. A tabela 2.3 d uma amostra das operaes na interface da classe Window. Window uma classe abstrata. As subclasses concretas de Window suportaro os diferentes tipos de janelas com as quais os usurios lidam. Por exemplo, janelas de aplicao, cones e dilogos de aviso so todos janelas, mas tm comportamentos um pouco diferentes. Assim, podemos definir subclasses como ApplicationWindow, IconWindow e DialogWindow para capturar essas diferenas. A hierarquia de classe resultante fornece a aplicaes como Lexi uma abstrao de janelas uniforme e intuitiva, que no depende de um sistema de janelas de um fornecedor em particular:

66

CAPTULO 2 UM ESTUDO DE CASO: PROJETANDO UM EDITOR DE DOCUMENTOS


glyph window

Glyph

Draw(Window)

Redraw() Iconify() Lower() ... DrawLine() ...

glyph>Draw(this)

ApplicationWindow

IconWindow Iconify()

DialogWindow Lower()

owner

owner>Lower()

Tabela 2.3 Interface da classe Window

Responsabilidade Administrao de janelas

Operaes

Grficos

virtual virtual virtual virtual virtual ... virtual virtual virtual virtual ...

void void void void void void void void void

Redraw () Raise () Lower () Iconify () Deiconify () DrawLine (...) DrawRect (...) DrawPolygon (...) DrawText (...)

Agora que definimos uma interface de janelas para o Lexi usar, de onde vm as janelas reais, especficas para as plataformas? Se no estamos implementando nosso prprio sistema de janelas, ento em algum ponto a nossa abstrao de janelas deve ser implementada em termos do que oferecido pelo sistema de janelas ao qual se destina. Assim, onde vai existir esta implementao? Uma abordagem seria implementar mltiplas verses da classe Window e suas subclasses, uma verso para cada plataforma com janelas. Teramos que escolher a verso a ser usada quando construssemos o Lexi para uma dada plataforma. Mas imagine as dores de cabea que teramos para manter o controle de mltiplas classes, todas de nome Window, mas cada uma implementada em um sistema de janelas diferente. Como alternativa, poderamos criar subclasses especficas da implementao de cada classe na hierarquia de Window e acabar tendo um outro problema de exploso de subclasses, como o que tivemos ao tentar acrescentar adornos. Ambas alternativas tm ainda um outro ponto negativo: nenhuma nos d a flexibilidade de mudar o sistema de janelas que ns usamos depois que compilamos o programa. Assim, tambm teremos que manter disposio vrios executveis diferentes.

PADRES DE PROJETO

67

Nenhuma alternativa muito atraente, mas o que mais podemos fazer? A mesma coisa que fizemos para a formatao e o adorno, ou seja, encapsular o conceito que varia. O que varia nesse caso a implementao do sistema de janelas. Se encapsulamos a funcionalidade de um sistema de janelas em um objeto, ento podemos implementar nossa classe Window e suas subclasses em termos da interface daquele objeto. Alm do mais, se aquela interface pode atender a todos os sistemas de janelas em que estamos interessados, ento no teremos que mudar Window ou algumas das suas subclasses para suportar diferentes sistemas de janela. Podemos configurar objetos-janela dos sistemas de janelas que queremos, simplesmente passando a eles o objeto de encapsulamento do sistemas de janela apropriado. Podemos at mesmo configurar a janela em tempo de execuo.

Window e WindowImp
Definiremos uma hierarquia de classes separada, chamada WindowImp, na qual podemos ocultar as implementaes de diferentes sistemas de janelas. WindowImp uma classe abstrata para objetos que encapsulam o cdigo dependente dos sistemas de janelas. Para o Lexi funcionar em um sistema particular de janelas, configuramos cada objeto-janela como uma instncia de uma subclasse de WindowImp para aquele sistema. O seguinte diagrama mostra o relacionamento entre as hierarquias de Window e WindowImp:
window imp

Raise() DrawRect(...)

windowlmp

DeviceRaise() DeviceRect(...) ...


DialogWindow

ApplicationWindow

IconWindow

MacWindowlmp DeviceRaise() DeviceRect(...) ...

PMWinodwIMp DeviceRaise() DeviceRect(...) ...

XWindowimp DeviceRaise() DeviceRect(...) ...

Ao ocultar as implementaes em classes WindowImp, evitamos poluir as classes de Window com dependncias de sistemas de janelas, o que mantm a hierarquia de classe de Window comparativamente pequena e estvel. Quando necessrio, podemos facilmente estender a hierarquia da implementao para suportar novos sistemas de janelas Subclasses de WindowImp As subclasses de WindowImp convertem solicitaes em operaes especficas para o sistema de janelas. Considere o exemplo que usamos na seo 2.2. Definimos Rectangle::Draw em termos da operao de DrawRect na instncia de Window:

A implementao default de DrawRect usa a operao abstrata para desenhar retngulos declarada por WindowImp:

68

CAPTULO 2 UM ESTUDO DE CASO: PROJETANDO UM EDITOR DE DOCUMENTOS

em que _ imp uma varivel membro de Window que armazena a WindowImp com a qual a Window configurada. A implementao de Window definida pela instncia da subclasse de WindowImp que apontada por _ imp. Para uma XWindowImp (isto , uma subclasse WindowImp para um sistema de janelas X, a implementao de DeviceRect poderia se parecer com o cdigo abaixo.

DeviceRect definida desta forma por que XDrawRectangle (a interface X para desenhar um retngulo) define um retngulo em termos do seu canto esquerdo inferior, sua largura e sua altura. DeviceRect deve computar estes valores a partir daqueles fornecidos. Primeiro, ele verifica qual o canto esquerdo inferior (uma vez que (x0, y0) pode ser um dos quatro cantos do retngulo), e ento calcula a largura e a altura. A PMWindowImp (uma subclasse de WindowImp para o Presentation Manager) definiria DeviceRect de forma diferente:

reporta ocorrncia de erro

PADRES DE PROJETO

69

Por que essa implementao to diferente da verso X? Bem, PM no tem explicitamente uma operao para desenhar retngulos como X tem. Ao invs disso, PM tem uma interface mais geral para especificar vrtices de formas multisegmentadas (chamadas de path) e para delinear ou preencher a rea que elas fecham. Obviamente, a implementao de DeviceRect de PM bem diferente daquela de X, mas isso no importa. WindowImp esconde as variaes nas interfaces dos sistemas de janelas atrs de uma interface potencialmente grande, porm estvel. Isso permite aos codificadores da subclasse Window se concentrarem na abstrao de janela e no nos detalhes de um sistema de janelas. Isso tambm permite-nos acrescentar suporte para novos sistemas de janelas sem afetar as classes de Window. Configurando Windows com WindowImps Um tpico-chave que no tratamos como uma janela fica configurada com a subclasse adequada de WindowImp. Dito de outra maneira, quando que_imp iniciado e quem sabe qual o sistema de janelas (e, conseqentemente, qual subclasse de WindowImp) est em uso? A janela necessitar de algum tipo de WindowImp antes que possa fazer algo de interesse. Existem vrias possibilidades, mas nos concentraremos em uma que usa o padro Abstract Factory (95). Podemos definir uma classe Abstract Factory, chamada WindowSystemFactory, que fornece uma interface para a criao de diferentes tipos de objetos de implementao dependentes do sistema de janelas:

uma operao "Create..." para cada recurso.

Agora podemos definir uma fbrica concreta para cada sistema de janelas:

O construtor para a classe-base Window pode usar a interface da WindowSystemFactory para iniciar o membro _imp com a WindowImp correta para o sistema de janelas:

70

CAPTULO 2 UM ESTUDO DE CASO: PROJETANDO UM EDITOR DE DOCUMENTOS

A varivel windowSystemFactory uma instncia bem conhecida de uma subclasse de WindowSystemFactory, aparentada com a bem conhecida varivel guiFactory que define a apresentao e a resposta. A varivel windowSystemFactory pode ser iniciada da mesma maneira.

O padro Bridge
A classe WindowImp define uma interface para os recursos comuns dos sistemas de janela, mas seu projeto orientado por restries diversas daquelas que se aplicam interface de Window. Os programadores de aplicao no lidaro com a interface de WindowImp diretamente, mas somente com objetos Window. Desse modo, a interface de WindowImp no precisa corresponder viso do mundo do programador da aplicao, como era nossa preocupao no projeto da hierarquia e interface da classe Window. A interface de WindowImp pode refletir de maneira mais prxima o que os sistemas de janela fornecem na realidade, com defeitos e tudo mais. Ela pode ser orientada ou para uma abordagem de interseo ou para uma unio de funcionalidade, qualquer uma que se adapte melhor ao sistema de janelas-alvo (isto , aquele sob o qual, e com cujos recursos, a aplicao rodar). O que importante perceber que a interface de Window cuida do programador de aplicaes, enquanto que o WindowImp cuida dos sistemas de janelas. Separar a funcionalidade de janelas em hierarquias Window e WindowImp permite-nos implementar e especializar essas interfaces independentemente. Os objetos dessas hierarquias cooperam para permitir que o Lexi funcione, sem modificaes, em mltiplos sistemas de janelas. A relao entre Window e WindowImp um exemplo do padro Bridge (151). A inteno por trs do Bridge permitir s hierarquias de classes separadas trabalharem em conjunto, mesmo quando elas evoluem independentemente. Nossos critrios de projetos levaram-nos a criar duas hierarquias de classes separadas, uma que suporta a noo lgica de janelas, e outra para capturar diferentes implementaes de janelas. O padro Bridge permite-nos manter e melhorar as nossas abstraes lgicas dos conceitos de janelas sem tocar no cdigo dependente do sistema que as implementa, e vice-versa.

2.7 Operaes do usurio


Uma poro da funcionalidade do Lexi est disponvel atravs da representao WYSIWYG do documento. Voc entra e deleta texto, move o ponto de insero e seleciona blocos de texto apontando, clicando e digitando diretamente no documento. Outra parte da funcionalidade acessada indiretamente atravs das operaes do usurio nos menus pull-down, botes e atalhos de teclado de Lexi. A funcionalidade inclui operaes para: criar um novo documento; abrir, salvar e imprimir um documento existente; cortar texto selecionado do documento e colar o mesmo de volta no documento; mudar a fonte tipogrfica e o estilo de um texto selecionado; mudar a formatao do texto, tal como seu alinhamento e sua justificao; sair da aplicao; e assim por diante.

PADRES DE PROJETO

71

O Lexi fornece diferentes interfaces de usurios para essas operaes. Mas no queremos associar uma operao de usurio com uma interface de usurio em particular, porque podemos vir a desejar mltiplas interfaces de usurio para a mesma operao (por exemplo, voc pode virar a pgina usando tanto um boto de pgina quanto uma operao de menu). Poderemos, tambm querer mudar a interface no futuro. Alm do mais, essas operaes esto implementadas em muitas classes diferentes. Ns, como implementadores, queremos acessar a sua funcionalidade sem criar uma poro de dependncias entre a implementao e as classes de interface do usurio. Caso contrrio, acabaremos com uma implementao fortemente acoplada, mais difcil de compreender, estender e manter. Para complicar ainda mais, queremos que o Lexi suporte as operaes desfazer e refazer 8 para a maior parte da, mas no para toda sua funcionalidade. Especificamente, queremos ser capazes de desfazer operaes de modificao de documentos como deletar, com a qual um usurio pode destruir muitos dados acidentalmente. Mas no devemos tentar desfazer uma operao como salvar um desenho ou sair da aplicao. Essas operaes no devem ter efeito sobre o processo de desfazer. Tambm no queremos um limite arbitrrio sobre o nmero de nveis de operaes de desfazer e refazer. Est claro que o suporte s operaes do usurio permeia a aplicao. O desafio encontrar um mecanismo, simples e extensvel, que satisfaa todas estas necessidades.

Encapsulando uma solicitao


A partir da nossa perspectiva como projetistas, um menu pull-down somente outro tipo de glifo que contm outros glifos. O que distingue menus pull-down de outros glifos que tm descendentes que a maior parte dos glifos dos menus executa alguma ao em resposta a um clique. Vamos assumir que esses glifos que realizam trabalho so instncias de uma subclasse de Glyph, chamada MenuItem e que eles fazem o seu trabalho em resposta a uma solicitao de um cliente.9 Executar a solicitao pode envolver uma operao sobre um objeto, ou muitas operaes sobre muitos objetos, ou algo entre esses extremos. Poderamos definir uma subclasse de MenuItem para cada operao do usurio, e, ento, codificar de forma rgida cada subclasse para executar a solicitao. Mas isso no realmente correto; no necessitamos uma subclasse de MenuItem mais do que necessitaramos de uma subclasse para cada string de texto em um menu pulldown. Alm do mais, essa abordagem acopla a solicitao a uma interface de usurio em particular, tornando difcil atender a solicitao atravs de uma interface de usurio diferente. Para ilustrar isso, suponha que voc poderia avanar para a ltima pgina do documento, tanto por um MenuItem no menu pull-down, quanto apertando um cone de pgina na base da interface do Lexi (o que pode ser mais conveniente para documentos curtos). Se associarmos a solicitao a um MenuItem atravs do uso de herana, ento deveremos fazer o mesmo para o cone de pgina e qualquer outro tipo de widget que possa emitir uma solicitao desse tipo. Isso pode dar origem a um nmero de classes aproximado ao produto do nmero de tipos de widgets pelo nmero de solicitaes.

72

CAPTULO 2 UM ESTUDO DE CASO: PROJETANDO UM EDITOR DE DOCUMENTOS

O que est faltando um mecanismo que nos permita parametrizar itens de menus com a solicitao que devem atender. Deste modo, evitamos uma proliferao de subclasses e permitimos uma maior flexibilidade em tempo de execuo. Poderamos parametrizar MenuItem como uma funo a ser chamada, mas isso no uma soluo completa pelo menos por trs razes: 1. No trata do problema de desfazer/refazer; 2. difcil associar um estado com uma funo. Por exemplo, uma funo que muda a fonte tipogrfica necessita saber qual a fonte; 3. As funes so difceis de estender, e difcil reutilizar partes delas. Essas razes sugerem que deveramos parametrizar MenuItem com um objeto, no como uma funo. Ento poderemos usar a herana para estender e reutilizar a implementao da solicitao. Desta forma, tambm temos um espao para armazenar estados e implementar a funcionalidade de desfazer/refazer. Aqui temos um outro exemplo de encapsulamento do conceito que varia, neste caso, uma solicitao. Encapsularemos cada solicitao em um objeto command.

A classe Command e suas subclasses


Primeiro, definimos uma classe abstrata Command para fornecer uma interface que emita uma solicitao. A interface bsica consiste de uma nica operao abstrata chamada Execute. Subclasses de Command implementam Execute de vrias maneiras para atender diferentes solicitaes. Algumas subclasses podem delegar parte do, ou todo, trabalho para outros objetos. Outras subclasses podero atender completamente solicitao por si mesmas (ver figura 2.11). Contudo, para o solicitante, um objeto Command um objeto Command sendo tratado uniformemente.
Command

Execute()

Pastecommand
Execute() buffer

Fontcommand
Execute() newFont

Savecommand
Execute()

save

Quitcommand
Execute()

paste buffer into document

make selected text appear in newFonte

pop up a dialog box that lets the user name the document, and then save the document under that name

if (document is modified) { save>Execute() } quit the application

Figura 2.11 Hierarquia parcial da classe Command.

Agora MenuItem pode armazenar um objeto Command que encapsula uma solicitao (Figura 2.12). Damos a cada objeto de um item de menu uma instncia da subclasse de Command que adequada para aquele item, da mesma forma como

PADRES DE PROJETO

73

Glyph

Menultem Clicked()

command

Command

Execute()

command>Execute();

Figura 2.12 Relacionamento MenuItem-Command.

especificamos o texto que deve aparecer no item do menu. Sempre que o usurio escolhe um determinado item de menu, o MenuItem simplesmente chama Execute no seu objeto Command para executar a solicitao. Note que botes e outros widgets podem usar commands da mesma maneira que itens de menu o fazem.

Desfazendo aes
Desfazer/refazer (aes) uma capacidade importante em aplicaes interativas. Para os comandos desfazer e refazer, acrescentaremos uma operao Unexecute interface de Command. Unexecute reverte os efeitos de uma operao Execute precedente, usando para isso todas as informaes que tenham sido armazenadas por Execute. Por exemplo: no caso de um FontCommand, a operao Execute armazenar a extenso de texto afetada pela mudana da fonte tipogrfica junto com as fontes originais. A operao Unexecute de FontCommand ir restaurar a extenso de texto mudada para suas fontes originais. Algumas vezes, a capacidade de desfazer aes deve ser determinada em tempo de execuo. Uma solicitao para mudar a fonte de uma seleo no faz nada se o texto j aparece naquela fonte. Suponha que o usurio selecione algum texto e, ento, solicite uma mudana espria de fonte. Qual seria o resultado de uma solicitao para desfazer subseqente? Deveria uma mudana sem sentido fazer com que a solicitao de refazer faa algo igualmente sem sentido? Provavelmente no. Se o usurio repete a mudana espria de fonte vrias vezes, no deveria ter que executar exatamente o mesmo nmero de operaes desfazer para voltar para a ltima operao com sentido. Se o efeito lquido da execuo de um comando resulta em nada, ento no h necessidade para uma correspondente solicitao de desfazer. Assim, para determinar se um comando pode ser desfeito, acrescentamos uma operao abstrata, Reversible, interface de Command. Reversible retorna um valor boleano. Subclasses podem redefinir essa operao para retornar true ou false, com base em critrios de tempo de execuo.

Histrico dos comandos


O passo final para suportar um nvel arbitrrio de operaes de desfazer e refazer definido em uma histria de comandos, ou uma lista de comandos que foram executados (ou desfeitos). Conceitualmente, a histria dos comandos se parece com isto:

74

CAPTULO 2 UM ESTUDO DE CASO: PROJETANDO UM EDITOR DE DOCUMENTOS

comandos passados presente

Cada crculo representa um objeto Command. Neste caso, o usurio emitiu quatro comandos. O comando mais esquerda foi emitido primeiro, seguido pelo segundo mais esquerda, e assim por diante, at o comando emitido mais recentemente, que est mais direita. A linha marcada (presente) mantm a informao do comando mais recentemente executado (e desfeito). Para desfazer o ltimo comando, ns simplesmente chamamos Unexecute no comando mais recente.

Unexecute() presente

Aps desfazer o comando, ns movemos a linha presente um comando para a esquerda. Se o usurio escolhe desfazer novamente, o prximo comando mais recentemente emitido ser desfeito da mesma forma, e ns ficaremos no estado ilustrado abaixo.

passado

futuro

presente

Voc pode ver que pela simples repetio desse procedimento, obtivemos mltiplos nveis de desfazer. O nmero de nveis limitado somente pelo comprimento da histria de comandos. Para refazer um comando que acabou de ser desfeito, fazemos a mesma coisa ao contrrio. Comandos direita da linha presente so comandos que podem ser refeitos no futuro. Para refazer o ltimo comando desfeito, chamamos Execute no comando direita da linha do presente:

Execute() presente

Agora, ento, avanamos linha do presente, de modo que uma ao de desfazer subsequente chamar desfazer no comando seguinte no futuro.

PADRES DE PROJETO

75

passado

futuro

presente

Naturalmente, se a operao subseqente no uma outra operao de refazer, mas sim de desfazer, ento o comando esquerda da linha do presente ser desfeito. Assim, o usurio pode efetivamente ir para frente e para trs no tempo, conforme necessrio, para recuperar-se de erros.

O padro Command
Os comandos do Lexi so uma aplicao do padro Command (222), o qual descreve como encapsular uma solicitao. O padro Command prescreve uma interface uniforme para emisso de solicitaes que permite que voc configure clientes para tratar de diferentes solicitaes. A interface protege os clientes da implementao da solicitao. Um comando pode delegar toda, parte, ou nada da implementao de uma solicitao para outros objetos. Isso perfeito para aplicaes como Lexi, que devem fornecer acesso centralizado a funcionalidades espalhadas por toda a aplicao. O padro tambm discute mecanismos de desfazer e refazer, construdos sobre a interface bsica de Command.

2.8 Verificao ortogrfica e hifenizao


O ltimo problema de projeto envolve a anlise do texto, especificamente a verificao de erros de ortografia e a introduo de pontos de hifenizao onde necessrios, para uma boa formatao. Aqui, as restries so similares quelas que tnhamos para o problema de projeto da formatao na seo 2.3. Da mesma forma que o caso das estratgias para quebras de linhas, h mais de uma maneira de efetuar a verificao ortogrfica e computar os pontos de hifenizao. Assim, aqui tambm queremos suportar mltiplos algoritmos. Um conjunto de algoritmos diversos pode fornecer uma escolha de compromissos de espao/tempo/qualidade. Deveramos, tambm, tornar fcil a adio de novos algoritmos. Tambm queremos evitar implementar essa funcionalidade na estrutura do documento. Agora esse objetivo mais importante do que foi no caso da formatao, porque a verificao ortogrfica e a hifenizao so somente dois de muitos tipos potenciais de anlises que podemos desejar que o Lexi suporte. Inevitavelmente, desejaremos expandir com o tempo as habilidades analticas do Lexi. Poderamos acrescentar busca, contagem de palavras, um recurso de clculo para adicionar valores em tabelas, verificao gramatical, e assim por diante. Porm, no queremos mudar a classe glyph e todas as suas subclasses cada vez que introduzirmos novas funcionalidades dessa natureza. H, na realidade, duas peas neste quebra-cabea: (1) acessar a informao a ser analisada, a qual podemos ter espalhado pelos glifos na estrutura do documento, e (2) efetuar a anlise. Ns examinaremos estas duas peas separadamente.

76

CAPTULO 2 UM ESTUDO DE CASO: PROJETANDO UM EDITOR DE DOCUMENTOS

Acessando informaes espalhadas


Muitos tipos de anlise requerem o exame do texto caractere por caractere. O texto que necessitamos analisar est espalhado atravs de uma estrutura hierrquica de objetos glifo. Para examinar o texto em uma estrutura, ns necessitamos de um mecanismo de acesso que tenha conhecimento sobre as estruturas de dados nas quais os objetos esto armazenados. Alguns glifos podem armazenar seus descendentes em listas ligadas, outros podem usar vetores (arrays), e outros, ainda, podem usar estruturas de dados mais esotricas. Nosso mecanismo de acesso deve ser capaz de tratar todas estas possibilidades. Uma complicao adicional que diferentes tipos de anlises acessam a informao de diferentes maneiras. A maioria das anlises far o percurso do texto do incio ao fim. Porm, algumas fazem o oposto por exemplo, uma busca invertida necessita progredir atravs do texto para trs, ao invs de para frente. A anlise e clculo de expresses algbricas poderiam exigir um percurso a partir do centro. Assim, nosso mecanismo de acesso deve acomodar diferentes estruturas de dados, e tambm devemos poder suportar diferentes tipos de percurso, tais como prefixado, ps-fixado e central.

Encapsulando acessos e percursos


At agora nossa interface de glifo tem usado um ndice do tipo inteiro para permitir aos clientes se referirem a descendentes. Embora isto possa ser razovel para classes glifo que armazenam seus descendentes em um vetor, pode ser ineficiente para glifos que utilizam uma lista ligada. Um importante papel da abstrao glifo ocultar a estrutura de dados na qual os descendentes esto armazenados. Dessa forma, podemos mudar a estrutura de dados que uma classe glifo usa sem afetar outras classes. Portanto, somente o glifo pode saber a estrutura de dados que ele usa. Um corolrio disto que a interface do glifo no deveria ser direcionada para uma ou outra estrutura de dados. Por exemplo, ela no deveria ser mais adequada para vetores do que para listas ligadas, como o caso atualmente. Podemos, ao mesmo tempo, solucionar esse problema e suportar diferentes tipos de percurso. Podemos prover capacidades mltiplas para acesso e percurso diretamente nas classes glifo e fornecer uma maneira de escolher entre elas: talvez fornecendo uma constante de enumerao como parmetro. As classes passariam esse parmetro umas para as outras durante um percurso para assegurar que todas elas esto fazendo o mesmo tipo de percurso. Elas tm que passar umas para as outras quaisquer informaes que elas tenham acumulado durante o percurso. Podemos acrescentar as seguintes operaes abstratas interface de glyph para suportar esta abordagem:

As operaes First, Next e IsDone controlam o percurso. A First inicia o percurso. Ela obtm um tipo de percurso como um parmetro do tipo Traversal (percurso), uma constante enumerada com valores tais como CHILDREN (para somen-

PADRES DE PROJETO

77

te os descendentes imediatos do glifo), PREORDER (para fazer o percurso de toda a estrutura em pr-ordem), POSTORDER e INORDER. Next avana para o prximo glifo no percurso, e IsDone retorna, se o percurso acabou ou no. A GetCurrent substitui a operao Child; ela acessa o glifo atual no percurso. Insert substitui a antiga operao; ela insere o glifo fornecido na posio corrente. Uma anlise usaria o seguinte cdigo C++ para efetuar um percurso em prordem de uma estrutura de glifos com raiz em g:

realiza alguma anlise

Note que retiramos o ndice de tipo inteiro da interface dos glifos. No h mais nada que direcione a interface para um tipo de coleo ou para outro. Poupamos, tambm, aos clientes de terem que implementar, eles mesmos, tipos comuns de percursos. Mas esta abordagem ainda tem problemas. Pelo menos em um aspecto: ela no pode suportar novos percursos sem estender o conjunto de valores enumerados ou acrescentar novas operaes. Digamos que queremos ter uma variao de percurso em pr-ordem que automaticamente ignora glifos que no sejam texto. Nesse caso teramos que mudar a enumerao de Traversal , para incluir algo como TEXTUAL_PREORDER. Gostaramos de evitar ter que mudar declaraes existentes. Colocar o mecanismo de percurso inteiramente na hierarquia de classes de Glyph torna difcil a modificao ou a extenso, sem a mudana de muitas classes. tambm difcil de reutilizar o mecanismo para fazer o percurso de outros tipos de estruturas de objeto. Alm disso, no podemos ter mais do que um percurso em andamento em uma estrutura. Novamente, a melhor soluo encapsular o conceito que varia, neste caso, os mecanismos de acesso e percurso. Podemos introduzir uma classe de objetos chamada iterators, cuja nica finalidade definir diferentes conjuntos destes mecanismos. Podemos usar a herana para permitir-nos acessar diferentes estruturas de dados de maneira uniforme, bem como suportar novos tipos de percursos. E no teremos que mudar interfaces de glifos ou afetar implementaes existentes dos mesmos para faz-lo.

Classes e subclasses de Iterator


Usaremos uma classe abstrata chamada Iterator para definir uma interface geral para acesso e percurso. Subclasses concretas como ArrayIterator e ListIterator implementam a interface para fornecer acesso para vetores (arrays) e listas, enquanto que PreorderIterator, PostorderIterator, e outras semelhantes, implementam diferentes percursos sobre estruturas especficas. Cada subclasse de Iterator tem uma referncia para a estrutura na qual ela faz o percurso. As instncias de subclasses so inicializadas com esta referncia quando elas so criadas. A figura 2.13 ilustra a classe Iterator juntamente com vrias subclasses. Note que acrescentamos uma operao abstrata CreateIterator interface da classe Glyph para suportar iteradores.

78

CAPTULO 2 UM ESTUDO DE CASO: PROJETANDO UM EDITOR DE DOCUMENTOS

Iterator

First() Next() IsDone() Currentltem()

iterators

PreorderIterator First() Next() IsDone() Currentltem() root

ArrayIterator First() Next() IsDone() Currentltem() currentltem

ListIterator First() Next() IsDone() Currentltem()

NullIterator First() Next() IsDone() Currentltem()

return true

Glyph

... CreatIterator()
return new NullIterator

Figura 2.13 A classe Iterator e suas subclasses.

A interface de Iterator fornece as operaes First, Next e IsDone para controlar o percurso. A classe ListIterator implementa a First para apontar para o primeiro elemento na lista, e a Next avana o iterador para o prximo item da lista. A IsDone retorna, se o apontador da lista aponta, ou no, para alm do ltimo elemento na mesma. A CurrentItem desreferencia o Iterator para retornar o glifo para o qual ele aponta. Uma classe ArrayIterator faria coisas semelhantes, mas sobre um vetor (array) de glifos. Agora podemos acessar os descendentes de uma estrutura de glifos sem saber sua representao:

CreateIterator retorna por omisso uma instncia de NullIterator. Um NullIterator um iterador degenerado para glifos que no tm descendentes, isto , glifosfolha. A operao IsDone de NullIterator sempre retorna o valor verdadeiro (true). Uma subclasse de glifo que no tem descendentes substituir CreateIterator para retornar uma instncia de uma subclasse diferente de Iterator. Qual a subclasse depende da estrutura que armazena os descendentes. Se a subclasse Linha (Row) de Glyph armazena seus descendentes em uma lista _children, ento sua operao CreateIterator iria se parecer com este cdigo:

PADRES DE PROJETO

79

Iteradores para percursos prefixados e centrais implementam seus percursos em termos de iteradores especficos para o glifo. Os iteradores para estes percursos so fornecidos pelo glifo-raiz da estrutura na qual eles fazem o percurso. Eles chamam CreateIterator nos glifos da estrutura, recorrendo a uma pilha para armazenar (desta forma, acompanhando para usar no momento apropriado) os iteradores resultantes. Por exemplo, a classe PreorderIterator obtm o iterador do glifo-raiz, inicializa-o para apontar para o seu primeiro elemento, e ento, coloca-o no topo da pilha:

A classe CurrentItem simplesmente chamaria CurrentItem no iterator que est no topo da pilha:

A operao Next obtm o iterador do topo da pilha e pede ao seu item corrente para criar um iterador, em uma tentativa de descer pela estrutura de glifo o mais longe possvel (afinal de contas, trata-se de um percurso em pr-ordem). Next aponta o iterador para o primeiro item no percurso e coloca-o no topo da pilha. Ento Next testa o ltimo iterador; se a operao isDone retorna verdadeiro (true), ento terminamos de percorrer a subrvore corrente (ou folha) no percurso. Nesse caso, Next remove o iterador do topo da pilha e repete esse processo at que encontre o prximo percurso incompleto, se houver; se no houver, ento acabamos de percorrer a estrutura toda.

80

CAPTULO 2 UM ESTUDO DE CASO: PROJETANDO UM EDITOR DE DOCUMENTOS

Note como a hierarquia da classe Iterator permite-nos acrescentar novos tipos de percursos sem modificar as classes glifo simplesmente acrescentamos subclasses a Iterator e acrescentamos um novo percurso do modo como fizemos com PreorderIterator. As subclasses de Glyph usam a mesma interface para dar para os clientes acesso aos seus descendentes, sem revelar a estrutura de dados subjacente que usam para armazen-los. Porque os iteradores armazenam sua prpria cpia do estado de um percurso, ns podemos efetuar mltiplos percursos simultaneamente, mesmo sobre a mesma estrutura. Embora nossos percursos tenham sido feitos sobre estruturas de glifos neste exemplo, no h razo pela qual no possamos parametrizar uma classe como PreorderIterator. Ns usaramos templates para fazer isso em C++. Dessa forma, podemos reutilizar toda a maquinaria em PreorderIterator para percorrer outras estruturas.

O padro Iterator
O padro Iterator (244) captura essas tcnicas para o suporte ao acesso e percurso de estruturas de objetos. aplicvel no somente a estruturas compostas, mas tambm a colees. Ele abstrai o algoritmo de percurso e isola os clientes da estrutura interna dos objetos que eles percorrem. O padro Iterator ilustra, uma vez mais, como o encapsulamento do conceito que varia ajuda-nos a obter flexibilidade e reutilizabilidade. Mesmo assim, o problema da iterao surpreendentemente profundo, e o padro Iterator cobre muito mais nuances e solues de compromissos do que consideramos aqui.

Percursos versus aes de percurso


Agora que temos uma maneira de percorrer a estrutura de glifo, necessitamos verificar a ortografia e fazer a hifenizao. As duas anlises envolvem acumulao de informaes durante o percurso. Em primeiro lugar, temos que decidir onde colocar a responsabilidade da anlise. Poderamos coloc-la nas classes Iterator, desta forma tornando a anlise uma parte integral do percurso. Mas, ganhamos mais flexibilidade e potencialidade de reutilizao se distinguirmos entre o percurso e as aes executadas durante o mesmo. Isso se deve ao fato de que anlises diferentes freqentemente requerem o mesmo tipo de percurso. Da podermos reutilizar o mesmo conjunto de iteradores para diferentes anlises. Por exemplo, o percurso em pr-ordem comum a muitas anlises, incluindo a verificao ortogrfica, a hifenizao, uma pesquisa para frente a partir de um ponto (por exemplo, de uma palavra) e a contagem de palavras. Assim, a anlise e o percurso deveriam ser separados. Onde mais poderamos colocar a responsabilidade da anlise? Sabemos que existem muitos tipos de anlise que poderamos vir a querer fazer. Cada anlise faria coisas diferentes, em diferentes pontos do percurso. Alguns glifos so mais significativos do que outros, dependendo do tipo de anlise. Se estivermos verificando a ortografia ou fazendo hifenizao, desejaremos considerar glifos de caracteres e no glifos como linhas e imagens em bitmaps. Se estivermos fazendo separaes de cores, desejaremos considerar os glifos visveis, e no os invisveis. Inevitavelmente, anlises diferentes analisaro glifos diferentes. Uma determinada anlise deve ser capaz de distinguir diferentes tipos de glifos. Uma abordagem bvia seria colocar a capacidade analtica nas prprias classes glifo. Para cada anlise, poderamos acrescentar uma ou mais operaes abstratas classe

PADRES DE PROJETO

81

Glyph e ter subclasses, implementando-as de acordo com o papel que elas exercem na anlise. Mas o problema com esta abordagem que teremos que mudar cada classe de glifo sempre que acrescentarmos um novo tipo de anlise. Podemos simplificar esse problema em alguns casos: se somente poucas classes participam da anlise, ou se a maioria das classes faz anlise do mesmo modo, ento podemos fazer uma implementao por omisso para a operao abstrata na classe Glyph. Esta operao cobriria o caso comum. Assim, limitaramos as mudanas somente classe Glyph e quelas subclasses que se desviam da norma. Mas ainda que uma implementao por omisso reduza o nmero de mudanas, permanece um problema incidioso: a interface de Glyph se expande com a adio de cada nova capacidade analtica. Com o passar do tempo, as operaes analticas comearo a obscurecer a interface bsica de Glyph. Acabar se tornando difcil ver que a finalidade principal de um glifo definir e estruturar objetos que tenham aparncia e forma essa interface ficar perdida no meio do rudo.

Encapsulando a anlise
Segundo todas as indicaes, necessitaremos encapsular a anlise em um objeto separado, de maneira bastante semelhante ao que j fizemos muitas vezes anteriormente. Poderamos por a maquinaria para uma determinada anlise na sua prpria classe. Poderamos usar uma instncia desta classe em conjunto com um iterador apropriado. O iterador carregaria a instncia de cada glifo da estrutura. Ento, o objeto da anlise poderia executar uma parte desta em cada ponto do percurso. O analisador (analyzer) acumula a informao de interesse (neste caso, os caracteres) medida que o percurso avana:

iterador 1 analisador "a_"

"a"

""

A questo com essa abordagem como o objeto-anlise distingue diferentes tipos de glifos sem recorrer a testes de tipo ou downcasts (C++). No queremos que uma classe SpellingChecker inclua (pseudo) cdigo como

82

CAPTULO 2 UM ESTUDO DE CASO: PROJETANDO UM EDITOR DE DOCUMENTOS

Esse cdigo muito feio. Ele depende de recursos bastante esotricos, como casts que garantam segurana quanto ao tipo (type-safe casts). Ele tambm difcil de entender. Teremos que lembrar de mudar o corpo dessa funo sempre que mudarmos a hierarquia de classes de Glyph. De fato, esse o tipo de cdigo que as linguagens orientadas a objeto pretendiam eliminar. Queremos evitar tal soluo pela fora bruta, mas como? Vamos considerar o que acontece quando acrescentamos as seguintes operaes abstratas classe Glyph:

Definimos CheckMe em toda a subclasse de Glyph, como segue:

em que GlyphSubclass seria substituda pelo nome da subclasse do glifo. Note que quando CheckMe chamada, a subclasse de Glyph conhecida afinal de contas, estamos em uma de suas operaes. Por sua vez, a interface da classe SpellingChecker inclui uma operao como CheckGlyphSubclass para cada subclasse de Glyph10:

PADRES DE PROJETO

83

A operao de verificao de SpellingChecker para glifos Character deve ser parecida com isto:

Note que definimos uma operao especial GetCharCode somente na classe Character. O verificador ortogrfico pode lidar com operaes especficas de subclasses sem recorrer a testes de tipo, ou casts ele permite-nos tratar objetos de maneira especial. CheckCharacter acumula caracteres alfabticos no buffer _currentWord. Quando encontra um caractere no-alfabtico, como um underscore, ele usa a operao IsMisspelled para verificar a grafia da palavra em _currentWord.11 Se a palavra est grafada de forma incorreta, ento CheckCharacter acrescenta a palavra lista de palavras grafadas incorretamente. Ele deve limpar ento o buffer _currentWord para prepar-lo para a prxima palavra. Quando o percurso terminou, voc pode recuperar a lista de palavras grafadas incorretamente com a operao GetMisspellings. Agora podemos percorrer a estrutura de glifo, chamando CheckMe para cada glifo com o verificador ortogrfico como argumento. Isso efetivamente identifica cada glifo para o SpellingChecker e alerta o verificador para efetuar o prximo incremento na verificao ortogrfica.

O seguinte diagrama de interao ilustra como glifos Character e o objeto


SpellingChecker trabalham em conjunto:

84

CAPTULO 2 UM ESTUDO DE CASO: PROJETANDO UM EDITOR DE DOCUMENTOS


aCharacter ("a") anotherCharacter ("_") aSpellingChecker

CheckMe(aSpellingChecker)

CheckCharacter(this) GetCharacter()

CheckMe(aSpellingChecker)

CheckCharacter(this) GetCharacter()

Verifica palavras completas

Essa abordagem funciona para encontrar erros de ortografia, mas como ela nos ajuda a suportar mltiplos tipos de anlise? Parece que teremos que acrescentar uma operao como CheckMe (SpellingChecker&) a Glyph e suas subclasses sempre que acrescentarmos um novo tipo de anlise. Isso verdade se insistirmos em ter uma classe independente para cada anlise. Mas no h razo pela qual no possamos dar a todas classes de anlise a mesma interface. Essa soluo nos permite us-las polimorficamente. Isto significa que podemos substituir operaes especficas da anlise, tais como CheckMe (SpellingChecker&), por uma operao independente da anlise, uma operao que aceita um parmetro mais geral.

A classe Visitor e suas subclasses


Usaremos o termo visitor (visitante), para referirmo-nos genericamente s classes de objetos que visitam outros objetos durante um percurso e fazem algo apropriado.12 Neste caso podemos definir uma classe Visitor que define uma interface abstrata para visitar glifos em uma estrutura.

assim por diante

As subclasses concretas de Visitor executam diferentes anlises. Por exemplo, poderamos ter uma subclasse SpellingCheckingVisitor para verificao ortogrfica, e uma subclasse HyphenationVisitor para hifenizao. SpellingCheckingVisitor seria implementada exatamente como ns implementamos SpellingChecker anteriormente, exceto que os nomes das operaes refletiriam a interface mais geral de Visitor. Por exemplo, CheckCharacter seria chamada de VisitCharacter. Uma vez que CheckMe no apropriado para visitantes que no verificam nada, ns lhe daremos um nome mais geral: Accept. Seu argumento tambm deveria mudar

PADRES DE PROJETO

85

para aceitar um Visitor&, refletindo o fato de que ele pode aceitar qualquer visitante. Agora, acrescentar uma nova anlise exige somente definir uma nova subclasse de Visitor no temos que mexer em nenhuma das classes glifo. Suportaremos todas as anlises futuras pelo acrscimo desta nova operao a Glyph e suas subclasses. J vimos como funciona a verificao ortogrfica. Usamos uma abordagem similar em HyphenationVisitor para acumular texto. Mas, uma vez que a operao VisitCharacter de HyphenationVisitor montou uma palavra inteira, ela funciona de um modo um pouco diferente. Ao invs de examinar a palavra para ver se h erros de ortografia, aplicando um algoritmo de hifenizao para determinar pontos de hifenizao, se existirem. Ento, a cada ponto de hifenizao insere um glifo discricionrio na composio. Glifos discricionrios so instncias de Discretionary, uma sublcasse de Glyph. Um glifo discricionrio tem uma entre duas possveis aparncias, dependendo se ou no o ltimo carter de uma linha. Se ele for o ltimo carter, ento o discricionrio se parece com um hfen; se ele no estiver no final de uma linha, ento o discricionrio no tem qualquer aparncia. O discricionrio verifica o seu pai (um objeto Linha) para ver se o ltimo descendente. O discricionrio faz essa verificao sempre que chamado para desenhar a si prprio, ou calcular os seus limites (fronteiras). A estratgia de formatao trata discricionrios da mesma maneira que espaos em branco: tornando-os candidatos a terminar uma linha. O diagrama seguinte mostra como um discricionrio embutido pode se apresentar.

"a"

"I"

discricionrio

"I"

"o" "y"

aluminum alloy

or

aluminum alloy

O padro Visitor
O que descrevemos aqui uma aplicao do padro Visitor (305). A classe Visitor e suas subclasses, descritas anteriormente, so participantes-chave no padro. O padro Visitor captura a tcnica que usamos para permitir um nmero qualquer de anlises de estruturas de glifos sem ter que mudar as classes glifos. Uma outra caracterstica agradvel dos visitantes que podem ser aplicados no apenas a estruturas compostas, como nossas estruturas de glifos, mas a qualquer estrutura de objeto. Isso inclui conjuntos, listas e at mesmo grafos direcionados acclicos. Alm disso, as classes que um visitante pode visitar no necessitam estar relacionadas umas com as outras, atravs de uma classe-me em comum. Isso significa que visitantes podem trabalhar atravs de diferentes hierarquias de classes. Uma importante pergunta a ser feita antes de aplicar o padro Visitor : quais hierarquias de classes mudam mais freqentemente? O padro mais adequado quando voc quer ser capaz de fazer uma variedade de coisas diferentes com objetos

86

CAPTULO 2 UM ESTUDO DE CASO: PROJETANDO UM EDITOR DE DOCUMENTOS

que tenham uma estrutura de classe estvel. A adio de um novo tipo de visitante no requer mudanas a essa estrutura de classe, o que especialmente importante quando a estrutura de classe grande. Mas sempre que voc acrescente uma subclasse estrutura ter tambm que atualizar todas as suas interfaces visitor para incluir uma operao Visit... quela subclasse. No nosso exemplo, isso significa acrescentar uma subclasse de Glyph chamada Foo, o que exigir mudar Visitor e todas as suas subclasses para incluir uma operao VisitFoo. Mas, determinadas as nossas restries de projeto, muito mais provvel que tenhamos que acrescentar um novo tipo de anlise ao Lexi do que um novo tipo de Glyph. Assim, o padro Visitor bem adequado para as nossas necessidades.

2.9 Resumo
Ns aplicamos oito padres diferentes ao projeto do Lexi: 1. 2. 3. 4. 5. 6. 7. 8. Composite (160) para representar a estrutura fsica do documento. Strategy (292) para permitir diferentes algoritmos de formatao. Decorator (170) para adornar a interface do usurio. Abstract Factory (95) para suportar mltiplos padres de interao. Bridge (151) para permitir mltiplas plataformas de sistemas de janela. Command (222) para desfazer operaes do usurio. Iterator (244) para acessar e percorrer estruturas de objetos. Visitor (305) para permitir um nmero qualquer de capacidades analticas sem complicar a implementao da estrutura do documento.

Nenhum desses tpicos do projeto est limitado a aplicaes de edio de documentos como Lexi. De fato, diversas aplicaes no-triviais tero a oportunidade de usar muitos desses padres, talvez, porm, para fazerem coisas diferentes. Uma aplicao de anlise financeira poderia usar Composite para definir portflios de investimento compostos de subportflios e contas de diferentes tipos. Um compilador poderia usar o padro Strategy para permitir diferentes esquemas de alocao de registradores para diferentes mquinas-alvo. Aplicaes como uma interface grfica para usurio provavelmente aplicaro pelo menos Decorator e Command, tal como fizemos aqui. Embora tenhamos coberto diversos problemas importantes no projeto do Lexi, existem muitos outros que no discutimos. Porm, este livro descreve mais do que apenas os oito padres que ns usamos aqui. Assim, medida que voc estudar os padres restantes, pense como poderia usar cada um deles no projeto do Lexi. Ou, melhor ainda, pense como us-los nos seus prprios projetos!

Notas
1

O projeto do Lexi est baseado no Doc, uma aplicao de edio de textos desenvolvida por Calder [CL92] Autores freqentemente vem um documento em termos, tambm, da sua estrutura lgica, isto , em termos de sentenas, pargrafos, sees, subsees e captulos. Para manter este exemplo simples, nossa representao interna no armazenar a informao sobre a estrutura lgica explicitamente. Mas a soluo de projeto que descrevemos funciona igualmente bem para representao de tal informao.

PADRES DE PROJETO
3

87

8 9

10

11

12

Calder foi o primeiro a usar o termo glyph neste contexto [CL90]. Muitos editores de documentos contemporneos no usam objetos para todos os caracteres, possivelmente por razes de eficincia. Calder demonstrou que essa abordagem vivel em sua tese [Cal 93]. Nossos glyphs so menos sofisticados que os seus, no sentido de que iremos nos restringir a hierarquias estritas ou puras por razes de simplicidade. Os glyphs, ou glifos, de Calder podem ser compartilhados para reduzir os custos de armazenamento, desta maneira formando estruturas de grficos direcionados acclicos. Ns podemos aplicar o padro Flyweight (187) para obter o mesmo efeito, mas deixaremos isso como um exerccio para o leitor. A interface que descrevemos aqui intencionalmente mnima para manter a discusso simples. Uma interface completa incluiria operaes para administrar atributos grficos, tais como cor, fontes tipogrficas e transformaes de coordenadas, mais operaes para uma administrao mais sofisticada dos objetos-filho. Um ndice do tipo inteiro provavelmente no a melhor maneira de especificar os descendentes de um glifo, dependendo da estrutura de dados utilizada. Se ele armazena seus descendentes numa lista ligada, ento um apontador para a lista seria mais eficiente. Veremos uma melhor soluo para o problema de indexao na Seo 2.8, quando discutiremos a anlise do documento. O usurio ter at mesmo mais a dizer sobre a estrutura lgica do documento as sentenas, pargrafos, sees, captulo e assim por diante. Em comparao, a estrutura fsica menos interessante. Muitas pessoas no se preocupam com onde ocorrem as quebras de linha em um pargrafo, desde que o pargrafo seja formatado adequadamente. O mesmo vale para formatao de colunas e pginas. Assim, os usurios acabam especificando somente restries de alto nvel sobre a estrutura fsica, deixando para o Lexi o trabalho duro de satisfaz-las. O compositor deve obter os cdigos de caracteres de glifos Character, de forma a poder computar as quebras de linha. Na Seo 2.8 veremos como obter essa informao polimorficamente, sem acrescentar uma operao especfica para caracteres interface de Glyph. Isto , refazer uma operao que acabou de ser desfeita. Conceitualmente, o cliente usurio do Lexi, mas, na realidade, outro objeto (tal como um despachador de eventos) que administra as entradas do usurio. Poderamos usar o overloading de funes para dar a cada uma dessas funes-membro o mesmo nome, uma vez que os seus parmetros j as difereciam. Aqui demos a elas nomes diferentes para enfatizar suas diferenas, especialmente quando elas so chamadas. IsMisspelled implementa o algoritmo de verificao ortogrfica, o qual no detalharemos aqui porque o tornamos independente do projeto do Lexi. Podemos suportar diferentes algoritmos atravs da introduo de subclasses SpellingChecker; alternativamente, podemos aplicar o padro Strategy (292) (como fizemos para a formatao, na Seo 2.3) para suportar diferentes algoritmos de verificao ortogrfica. Visitar um termo ligeiramente mais genrico para analisar. Ele antecipa a terminologia que usaremos no padro de projeto que vamos introduzir agora.

Catlogo de padres de projeto

3
Padres de criao
Os padres de criao abstraem o processo de instanciao. Eles ajudam a tornar um sistema independente de como seus objetos so criados, compostos e representados. Um padro de criao de classe usa a herana para variar a classe que instanciada, enquanto que um um padro de criao de objeto delegar a instanciao para outro objeto. Os padres de criao se tornam importantes medida que os sistemas evoluem no sentido de depender mais da composio de objetos do que da herana de classes. Quando isso acontece, a nfase se desloca da codificao rgida de um conjunto fixo de comportamentos para a definio de um conjunto menor de comportamentos fundamentais, os quais podem ser compostos em qualquer nmero para definir comportamentos mais complexos. Assim, criar objetos com comportamentos particulares exige mais do que simplesmente instanciar uma classe. H dois temas recorrentes nesses padres. Primeiro, todos encapsulam conhecimento sobre quais classes concretas so usadas pelo sistema. Segundo, ocultam o modo como as instncias destas classes so criadas e compostas. Tudo o que o sistema sabe no geral sobre os objetos que suas classes so definidas por classes abstratas. Conseqentemente, os padres de criao do muita flexibilidade ao que, como e quando criado e a quem cria. Eles permitem configurar um sistema com objetosproduto que variam amplamente em estrutura e funcionalidade. A configurao pode ser esttica (isto , especificada em tempo de compilao) ou dinmica (em tempo de execuo). Algumas vezes, os padres de criao competem entre si. Por exemplo, h casos em que tanto Prototype (121) como Abstract Factory (95) podem ser usados proveitosamente. Em outras ocasies, eles so complementares: Builder (104) pode usar um dos outros padres para implementar quais componentes so construdos. Prototype (121) pode usar Singleton (130) na sua implementao. Uma vez que os padres de criao so intimamente relacionados, estudaremos os cinco em conjunto para destacar suas semelhanas e diferenas. Tambm usare-

92

CAPTULO 3 PADRES DE CRIAO

mos um exemplo comum a construo de um labirinto (maze, em ingls) para um jogo de computador para ilustrar suas implementaes. O labirinto e o jogo variaro ligeiramente de padro para padro. Algumas vezes, o jogo ser simplesmente encontrar a sada do labirinto; nesse caso, o jogador provavelmente ter apenas uma viso local do labirinto. Algumas vezes, labirintos contm problemas para resolver e perigos para superar, e estes jogos podem fornecer um mapa (map) da parte do labirinto que no foi explorada. Ignoraremos muitos detalhes do que pode estar num labirinto e se um jogo de labirinto tem um nico ou vrios jogadores. Em vez disso, focalizaremos apenas em como os labirintos so criados. Definimos um labirinto como um conjunto de salas (rooms). Uma sala conhece os seus vizinhos; possveis vizinhos so uma outra sala, uma parede (wall), ou uma porta (door) para uma outra sala. As classes Room, Door e Wall definem os componentes do labirinto usados em todos os nossos exemplos. Definimos somente partes dessas classes que so importantes para criar um labirinto. Ignoraremos jogadores, operaes para exibir informaes e vagar pelo labirinto e outras funcionalidades importantes que no so relevantes para a construo do labirinto. O diagrama a seguir mostra os relacionamentos entre estas classes:
MapSite Enter()

sides

Room Enter() SetSide() GetSide roomNumber

Wall Enter()

Door Enter() isOpen()

Maze AddRoom() RomNo()

rooms

Cada sala tem quatro lados (sides), ns usamos uma enumerao Direction nas implementaes em C++ para especificar os lados norte, sul, leste e oeste de uma sala:

As implementaes em Smalltalk usam smbolos correspondentes para representar estas direes. A classe MapSite (lugar no mapa) a classe abstrata comum para todos os componentes de um labirinto. Para simplificar o exemplo, MapSite define somente uma operao, Enter. O seu significado depende do local em que voc est entrando. Se voc entra numa sala, ento sua localizao muda. Se tenta entrar em uma porta, ento acontece uma de duas coisas: se a porta estiver aberta, voc vai para a prxima sala; se a porta estiver fechada, machuca o seu nariz.

Enter fornece uma base simples para operaes mais sofisticadas do jogo. Por exemplo, se voc est numa sala e diz: V para leste, o jogo simplesmente

PADRES DE PROJETO

93

determina qual MapSite est imediatamente ao leste e ento chama Enter para entrar neste local. A operao Enter especfica da subclasse determinar se a sua localizao mudou ou se voc machucou o nariz. Num jogo real, Enter poderia aceitar como argumento o objeto que representa um jogador que est se deslocando pelo labirinto. Room a subclasse concreta de MapSite que define os relacionamentos-chave entre os componentes do labirinto. Ela mantm referncias para outros objetos de MapSite e armazena o nmero de uma sala. Esses nmeros identificaro as salas no labirinto.

As seguintes classes representam a parede ou a porta que existe em cada lado de uma sala.

Necessitamos saber mais do que apenas as partes de um labirinto. Tambm definiremos uma classe Maze para representar uma coleo de salas. Maze tambm pode achar uma sala especfica, dado um nmero de sala, usando sua operao RoomNo.

94

CAPTULO 3 PADRES DE CRIAO

RoomNo poderia fazer uma inspeo (look-up) usando uma busca linear, uma tabela de randomizao (hash table), ou simplesmente um vetor (array). Mas no vamos nos preocupar com tais detalhes aqui. Em vez disso, vamos nos concentrar em como especificar os componentes de um objeto labirinto. Outra classe que definimos a MazeGame, que cria o labirinto. Uma maneira simplista de criar um labirinto atravs de uma srie de operaes que acrescentam componentes a um labirinto e ento os interconectam. Por exemplo, a seguinte funo-membro (C++) criar um labirinto que consiste de duas salas com uma porta entre elas.

Esta funo bem complicada, considerando que tudo o que faz criar um labirinto com duas salas. H maneiras bvias de torn-la mais simples. Por exemplo, o construtor (C++) de Room, poderia iniciar os lados com paredes. Porm, isto s move o cdigo para um outro lugar. O problema real desta funo-membro no o seu tamanho, mas sim sua inflexibilidade. Ela codifica de maneira rgida o layout do labirinto. Mudar o layout significa mudar esta funo-membro, seja redefinindo-a (overriding) o que significa reimplementar tudo ou atravs da mudana de partes do labirinto o que est sujeito a erros e no promove a reutilizao.

PADRES DE PROJETO

95

Os padres de criao mostram como tornar esse projeto mais flexvel e no necessariamente menor. Em particular, eles facilitaro mudar as classes que definem os componentes de um labirinto. Suponha que se quisesse reutilizar o layout de um labirinto existente para um novo jogo contendo labirintos encantados (entre vrias possibilidades imaginveis). O jogo do labirinto encantado tem novos tipos de componentes, tais como DoorNeedingSpell, uma porta que s pode ser fechada e aberta subseqentemente com uma palavra mgica; e EnchantedRoom, uma sala que pode ter itens noconvencionais nela, como chaves ou palavras mgicas. Como pode voc mudar CreateMaze facilmente, de maneira que ele crie labirintos com essas novas classes de objetos? Neste caso, a maior barreira para a mudana est na codificao rgida (hardcoding) das classes que so instanciadas. Os padres de criao fornecem diferentes maneiras para remover referncias explcitas a classes concretas de cdigo necessrio para cri-las: Se CreateMaze chama funes virtuais em vez de chamadas a construtores para criar as salas, as paredes e as portas de que necessita, ento voc pode mudar as classes que so instanciadas criando uma subclasse de MazeGame e redefinindo aquelas funes virtuais. Esta abordagem um exemplo do padro Factory Method (112). Se um objeto passado como um parmetro para o CreateMaze utilizar na criao de salas, paredes e portas, ento voc pode mudar as classes de salas, paredes e portas passando um parmetro diferente. Isso um exemplo do padro Abstract Factory (95). Se passamos um objeto para CreateMaze que pode criar um novo labirinto em sua totalidade, usando operaes para acrescentar salas, portas e paredes ao labirinto que ele constri, ento voc pode usar a herana para mudar partes do labirinto ou a maneira como o mesmo construdo. Isso um exemplo do padro Builder (104). Se CreateMaze parametrizado por vrios objetos-prottipo de sala, porta e parede, os quais copia e acrescenta ao labirinto, ento voc muda a composio do labirinto substituindo esses objetos-prottipo por outros diferentes. Isso um exemplo do padro Prototype (121). O ltimo padro de criao, Singleton (130), pode garantir que haja somente um labirinto por jogo e que todos os objetos do jogo tenham pronto acesso a ele sem recorrer a variveis ou funes globais. O Singleton tambm facilita estender ou substituir o labirinto sem mexer no cdigo existente.

ABSTRACT FACTORY
Inteno

criao de objetos

Fornecer uma interface para criao de famlias de objetos relacionados ou dependentes sem especificar suas classes concretas.

96

CAPTULO 3 PADRES DE CRIAO

Tambm conhecido como


Kit

Motivao
Considere um toolkit para construo de interfaces de usurios que suporte mltiplos estilos de interao (look-and-feel) tais como o Motif e o Presentation Manager. Diferentes estilos de interao definem diferentes apresentaes e comportamento para os widgets de uma interface de usurio, tais como barras de rolamento, janelas e botes. Para ser porttil entre vrios estilos de aparncia, uma aplicao no deve codificar rigidamente seus widgets para um determinado padro. Instanciando classes especficas de estilo de interao para os widgets pela aplicao toda, torna difcil mudar o estilo no futuro. Podemos resolver esse problema definindo uma classe abstrata WidgetFactory que declara uma interface para criao de cada tipo bsico de widget. Existe tambm uma classe abstrata para cada tipo de widget, e subclasses concretas implementam os widgets para interao. A interface de WidgetFactory tem uma operao que retorna um novo objeto widget para cada classe abstrata de widget. Os clientes chamam estas operaes para obter instncias de widget, mas no tm conhecimento das classes concretas que esto usando. Desta forma, os clientes ficam independentes do padro de interao usado no momento.
WidgetFactory Client Window

CreateScrollBar() CreateWindow()

PMWindow

MotifWindow

MotifWidgetFactory CreateScrollBar() CreateWindow()

PMWidgetFactory CreateScrollBar() CreateWindow() ScrollBar

PMScrollBar

MotifScrollBar

Existe uma subclasse concreta de WidgetFactory para cada estilo de interao. Cada subclasse implementa as operaes para criar o widget apropriado para aquele estilo de interao. Por exemplo, a operao CreateScrollBar aplicada MotifWidgetFactory instancia e retorna uma barra de rolamento de acordo com o Motif, enquanto que a correspondente operao aplicada PMWidgetFactory retorna uma barra de rolamento para o Presentation Manager. Os clientes criam widgets exclusivamente atravs da interface de WidgetFactory e no tem conhecimento das classes que implementam os widgets para um padro em particular. Em outras palavras, os clientes tm somente que se comprometer com uma interface definida por uma classe abstrata, no uma determinada classe concreta. Uma

PADRES DE PROJETO

97

WidgetFactory tambm implementa e garante as dependncias entre as classes concretas de widgets. Uma barra de rolamento Motif deveria ser usada com um boto Motif e um editor de textos Motif, e essa restrio garantida automaticamente como conseqncia de usar uma MotifWidgetFactory.

Aplicabilidade
Use o padro Abstract Factory quando: um sistema deve ser independente de como seus produtos so criados, compostos ou representados; um sistema deve ser configurado como um produto de uma famlia de mltiplos produtos; uma famlia de objetos-produto for projetada para ser usada em conjunto, e voc necessita garantir esta restrio; voc quer fornecer uma biblioteca de classes de produtos e quer revelar somente suas interfaces, no suas implementaes.

Estrutura
AbstractFactory Client AbstractProductA

CreateProductA() CreateProductB()

ProductA2

ProductA1

ConcreteFactory1 CreateProductA() CreateProductB()

ConcreteFactory2 CreateProductA() CreateProductB() AbstractProductB

ProductB2

ProductB1

Participantes
AbstractFactory (WidgetFactory) declara uma interface para operaes que criam objetos-produto abstratos. Concrete Factory (MotifWidgetFactory, PMWidgetFactory) implementa as operaes que criam objetos-produto concretos. AbstractProduct (Window, ScrollBar) declara uma interface para um tipo de objeto-produto ConcreteProduct (MotifWindow, MotifScrollBar) define um objeto-produto a ser criado pela correspondente fbrica concreta. implementa a interface de Abstract Product. Client

98

CAPTULO 3 PADRES DE CRIAO

usa somente interfaces declaradas pelas classes Abstract Factory e Abstract Product.

Colaboraes
Normalmente uma nica instncia de uma classe ConcreteFactory criada em tempo de execuo. Essa fbrica concreta cria objetos-produto que tm uma implementao particular. Para criar diferentes objetos-produto, os clientes deveriam usar uma fbrica concreta diferente. AbstractFactory adia a criao dos objetos-produto para as suas subclasses ConcreteFactory.

Conseqncias
O padro Abstract Factory tem os seguintes benefcios e desvantagens: 1. Ele isola as classes concretas. O padro Abstract Factory ajuda a controlar as classes de objetos criadas por uma aplicao. Uma vez que a fbrica encapsula a responsabilidade e o processo de criar objetos-produto, isola os clientes das classes de implementao. Os clientes manipulam as instncias atravs das suas interfaces abstratas. Nomes de classes-produto ficam isolados na implementao da fbrica concreta; eles no aparecem no cdigo do cliente. 2. Ele torna fcil a troca de famlias de produtos. A classe de uma fbrica concreta aparece apenas uma vez numa aplicao isto , quando instanciada. Isso torna fcil mudar a fbrica concreta que uma aplicao usa. Ela pode usar diferentes configuraes de produtos simplesmente trocando a fbrica concreta. Uma vez que a fbrica abstrata cria uma famlia completa de produtos, toda famlia de produtos muda de uma s vez. No nosso exemplo de interface de usurio, podemos mudar de widgets do Motif para widgets do Presentation Manager simplesmente trocando os correspondentes objetosfbrica e recriando a interface. 3. Ela promove a harmonia entre produtos. Quando objetos-produto numa famlia so projetados para trabalharem juntos, importante que uma aplicao use objetos de somente uma famlia de cada vez. AbstractFactory torna fcil assegurar isso. 4. difcil de suportar novos tipos de produtos. Estender fbricas abstratas para produzir novos tipos de Produtos no fcil. Isso se deve ao fato de que a interface de AbstractFactory fixa o conjunto de produtos que podem ser criados. Suportar novos tipos de produto exige estender a interface da fbrica, o que envolve mudar a classe AbstractFactory e todas as suas subclasses. Discutimos uma soluo para este problema na seo de Implementao.

Implementao
A seguir apresentamos algumas tcnicas teis para implementar o padro Abstract Factory. 1. Fbricas como singletons. Uma aplicao necessita somente de uma instncia de uma Concrete Factory por famlia de produto. Assim, ela normalmente melhor implementada como um Singleton (130).

PADRES DE PROJETO

99

2. Criando os produtos. AbstractFactory somente declara uma interface para criao de produtos. Fica a cargo das subclasses de ConcreteProducts cri-los efetivamente. A maneira mais comum de fazer isso definir um mtodo-fbrica (ver Factory Method (112) para cada produto. Uma fbrica concreta especificar seus produtos redefinindo (overriding) o mtodo-fbrica para cada um. Embora esta implementao seja simples, exige uma nova subclasse de uma fbrica concreta para cada famlia de produtos, ainda que as famlias de produto tenham somente diferenas pequenas. Se muitas famlias de produtos so possveis, a fbrica concreta pode ser implementada usando o padro Prototype (121). A fbrica concreta iniciada com uma instncia prototpica de cada produto da famlia e cria um novo produto clonando o seu prottipo. A abordagem baseada no Prototype elimina a necessidade de uma nova classe de fbrica concreta para cada nova famlia de produtos. Aqui apresentamos uma maneira de implementar em Smalltalk uma fbrica baseada no Prototype. A fbrica concreta armazena os prottipos a serem clonados em um dicionrio chamado partCatalog. O mtodo make: l o prottipo e faz sua clonagem:

A fbrica concreta tem um mtodo para adicionar partes ao catlogo.

Prottipos so acrescentados fbrica, identificando-os com um smbolo:

Uma variao da abordagem baseada no Prototype possvel em linguagens que tratam classes como objetos de primeira classe (por exemplo, Smalltalk e Objective C). Voc pode pensar sobre uma classe nessas linguagens como sendo uma fbrica degenerada que cria somente um tipo de produto. Pode armazenar classes dentro de uma fbrica concreta que cria os vrios produtos concretos em variveis, de maneira bastante semelhante a prottipos. Essas classes criam novas instncias em nome da fbrica concreta. Define-se uma nova fbrica iniciando uma fbrica concreta com classes de produtos ao invs de usar subclasses. Esta abordagem aproveita as caractersticas da linguagem, ao passo que a abordagem pura baseada em prottipos independente de linguagens. Como a fbrica baseada em prottipos em Smalltalk que acabamos de discutir, a verso baseada em classes ter uma nica varivel de instncia partCatalog, que um dicionrio cuja chave o nome da parte. Em vez de armazenar prottipos para serem clonados, partCatalog armazena as classes dos produtos. O mtodo make: agora se parece com o seguinte:

3. Definindo fbricas extensveis. AbstractFactory normalmente define uma operao diferente para cada tipo de produto que pode produzir. Os tipos de produtos esto codificados nas assinaturas das operaes. O acrscimo de um novo tipo de produto exige a mudana da interface de AbstractFactory e de todas as classes que dependem dela.

100

CAPTULO 3 PADRES DE CRIAO

Um projeto mais flexvel, mas menos seguro, acrescentar um parmetro s operaes que criam objetos. Este parmetro especifica o tipo de objeto a ser criado. Ele poderia ser um identificador de classe, um inteiro, um string ou qualquer outra coisa que identifica o tipo de produto. De fato, com esta abordagem, AbstractFactory somente necessita uma nica operao Make com um parmetro indicando um tipo de objeto a ser criado. Esta a tcnica usada em Prototype e nas fbricas abstratas baseadas em classe, discutidas anteriormente. Essa variao mais fcil de usar numa linguagem com tipos dinamicamente definidos como Smalltalk, do que numa linguagem com tipos estaticamente definidos como C++. Voc pode us-la em C++ somente quando todos os objetos tm a mesma classe abstrata de base ou quando os objetos-produto podem ser seguramente forados a serem do tipo correto pelo cliente que os solicitou. A seo de implementao de Factory Method (115) mostra como implementar tais operaes parametrizadas em C++. Mas mesmo quando no necessrio forar o tipo correto, permanece um problema inerente: todos os produtos so retornados ao cliente com a mesma interface abstrata conforme especificado pelo tipo de retorno. O cliente no ser capaz de diferenciar ou fazer hipteses seguras sobre a classe de um produto. Se os clientes necessitam excetuar operaes especficas s subclasses, elas no sero acessveis atravs da interface abstrata. Embora o cliente possa executar um downcast (por exemplo, com dynamic_cast em C++), isso no sempre vivel ou seguro porque o downcast pode falhar. Este o clssico compromisso para se obter uma interface altamente flexvel e extensvel.

Exemplo de cdigo
Aplicaremos o padro Abstract Factory para criar os labirintos que discutimos no comeo deste captulo. A classe MazeFactory pode criar componentes de labirintos. Ela constri salas, paredes e portas entre salas. Pode ser usada por um programa que l plantas de labirintos de um arquivo e constri o correspondente labirinto. Ou pode ser usada por um programa que constri labirintos aleatoriamente. Os programas que constrem labirintos recebem MazeFactory como argumento, de maneira que o programador pode especificar as classes de salas, paredes e portas a serem construdas.

PADRES DE PROJETO

101

Lembre que a funo-membro CreateMaze (94) constri um pequeno labirinto consistindo em duas salas com uma porta entre elas. CreateMaze codifica de maneira rgida os nomes das classes, tornando difcil criar labirintos com componentes diferentes. Aqui apresentamos uma verso de CreateMaze que corrige essa falha aceitando uma MazeFactory como um parmetro:

Podemos criar EnchantedMazeFactory, uma fbrica para labirintos encantados, introduzindo subclasses de MazeFactory. EnchantedMazeFactory redefinir diferentes funes-membro e retornar diferentes subclasses de Room, Wall, etc.

Suponha agora que queiramos construir um jogo de labirinto no qual uma sala pode ter uma bomba colocada nela. Se a bomba detona, ela danificar as paredes (no mnimo). Podemos construir uma subclasse de Room para registrar se uma sala tem uma bomba nela e se a bomba explodiu. Tambm necessitaremos de uma subclasse de Wall para manter o registro do dano feito parede. Chamaremos estas classes de RoomWithABomb e BombedWall. A ltima classe que definiremos BombedMazeFactory, uma subclasse de MazeFactory que assegura que as paredes so da classe BombedWall e as salas so da classe RoomWithABomb. BombedMazeFactory somente necessita redefinir duas funes:

102

CAPTULO 3 PADRES DE CRIAO

Para construir um simples labirinto que pode conter bombas, simplesmente chamamos CreateMaze com uma BombedMazeFactory.

CreateMaze pode receber uma instncia de EnchantedMazeFactory da mesma maneira para construir labirintos encantados. Note que a MazeFactory apenas uma coleo de mtodos de fbricas. Essa a maneira mais comum de implementar o padro Abstract Factory. Note tambm que MazeFactory no uma classe abstrata; assim, ela funciona tanto como AbstractFactory quanto como ConcreteFactory. Esta uma outra implementao comum para aplicaes simples do padro Abstract Factory. Porque a MazeFactory uma classe concreta que consiste inteiramente de mtodos de fbricas, fcil criar uma nova MazeFactory criando uma subclasse e redefinindo as operaes que necessitam ser mudadas. CreateMaze usou a operao SetSide sobre as salas para especificar os seus lados. Se ela cria salas com uma BombedMazeFactory, ento o labirinto ser constitudo de objetos RoomWithABomb, com lados BombedWall. Se RoomWithABomb tivesse que acessar um membro especfico de uma subclasse de BombedWall, ento ele teria que fazer um cast para uma referncia a suas paredes de Wall* para BombedWall*. Este downcasting seguro na medida que o argumento de fato uma BombedWall, o que certamente verdadeiro se as paredes so construdas exclusivamente com uma BombedMazeFactory. Linguagens com tipos dinmicos, tais como Smalltalk, naturalmente no exigem downcasting, mas elas podem produzir erros em tempo de execuo se encontrarem Wall quando esto esperando uma subclasse de Wall. A utilizao do padro Abstract Factory para construir paredes ajuda a evitar esses erros em tempo de execuo ao garantir que somente certos tipos de paredes podem ser criados. Consideremos uma verso Smalltalk de MazeFactory, uma verso com uma nica operao make que aceita um tipo de objeto a ser criado como um parmetro. Alm disso, a fbrica concreta armazena as classes dos produtos que cria. Primeiro, escreveremos uma verso equivalente de CreateMaze em Smalltalk:

PADRES DE PROJETO

103

Como discutimos na seo de Implementao, Maze Factory necessita somente de uma varivel de instncia partCatalog para produzir um dicionrio cuja chave a classe do componente. Lembre-se tambm de como implementamos o mtodo make:

Agora podemos criar uma MazeFactory e us-la para implementar createMaze. Criaremos a fbrica usando um mtodo createMazeFactory da classe MazeGame.

Uma BombedMazeFactory ou uma EnchantedMazeFactory criada associando-se diversas classes com as chaves. Por exemplo, uma EnchantedMazeFactory poderia ser criada da seguinte maneira:

Usos conhecidos
InterViews usa o sufixo Kit [Lin92] para denotar classes AbstractFactory. Ela define fbricas abstratas WidgetKit e DialogKit para gerao de objetos especficos da interface de usurio para interao. InterViews tambm inclui LayoutKit, que gera diferentes objetos por composio dependendo do layout desejado. Por exemplo, um layout que conceitualmente horizontal pode exigir diferentes objetos compostos, dependendo da orientao do documento (retrato ou paisagem). ET++ [WGM88] usa o padro Abstract Factory para obter portabilidade entre diferentes sistemas de janelas (X Windows e SunView, por exemplo). A classe abstrata base WindowSystem define a interface para criar objetos que representam recursos do sistema de janelas (MakeWindow, MakeFont, MakeColor, por exemplo). As subclasses concretas implementam as interfaces para um sistema de janelas especfico. Em tempo de execuo, ET++ cria uma instncia de uma subclasse concreta WindowSystem que cria objetos concretos para os recursos do sistema.

Padres relacionados
As classes AbstractFactory so freqentemente implementadas com mtodos-fbrica (Factory Method (112), mas elas tambm podem ser implementadas usando Prototype (121). Uma fbrica concreta freqentemente um singleton (Singleton (130).

104

CAPTULO 3 PADRES DE CRIAO

BUILDER
Inteno

criao de objetos

Separar a construo de um objeto complexo da sua representao de modo que o mesmo processo de construo possa criar diferentes representaes.

Motivao
Um leitor de um documento em RTF (Rich Text Format) deveria ser capaz de converter RTF em muitos formatos de texto. O leitor poderia converter documentos RTF em texto ASCII comum ou widget de texto, que possa ser editado interativamente. O problema, contudo, que o nmero de converses possveis aberto. Por isso, deve ser fcil acrescentar uma nova converso sem modificar o leitor. Uma soluo configurar a classe RTFReader com um objeto TextConverter que converte RTF para uma outra representao de textos. medida que o RTFReader analisa o documento RTF, ele usa o objeto TextConverter para efetuar a converso. Sempre que o RTFReader reconhece um smbolo RTF (texto simples, ou uma palavra de controle do RTF), ele emite uma solicitao para o TextConverter para converter esse smbolo. Os objetos TextConverter so responsveis tanto por efetuar a converso dos dados como pela representao do smbolo num formato particular. As subclasses de TextConverter se especializam em diferentes converses e formatos. Por exemplo, um ASCIIConverter ignora solicitaes para converter qualquer coisa, exceto texto simples. Por outro lado, um TeXConverter implementar operaes para todas as solicitaes visando produzir uma representao TEX que capture toda a informao estilstica do texto. Um TextWidgetConverter produzir um objeto para uma interface de usurio complexa que permite ao usurio ver e editar o texto.
RTFReader ParseRTF() builder

builders TextConverter ConvertCharacter(char) ConvertFontChange(Font) ConvertParagraph()

while (t = get the next token) { switch t.Type { CHAR: builder>CovertCharacter(t.Char) FONT: builder>ConvertFontChange(t.Font) PARA: builder>ConvertParagraph() } }

ASCIIConverter ConvertCharacter(char) GetASCIIText()

TeXConverter ConvertCharacter(char) ConvertFontChange(Font) ConvertParagraph() GetTeXText()

TextWidgetConverter ConvertCharacter(char) ConvertFontChange(Font) ConvertParagraph() GetTestWidget()

ASCIIText

TeXText

TextWidget

Cada tipo de classe conversora implementa o mecanismo para criao e montagem de um objeto complexo, colocando-o atrs de uma interface abstrata. O conversor separado do leitor, que responsvel pela anlise de um documento RTF. O padro Builder captura todos estes relacionamentos. Cada classe conversora chamada um builder no padro, e o leitor chamado de director. Aplicado a este

PADRES DE PROJETO

105

exemplo, o Builder separa o algoritmo para interpretar um formato de texto (isto , o analisador de documentos RTF) de como um formato convertido criado e representado. Isso nos permite reutilizar o algoritmo de anlise (parsing) do RTFReader para criar diferentes representaes de texto a partir de documentos RTF simplesmente configure o RTFReader com diferentes subclasses de TextConverter.

Aplicabilidade
Use o padro Builder quando: o algoritmo para criao de um objeto complexo deve ser independente das partes que compem o objeto e de como elas so montadas. o processo de construo deve permitir diferentes representaes para o objeto que construdo.

Estrutura
Director Construct() builder Builder

BuildPart()

for all objects in structure { builder>BuildPart() }

ConcreteBuilder BuildPart() GetResult()

Product

Participantes
Builder(TextConverter) especifica uma interface abstrata para criao de partes de um objetoproduto. ConcreteBuilder (ASCIIConverter, TeXConverter, TextWidgetConverter) constri e monta partes do produto pela implementao da interface de Builder; define e mantm a representao que cria; fornece uma interface para recuperao do produto (por exemplo, GetASCIIText, GetTextWidget). Director (RTFReader) constri um objeto usando a interface de Builder. Product (ASCIIText, TeXText, TextWidget). representa o objeto complexo em construo. ConcreteBuilder constri a representao interna do produto e define o processo pelo qual ele montado; inclui classes que definem as partes constituintes, inclusive as interfaces para a montagem das partes no resultado final.

106

CAPTULO 3 PADRES DE CRIAO

Colaboraes
O cliente cria o objeto Director e o configura com o objeto Builder desejado. Director notifica o construtor sempre que uma parte do produto deve ser construda. Builder trata solicitaes do diretor e acrescenta partes ao produto. O cliente recupera o produto do construtor. O seguinte diagrama de interao ilustra como Builder e Director cooperam com um cliente.
aClient
new ConcreteBuilder new Director(aConcreteBuilder)

aDirector

aConcreteBuilder

Construct()

BuildPartA() BuildPartB() BuildPartC()

GetResult()

Conseqncias
A seguir so apresentadas as conseqncias-chave da utilizao do padro Builder: 1. Permite variar a representao interna de um produto. O objeto Builder fornece ao diretor uma interface abstrata para a construo do produto. A interface permite ao construtor ocultar a representao e a estrutura interna do produto. Ela tambm oculta como o produto montado. J que o produto construdo atravs de uma interface abstrata, tudo o que voc tem que fazer para mudar sua representao interna definir um novo tipo de construtor. 2. Isola o cdigo para construo e representao. O padro Builder melhora a modularidade pelo encapsulamento da forma como um objeto complexo construdo e representado. Os clientes nada necessitam saber sobre as classes que definem a estrutura interna do produto; tais classes no aparecem na interface de Builder. Cada ConcreteBuilder contm todo o cdigo para criar e montar um tipo de produto especfico. O cdigo escrito somente uma vez; ento, diferentes Directors podem reutiliz-lo para construir variantes de Product com o mesmo conjunto de partes. No exemplo anterior do RTF, ns poderamos definir o leitor para um formato diferente do RTF, digamos um SGMLReader, e usado os mesmos TextConverters para gerar representaes ASCIIText, TeXText, e TexWidget de documentos SGML.

PADRES DE PROJETO

107

3. Oferece um controle mais fino sobre o processo de construo. Ao contrrio de padres de criao que constroem produtos de uma s vez, o Builder constri o produto passo a passo sob o controle do diretor. Somente quando o produto est terminado o diretor o recupera do construtor. Da a interface de Builder refletir o processo de construo do produto mais explicitamente do que outros padres de criao. Isso d um controle mais fino sobre o processo de construo e, conseqentemente, da estrutura interna do produto resultante.

Implementao
Existe uma classe abstrata Builder que define uma operao para cada componente que um diretor lhe pedir para criar. As operaes no fazem nada por omisso. Uma classe ConcreteBuilder redefine as operaes para os componentes que ela est interessada em criar. Aqui apresentamos outros tpicos de implementao a serem considerados: 1. Interface de montagem e construo. Os Builders constroem os seus produtos de uma forma gradual. Portanto, a interface da classe Builder deve ser geral o bastante para permitir a construo de produtos para todos os tipos de construtores concretos. Um tpico-chave de projeto diz respeito ao modelo para o processo de construo e montagem. Um modelo onde os resultados das solicitaes de construo so simplesmente acrescentados ao produto normalmente suficiente. No exemplo do RTF, o construtor converte e acrescenta o prximo smbolo ao texto que converteu at aqui. Mas s vezes voc pode necessitar acesso a partes do produto construdas anteriormente. No exemplo do labirinto que ns apresentamos no cdigo de exemplo, a interface MazeBuilder permite acrescentar uma porta entre salas existentes. Estruturas de rvores, tais como rvores de derivao, que so construdas de baixo para cima (bottom-up), so um outro exemplo. Nesse caso, o construtor retornaria nsfilhos para o diretor, que ento os passaria de volta ao construtor para construir os ns-pais. 2. Por que no classes abstratas para produtos? Nos casos comuns, os produtos produzidos pelos construtores concretos diferem to grandemente na sua representao que h pouco a ganhar ao dar a diferentes produtos uma classe-pai comum. No exemplo do RTF, os objetos ASCIIText e TextWidget tm pouca probabilidade de ter uma interface comum, e tampouco necessitam de uma. Uma vez que o cliente em geral configura o diretor com o construtor concreto apropriado, o cliente est em posio de saber quais subclasses concretas de Builder esto em uso e pode tratar os seus produtos de acordo. 3. Mtodos vazios como a omisso em Builder. Em C++, os mtodos de construo so intencionalmente no-declarados como funes-membro. Em vez disso, eles so definidos como mtodos vazios, permitindo aos clientes redefinirem somente as operaes em que esto interessados.

108

CAPTULO 3 PADRES DE CRIAO

Exemplo de cdigo
Definiremos uma variante da funo-membro (C++) CreateMaze (pgina 94) que aceita como argumento um construtor (builder) da classe MazeBuilder. A classe MazeBuilder define a seguinte interface para a construo de labirintos:

Essa interface pode criar trs coisas: (1) o labirinto, (2) salas, cada uma com um nmero de sala, e (3) portas entre salas numeradas. A operao GetMaze retorna o labirinto para o cliente. As subclasses de MazeBuilder redefiniro essa operao para retornar o labirinto que construram. Todas as operaes de construo de labirinto de MazeBuilder, por omisso, nada fazem. Elas no so declaradas virtuais puras para permitir s classes derivadas redefinir somente aqueles mtodos nos quais estiverem interessadas. Dada a interface MazeBuilder, podemos criar a funo membro CreateMaze, de forma a aceitar este construtor (builder) como um parmetro.

Compare esta verso de CreateMaze com a original. Observe como o builder oculta a representao interna do labirinto isto , as classes que definem salas, portas e paredes e como estas partes so montadas para completar o labirinto final. Algum poderia supor que existem classes para representar salas e portas, mas no h sinal de uma classe para paredes. Isto torna mais fcil mudar a forma pela qual um labirinto representado, uma vez que nenhum dos clientes de MazeBuilder tem que ser modificado. Como os outros padres de criao, o padro Builder encapsula como os objetos so criados, neste caso atravs da interface definida por MazeBuilder. Isso significa que podemos reutilizar MazeBuilder para construir diferentes tipos de labirintos. A operao CreateComplexMaze nos d um exemplo:

PADRES DE PROJETO

109

Note que MazeBuilder, ele prprio, no cria labirintos; sua finalidade principal somente definir uma interface para criar labirintos. Ele define implementaes vazias primariamente por convenincia. Subclasses de MazeBuilder fazem o trabalho real. A subclasse StandardMazeBuilder uma implementao que constri labirintos simples. Ela mantm o controle do labirinto que est construindo atravs da varivel _currentMaze.

A CommonWall uma operao utilitria que determina a direo da parede comum entre duas salas. O constructor (C++) StandardMazeBuilder simplesmente inicia _currentMaze.

A operao BuildMaze instancia um Maze (labirinto) que outras operaes montaro e que, em algum momento, retornaro para o cliente (com GetMaze).

A operao BuildRoom cria uma sala e constri as paredes em volta dela:

110

CAPTULO 3 PADRES DE CRIAO

Para construir uma porta entre duas salas, StandardMazeBuilder procura ambas as salas no labirinto e encontra a parede adjacente:

Os clientes agora podem usar CreateMaze em conjunto com StandardMazeBuilder para criar um labirinto:

Poderamos ter posto todas as operaes de StandardMazeBuilder em Maze e deixado cada Maze construir a si prprio. Porm, ao tornar Maze menor mais fcil compreend-lo e modific-lo, e StandardMazeBuilder fcil de separar de Maze. Ainda mais importante, separar os dois permite ter uma variedade de MazeBuilders, cada um usando diferentes classes para salas, paredes e portas. Um MazeBuilder mais extico CountingMazeBuilder. Esse construtor no cria nenhum labirinto, ele apenas conta os diferentes tipos de componentes que teriam sido criados.

O construtor inicia os contadores e as operaes de MazeBuilder redefinidas os incrementam adequadamente.

PADRES DE PROJETO

111

Aqui apresentamos como um cliente pode usar um CountingMazeBuilder:

Usos conhecidos
O conversor de RTF de ET++ [WGM88]. O seu bloco construtor de texto usa um builder para processar texto armazenado no formato RTF. Builder um padro comum em Smalltalk-80 [Par90]: A classe Parser no subsistema compilador um Director que aceita um objeto ProgramNodeBuilder como um argumento. Um objeto Parser notifica seu objeto ProgramNodeBuilder cada vez que reconhece uma construo sinttica. Quando o analisador (parser) termina, pede ao construtor a rvore de derivao (parse tree) que construiu, retornando-a para o cliente. O ClassBuilder um builder que Classes usam para criar subclasses por elas mesmas. Neste caso, Class tanto Director como Producto. O ByteCodeStream um construtor que cria um mtodo compilado como um vetor de bytes (byte array). ByteCodeStream um uso no-padronizado do padro Builder porque o objeto complexo que ele constri codificado como um vetor de bytes e no como um objeto Smalltalk normal. Mas a interface de ByteCodeStream tpica de um construtor (builder,) e seria fcil substituir ByteCodeStream por uma classe diferente que representasse programas como um objeto composto. O framework Service Configurator do Adaptive Communications Environment usa um builder para construir componentes de servios de rede que so linkeditados a um servidor em tempo de execuo [SS94]. Os componentes so descritos com

112

CAPTULO 3 PADRES DE CRIAO

uma linguagem de configurao que analisada por um analisador LALR(1). As aes semnticas do analisador executam operaes sobre o construtor que acrescenta informaes ao componente de servio. Neste caso, o analisador o Director.

Padres relacionados
Abstract Factory (95) semelhante a Builder no sentido de que tambm pode construir objetos complexos. A diferena principal que o padro Builder focaliza a construo de um objeto complexo passo a passo. A nfase do Abstract Factory sobre famlias de objetos-produto (simples ou complexos). O Builder retorna o produto como um passo final, mas no caso do padro Abstract Factory o produto retornado imediatamente. Um Composite (160) o que freqentemente o builder constri.

FACTORY METHOD
Inteno

criao de classes

Definir uma interface para criar um objeto, mas deixar as subclasses decidirem que classe instanciar. O Factory Method permite adiar a instanciao para subclasses.

Tambm conhecido como


Virtual Constructor

Motivao
Os frameworks usam classes abstratas para definir e manter relacionamentos entre objetos. Um framework freqentemente responsvel tambm pela criao desses objetos. Considere um framework para aplicaes que podem apresentar mltiplos documentos para o usurio. Duas abstraes-chave nesse framework so as classes Application (aplicao) e Document (documento). As duas classes so abstratas, e os clientes devem prover subclasses para realizar suas implementaes especficas para a aplicao. Por exemplo, para criar uma aplicao de desenho, definimos as classes DrawingApplication e DrawingDocument. A classe Application responsvel pela administrao de Documents e ir cri-los conforme exigido quando o usurio seleciona Open (abrir) ou New (novo), por exemplo, num menu. Uma vez que a subclasse Document a ser instanciada prpria da aplicao especfica, a classe Application no pode prever a subclasse de Document a ser instanciada a classe Application somente sabe quando um documento deve ser criado, e no que tipo de Document criar. Isso cria um dilema: o framework deve instanciar classes, mas ele somente tem conhecimento de classes abstratas, as quais no pode instanciar. O padro Factory Method oferece uma soluo. Ele encapsula o conhecimento sobre a subclasse de Document que deve ser criada e move este conhecimento para fora do framework.

PADRES DE PROJETO

113

As subclasses de Application redefinem uma operao abstrata CreateDocument em Application para retornar a subclasse apropriada de Document. Uma vez que uma subclasse de Application instanciada, pode ento instanciar Documents especficos da aplicao sem conhecer suas classes. Chamamos CreateDocument um factory method porque ele responsvel pela manufatura de um objeto.

Aplicabilidade
Use o padro Factory Method quando: uma classe no pode antecipar a classe de objetos que deve criam; uma classe quer que suas subclasses especifiquem os objetos que criam; classes delegam responsabilidade para uma dentre vrias subclasses auxiliares, e voc quer localizar o conhecimento de qual subclasse auxiliar que a delegada.

Estrutura
Creator Product

FactoryMethod() AnOperation()

... product = FactoryMethod() ...

ConcreteProduct

ConcreteCreator FactoryMethod() return new ConcreteProduct

Participantes
Product (Document) define a interface de objetos que o mtodo fbrica cria. ConcreteProduct (MyDocument) implementa a interface de Product. Creator (Application) Declara o mtodo fbrica, o qual retorna um objeto do tipo Product. Creator pode tambm definir uma implementao por omisso do mtodo factory que retorna por omisso um objeto ConcreteProduct.

114

CAPTULO 3 PADRES DE CRIAO

Pode chamar o mtodo factory para criar um objeto Product. ConcreteCreator (MyApplication) Redefine o mtodo-fbrica para retornar a uma instncia de um ConcreteProduct.

Colaboraes
Creator depende das suas subclasses para definir o mtodo fbrica de maneira que retorne uma instncia do ConcreteProduct apropriado.

Conseqncias
Os Factory Methods eliminam a necessidade de anexar classes especficas das aplicaes no cdigo. O cdigo lida somente com a interface de Product; portanto, ele pode trabalhar com quaisquer classes ConcreteProduct definidas pelo usurio. Uma desvantagem em potencial dos mtodos-fbrica que os clientes podem ter que fornecer subclasses da classe Creator somente para criar um objeto ConcreteProduct em particular. Usar subclasses bom quando o cliente tem que fornecer subclasses a Creator de qualquer maneira, caso contrrio, o cliente deve lidar com outro ponto de evoluo. Apresentamos aqui duas conseqncias adicionais do Factory Method: 1. Fornece ganchos para subclasses. Criar objetos dentro de uma classe com um mtodo fbrica sempre mais flexvel do que criar um objeto diretamente. Factory Method d s subclasses um gancho para fornecer uma verso estendida de um objeto. No exemplo de Documentos, a classe Document poderia definir um mtodofbrica chamado CreateFileDialog que cria um objeto file dialog por omisso para abrir um documento existente. Uma subclasse de Document pode definir um file dialog especfico da aplicao redefinindo este mtodo fbrica. Neste caso, o mtodo fbrica no abstrato, mas fornece uma implementao por omisso razovel. 2. Conecta hierarquias de classe paralelas. Nos exemplos que consideramos at aqui o mtodo-fbrica somente chamado por Creators. Mas isto no precisa ser obrigatoriamente assim; os clientes podem achar os mtodos-fbrica teis, especialmente no caso de hierarquias de classe paralelas. Hierarquias de classe paralelas ocorrem quando uma classe delega alguma das suas responsabilidades para uma classe separada. Considere, por exemplo, figuras que podem ser manipuladas interativamente; ou seja, podem ser esticadas, movidas ou giradas usando o mouse. Implementar tais interaes no sempre fcil. Isso freqentemente requer armazenar e atualizar informao que registra o estado da manipulao num certo momento. Este estado necessrio somente durante a manipulao; portanto, no necessita ser mantido no objeto-figura. Alm do mais, diferentes figuras se comportam de modo diferente quando so manipuladas pelo usurio. Por exemplo, esticar uma linha pode ter o efeito de mover um dos extremos, enquanto que esticar um texto pode mudar o seu espaamento de linhas.

PADRES DE PROJETO

115

Com essas restries, melhor usar um objeto Manipulator separado, que implementa a interao e mantm o registro de qualquer estado especfico da manipulao que for necessrio. Diferentes figuras utilizaro diferentes subclasses Manipulator para tratar interaes especficas. A hierarquia de classes Manipulator resultante paralela (ao menos parcialmente) hierarquia de classes de Figure:
Figure Client Manipulator

CreateManipulator() ...

DownClick() Drag() UpClick()

LineFigure CreateManipulator() ...

TextFigure CreateManipulator() ...

LineManipulator DownClick() Drag() UpClick()

TextManipulator DownClick() Drag() UpClick()

A classe Figure fornece um mtodo fbrica CreateManipulator que permite aos clientes criar o correspondente Manipulator de uma Figure. As subclasses de Figure substituem esse mtodo para retornar uma instncia da subclasse Manipulator correta para elas. Como alternativa, a classe Figure pode implementar CreateManipulator para retornar por omisso uma instncia de manipulator, e as subclasses de Figure podem simplesmente herdar essa instncia por omisso. As classes Figure que fizerem assim no necessitaro de uma subclasse correspondente de Manipulator por isso dizemos que as hierarquias so somente parcialmente paralelas. Note como o mtodo-fbrica define a conexo entre as duas hierarquias de classes. Nele se localiza o conhecimento de quais classes trabalham juntas.

Implementao
Considere os seguintes tpicos ao aplicar o padro Factory Method: 1. Duas variedades principais. As duas principais variaes do padro Factory Method so: (1) o caso em que a classe Creator uma classe abstrata e no fornece uma implementao para o mtodo-fbrica que ela declara, e (2) o caso quando o Creator uma classe concreta e fornece uma implementao por omisso para o mtodo-fbrica. Tambm possvel ter uma classe abstrata que define uma implementao por omisso, mas isto menos comum. O primeiro caso exige subclasses para definir uma implementao porque no existe uma omisso razovel, assim contornando o dilema de ter que instanciar classes imprevisveis. No segundo caso, o ConcretCreator usa o mtodo fbrica principalmente por razes de flexibilidade. Est seguindo uma regra que diz: criar objetos numa operao separada de modo que subclasses possam redefinir a maneira como eles so criados. Essa regra garante que projetistas de subclasses, caso necessrio, possam mudar a classe de objetos que a classe ancestral instancia. 2. Mtodos-fbrica parametrizados. Uma outra variante do padro permite ao mtodo-fbrica criar mltiplos tipos de produtos. O mtodo-fbrica recebe um parmetro que identifica o objeto a ser criado.

116

CAPTULO 3 PADRES DE CRIAO

Todos os objetos que o mtodo-fbrica cria compartilharo a interface de Product. No exemplo de Document, Application pode suportar diferentes tipos de Documents. Voc passa a Create Document um parmetro extra para especificar o tipo de documento a ser criado. O framework de edio grfica Unidraw [VL90] usa esta abordagem para reconstruir objetos salvos em disco. Unidraw define uma classe Creator com mtodo-fbrica Create que aceita um identificador de classe como argumento. O identificador de classe especifica a classe a ser instanciada. Quando Unidraw salva um objeto em disco, primeiro grava o identificador da classe, e ento suas variveis de instncia. Quando reconstri o objeto de disco, primeiro l o identificador de classe. Depois que o identificador de classe lido, o framework chama Create, passando o identificador como o parmetro. Create procura o constructor para a classe correspondente, utilizando-o para instanciar o objeto. Por ltimo, Create chama a operao Read do objeto, a qual l a informao restante do disco e inicia as variveis de instncia do objeto. Um mtodo-fbrica parametrizado tem a seguinte forma geral, onde MyProduct e YourProduct so subclasses de Product:

repete para os produtos restantes

Redefinir um mtodo fbrica parametrizado permite, fcil e seletivamente, estender ou mudar os produtos que um Creator produz. Voc pode introduzir novos identificadores para novos tipos de produtos, ou pode associar identificadores existentes com diferentes produtos. Por exemplo, uma subclasse MyCreator poderia trocar MyProduct por YourProduct e suportar uma nova subclasse TheirProduct:

nota: YOURS e MINE foram trocados propositadamente

chamado se todos os demais falham

Note que a ltima coisa que essa operao faz chamar Create na classeme. Isso porque MyCreator::Create trata somente YOURS, MINE e THEIRS de modo diferente da classe-me. Ela no est interessada em outras classes. Da dizermos que MyCreator estende os tipos de produtos criados e adia a responsabilidade da criao de todos, exceto uns poucos produtos, para sua superclasse.

PADRES DE PROJETO

117

3. Variantes e tpicos especficos das linguagens. Diferentes linguagens levam a outras variantes interessantes, bem como a cuidados especiais. Os programas em Smalltalk freqentemente usam um mtodo que retorna a classe do objeto a ser instanciado. Um mtodo-fbrica Creator pode usar esse valor para criar um produto, e um ConcreteCreator pode armazenar ou mesmo computar esse valor. O resultado uma associao ainda mais tardia para o tipo de ConcreteProduct a ser instanciado. Uma verso Smalltalk do exemplo de Document pode definir um mtodo documentClass em Application. O mtodo documentClass retorna a classe apropriada de Document para instanciar documentos. A implementao de documentClass em MyApplication retorna a classe MyDocument. Assim, na classe Application ns temos

Na classe MyApplication temos

que retorna a classe MyDocument a ser instanciada para Application. Uma abordagem ainda mais flexvel prxima dos mtodos-fbrica parametrizados armazenar a classe a ser criada como uma varivel de classe de Application. Desse modo, voc no tem que introduzir subclasses de Application para variar o produto. Os mtodos-fbrica em C++ so sempre funes virtuais e, freqentemente, virtuais puras. Somente seja cuidadoso para no chamar mtodos-fbrica no construtor de Creator o mtodo-fbrica em ConcreteCreator ainda no estar disponvel. Voc pode evitar esse problema sendo cuidadoso, acessando produtos exclusivamente atravs de operaes de acesso que criam o produto sob demanda. Em vez de criar o produto concreto no constructor, o constructor meramente o inicia como 0 (zero). O accessor retorna o produto. Mas primeiro ele verifica a existncia do produto, e quando no existe o accessor o cria. Essa tcnica algumas vezes chamada de inicializao tardia (lazy inicialization). O cdigo a seguir mostra uma implementao tpica:

118

CAPTULO 3 PADRES DE CRIAO

4. Utilizando templates para evitar o uso de subclasses. Como j mencionamos, outro problema potencial com mtodos-fbrica que podem for-lo a introduzir subclasses somente para criar os objetos-produto apropriados. Uma outra maneira de contornar isto em C++ fornecer uma subclasse template de Creator que parametrizada pela classe Product:

Com esse template, o cliente fornece somente a classe-produto no so necessrias subclasses de Creator.

5. Convenes de nomenclatura. uma boa prtica o uso de convenes de nomenclatura que tornam claro que voc est usando mtodos-fbrica. Por exemplo, o framework de aplicaes MacApp para o Macintosh [APP89] sempre declara a operao abstrata que define o mtodo fbrica como Class* DoMakeClass (), onde Class a classe-produto.

Exemplo de cdigo
A funo CreateMaze (pg. 94) constri e retorna um labirinto. Um problema com esta funo que codifica de maneira rgida as classes de labirinto, salas, portas e paredes. Ns introduziremos o mtodo-fbrica para permitir s subclasses escolherem estes componentes. Primeiramente, definiremos o mtodo-fbrica em MazeGame para criar os objetoslabirinto, sala, parede e porta:

mtodos-fbrica

PADRES DE PROJETO

119

Cada mtodo-fbrica retorna um componente de labirinto de um certo tipo.


MazeGame fornece implementaes por omisso que retornam os tipos mais simples de

labirinto, salas, portas e paredes. Agora podemos reescrever CreateMaze para usar esses mtodos fbrica:

Diferentes jogos podem introduzir subclasses de MazeGame para especializar partes do labirinto. As subclasses de MazeGame podem redefinir alguns ou todos os mtodos-fbrica para especificar variaes em produtos. Por exemplo, um BombedMazeGame pode redefinir os produtos Room e Wall para retornar variedades com bombas:

Uma variante EnchantedMazeGame poderia ser definida desta forma:

120

CAPTULO 3 PADRES DE CRIAO

Usos conhecidos
Os mtodos-fbrica permeiam toolkits e frameworks. O exemplo precedente de documentos um uso tpico no MacApp e ET++ [WGM88]. O exemplo do manipulador vem do Unidraw. A classe View no framework Model/View/Controller/Smalltalk-80 tem um mtodo defaultController que cria um controlador, e isso pode parecer ser o mtodo-fbrica [Par90]. Mas subclasses de View especificam a classe no seu controlador por omisso atravs da definio de defaultControllerClass, que retorna a classe da qual defaultController cria instncias. Assim, defaultControllerClass o verdadeiro mtodo fbrica, isto , o mtodo que as subclasses deveriam redefinir. Um exemplo mais esotrico no Smalltalk-80 o mtodo-fbrica parserClass definido por Behavior (uma superclasse de todos os objetos que representam classes). Isto permite a uma classe usar um parser (analisador) customizado para seu cdigofonte. Por exemplo, um cliente pode definir uma classe SQLParser para analisar o cdigo-fonte de uma classe com comandos SQL embutidos. A Classe Behavior implementa parserClass retornando a classe Parser padro do Smalltalk. A classe que inclui comandos SQL embutidos redefine este mtodo (como um mtodo de classe) e retorna a classe SQLParser. O sistema ORB Orbix da IONA Technologies [ION94] usa Factory Method para gerar um tipo apropriado de proxy (ver Proxy (198)) quando um objeto solicita uma referncia para um objeto remoto. O Factory Method torna fcil substituir o proxypadro por um outro que, por exemplo, use caching do lado do cliente.

Padres relacionados
Abstract Factory (95) freqentemente implementado utilizado o padro Factory Method. O exemplo na relao de Motivao no padro Abstract Factory tambm ilustra o padro Factory Method. Factory Methods so usualmente chamados dentro de Template Methods (301). No exemplo do documento acima, NewDocument um template method. Prototypes (121) no exigem subclassificao de Creator. Contudo, freqentemente necessitam uma operao Initialize na classe Product. A Creator usa Initialize para iniciar o objeto. O Factory Method no exige uma operao desse tipo.

PADRES DE PROJETO

121

PROTOTYPE
Inteno

criao de objetos

Especificar os tipos de objetos a serem criados usando uma instncia-prottipo e criar novos objetos pela cpia desse prottipo.

Motivao
Voc poderia construir um editor para partituras musicais customizando um framework geral para editores grficos, acrescentando novos objetos que representam notas, pausas e pentagramas. O editor do framework pode ter uma paleta de ferramentas para acrescentar esses objetos de msica partitura. A paleta tambm incluiria ferramentas para selecionar, mover e manipular objetos de msica de outra forma. O usurio clicaria na ferramenta de uma semnima para adicionar semnimas partitura. Ou poderia usar a ferramenta de movimentao para mover uma nota para cima ou para baixo nas linhas de pauta, desta forma alterando seu registro sonoro. Vamos considerar que o framework fornea uma classe abstrata Graphic para componentes grficos, como notas e pentagramas. Alm disso, fornece uma classe abstrata Tool para definir ferramentas como aquelas da paleta. O framework tambm predefine uma subclasse GraphicTool para ferramentas que criam instncias de objetos grficos e os adicionam ao documento. Mas GraphicTool apresenta um problema para o projetista do framework. As classes para notas e pentagramas so especficas da nossa aplicao, mas a classe GraphicTool pertence ao framework. GraphicTool no sabe como criar instncias das nossas classes musicais para acrescent-las partitura. Poderamos introduzir subclasses de GraphicTool para cada tipo de objeto musical, mas isso produziria muitas subclasses diferentes somente no tipo de objeto musical que elas instanciam. Sabemos que composio de objetos uma alternativa flexvel para o uso de subclasses. A questo, porm, , como pode um framework us-la para parametrizar instncias de GraphicTool pela Classe de Graphic que se espera que elas criem? A soluo fazer GraphicTool criar um novo Graphic copiando ou clonando uma instncia de uma subclasse de Graphic. Chamamos esta instncia de prottipo (prototype). A GraphicTool parametrizada pelo prottipo que ela deveria clonar e acrescentar ao documento. Se todas as subclasses de Graphic suportam uma operao Clone, ento GraphicTool pode clonar qualquer tipo de Graphic. Assim, em nosso editor musical, cada ferramenta para criar um objeto musical uma instncia de GraphicTool que iniciada com um prottipo diferente. Cada instncia de GraphicTool produzir um objeto musical clonando o seu prottipo e acrescentando o clone partitura.

122

CAPTULO 3 PADRES DE CRIAO

Tool Manipulate()

Graphic Draw(Position) Clone()

prototype RotateTool Manipulate() GraphicTool Manipulate() Staff Draw(Position) Clone() WholeNote Draw(Position) Clone() HalfNote Draw(Position) Clone()

MusicalNote

p = prototype>Clone() while (user drags mouse) { p>Draw(new position) } insert p into drawing

return copy of self

return copy of self

Podemos usar o padro Prototype para reduzir o nmero de classes ainda mais. Temos classes separadas para breves e semibreves, mas isto provavelmente desnecessrio. Ao invs disso, poderiam ser instncias da mesma classe iniciada com diferentes bitmaps e duraes. Uma ferramenta para criao de notas do tipo breve torna-se somente uma GraphicTool cujo prottipo uma MusicalNote iniciada como uma breve. Isso pode reduzir o nmero de classes no sistema dramaticamente. Isso tambm torna mais fcil acrescentar um novo tipo de nota ao editor musical.

Aplicabilidade
Use o padro Prototype quando um sistema tiver que ser independente de como os seus produtos so criados, compostos e representados; e quando as classes a instanciar forem especificadas em tempo de execuo, por exemplo, por carga dinmica; ou para evitar a construo de uma hierarquia de classes de fbricas paralela hierarquia de classes de produto; ou quando as instncias de uma classe puderem ter uma dentre poucas combinaes diferentes de estados. Pode ser mais conveniente instalar um nmero correspondente de prottipos e clon-los, ao invs de instanciar a classe manualmente, cada vez com um estado apropriado.

PADRES DE PROJETO

123

Estrutura
Client Operation() prototype Prototype

Clone()

p = prototype>Clone() ConcretePrototype1 Clone() ConcretePrototype2 Clone()

return copy of self

return copy of self

Participantes
Prototype (Graphic) declara uma interface para clonar a si prprio. ConcretePrototype (Staff, Whole Nota, Half Note) implementa uma operao para clonar a si prprio. Client (GraphicTool) cria um novo objeto solicitando a um prottipo que clone a si prprio.

Colaboraes
Um cliente solicita a um prottipo que este clone a si prprio.

Conseqncias
Prototype tem muitas das mesmas conseqncias que Abstract Factory (95) e Builder (104) tm: ele oculta as classes de produtos concretas do cliente, desta forma reduzindo o nmero de nomes que os clientes necessitam saber. Alm disso, esses padres permitem a um cliente trabalhar com classes especficas de uma aplicao sem necessidade de modificao. Os benefcios adicionais do padro Prototype esto relacionados abaixo. 1. Acrescenta e remove produtos em tempo de execuo. Prototype permite incorporar uma nova classe concreta de produto a um sistema, simplesmente registrando uma instncia prottipo com o cliente. Isso um pouco mais flexvel do que outros padres de criao, porque o cliente pode instalar e remover prottipos em tempo de execuo. 2. Especifica novos objetos pela variao de valores. Sistemas altamente dinmicos permitem definir novos comportamentos atravs da composio de objetos por exemplo, pela especificao de valores para as variveis de um objeto e no pela definio de novas classes. Voc efetivamente define novos tipos de objetos pela instanciao das classes existentes e registrando as instncias como prottipos dos objetos-clientes. Um cliente pode exibir um novo comportamento atravs da delegao de responsabilidades para o prottipo.

124

CAPTULO 3 PADRES DE CRIAO

Esse tipo de projeto permite aos usurios definir novas classes sem ter que programar. De fato, clonar um prottipo semelhante a instanciar uma classe. O padro Prototype pode reduzir grandemente o nmero de classes que um sistema necessita. No nosso editor musical, uma classe GraphicTool pode criar uma variedade ilimitada de objetos musicais. 3. Especifica novos objetos pela variao da estrutura. Muitas aplicaes constrem objetos com partes e subpartes. Por exemplo, editores para o projeto de circuitos que constroem circuitos a partir de subcircuitos.1 Por questes de convenincia, tais aplicaes freqentemente permitem instanciar estruturas complexas, definidas pelo usurio, para, por exemplo, usar um subcircuito especfico repetidas vezes. O padro Prototype tambm suporta isso. Simplesmente adicionamos esse subcircuito como um prottipo paleta dos elementos de circuitos disponveis. Contanto que o objeto-circuito composto implemente um clone por replicao (deep copy), circuitos com diferentes estruturas podem ser prottipos. 4. Reduz o nmero de subclasses. O Factory Method (112) freqentemente produz uma hierarquia de classes Creator paralela hierarquia de classes do produto. O padro Prototype permite clonar um prottipo em vez de pedir a um mtodo fbrica para construir um novo objeto. Da no necessitar-se de nenhuma hierarquia de classes Creator. Esse benefcio se aplica primariamente a linguagens como C++, que no tratam as classes como objetos de primeira classe. As linguagens que assim o fazem, como Smalltalk e Objective C, obtm menos benefcios, uma vez que sempre se usa um objeto-classe como um criador. Objetos-classe j funcionam como prottipos nessas linguagens. 5. Configura dinamicamente uma aplicao com classes. Alguns ambientes de tempo de execuo permitem carregar classes dinamicamente numa aplicao. O padro Prototype a chave para a explorao de tais possibilidades numa linguagem como C++. Uma aplicao que quer criar instncias de uma classe dinamicamente carregada no ser capaz de referenciar o seu constructor estaticamente. Em vez disso, o ambiente de tempo de execuo cria uma instncia de cada classe automaticamente, quando carregada, e registra a instncia junto a um gerenciador de prottipo (ver a seo Implementao). Ento, a aplicao pode solicitar ao gerenciador de prottipos instncias de classes recmcarregadas, classes essas que originalmente no estavam linkadas ao programa. O framework de aplicaes da ET++ [WGM88] tem um sistema de tempo de execuo que usa este esquema. O principal ponto fraco do padro Prototype que cada subclasse de Prototype deve implementar a operao Clone, o que pode ser difcil. Por exemplo, acrescentar Clone difcil quando as classes consideradas j existem. A implementao de Clone pode ser complicada quando uma estrutura interna dessas classes inclui objetos que no suportam operao de cpia ou tm referncias circulares.

Implementao
Prototype particularmente til com linguagens estticas como C++, na qual as classes no so objetos, e pouca ou nenhuma informao sobre tipos est disponvel em tempo

PADRES DE PROJETO

125

de execuo. Ele menos importante em linguagens como Smalltalk ou Objective C, que fornecem o equivalente a um prottipo (ou seja, um objeto-classe) para criao de instncias de cada classe. Este padro est incorporado em linguagens baseadas em prottipos como a Self [US87], na qual toda a criao de objetos se d pela clonagem de um prottipo. Ao implementar prottipos levam-se em considerao os seguintes aspectos: 1. Usar um gerenciador de prottipos. Quando o nmero de prottipos num sistema no fixo (ou seja, eles podem ser criados e destrudos dinamicamente), importante manter um sistema de registro dos prottipos disponveis. Os clientes no vo gerenciar os prottipos, mas faro sua armazenagem e recuperao pelo sistema de registro. Um cliente solicitar um prottipo ao sistema de registro antes de clon-lo. Ns chamamos esse sistema de registro de gerenciador de prottipos. Um gerenciador de prottipos uma memria associativa que retorna o prottipo correspondente a uma chave fornecida. Ele tem operaes para registrar um prottipo com uma chave e para remov-lo do registro. Os clientes podem mudar ou mesmo pesquisar o registro em tempo de execuo. Isso permite aos clientes estenderem e fazerem um inventrio do sistema sem necessidade de escrever linhas de cdigo. 2. Implementar a operao Clone. A parte mais difcil do padro Prototype a implementao correta da operao Clone. Ela particularmente difcil quando as estruturas de objetos contm referncias circulares. A maioria das linguagens fornece algum suporte para clonagem de objetos. Por exemplo, Smalltalk fornece uma implementao de copy que herdada por todas as subclasses de Object. C++ fornece um constructor copy. Mas estes recursos no resolvem o problema shallow copy versus deep copy (cpia por referncia versus cpia por replicao) [GR83]. Ou seja, clonar objetos significa clonar suas variveis de instncia, ou o clone e o original simplesmente compartilham as variveis? Uma shallow copy simples e, na maior parte das vezes, suficiente, e o que o Smalltalk fornece por omisso. O constructor de cpias por omisso em C++ faz uma cpia membro a membro, o que significa que os apontadores sero compartilhados entre a cpia e o original. Porm, clonar prottipos com estruturas complexas normalmente exige uma cpia por replicao (deep copy), porque o clone e o original devem ser independentes. Portanto, voc deve garantir que os componentes do clone so clones dos componentes do prottipo. A clonagem fora a decidir o que, se for o caso, ser compartilhado. Se os objetos no sistema fornecem operaes de Salvar e Carregar, ento voc pode us-las para fornecer uma implementao por omisso de Clone simplesmente salvando o objeto e carregando-o de volta imediatamente. A operao Salvar salva o objeto num buffer de memria, e a operao Carregar cria uma cpia por reconstruo do objeto a partir do buffer. 3. Iniciar clones. Enquanto alguns clientes ficam perfeitamente contentes com o clone tal como ele , outros desejaro iniciar alguns ou todos os seus estados internos com valores de sua escolha. Voc geralmente no pode passar esses valores para operao Clone porque o seu nmero variar entre as classes de prottipo. Alguns prottipos podem necessitar de mltiplos parmetros de inicializao; outros no necessitaro

126

CAPTULO 3 PADRES DE CRIAO

de nenhum. Passar parmetros para a operao Clone impede uma interface uniforme de clonagem. Pode ser que suas classes-prottipo j definam operaes para (re)estabelecer estados-chave. Caso isso acontea, os clientes podem usar essas operaes imediatamente aps a clonagem. Se isso no acontecer, ento voc pode ter que introduzir uma operao Initialize (ver a seo de Exemplo de Cdigo) que recebe parmetros de inicializao como argumentos e estabelece o estado interno do clone de acordo. Cuidado com as operaes clone que usam replicao (deep copying) as cpias podem ter que ser deletadas (ou explicitamente, ou dentro de Initialize) antes de voc reinicializ-las.

Exemplo de cdigo
Definiremos uma subclasse MazePrototypeFactory da classe MazeFactory (pgina 100). MazePrototypeFactory ser iniciada com prottipos dos objetos que criar, de maneira que no tenhamos que criar subclasses somente para mudar as classes de paredes ou salas que ela cria. A MazePrototypeFactory aumenta a interface de Maze Factory com um constructor que aceita os prottipos como argumentos:

O novo constructor simplesmente inicia seus prottipos:

As funes-membro para a criao de paredes, salas e portas so semelhantes: cada uma clona um prottipo e ento o inicia. Aqui esto as definies de MakeWall e MakeDoor:

PADRES DE PROJETO

127

Podemos usar MazePrototypeFactory para criar um labirinto-prottipo (ou um labirinto por omisso) simplesmente iniciando-o com prottipos dos componentes bsicos de labirinto:

Para mudar o tipo de labirinto, iniciamos MazePrototypeFactory com um conjunto diferente de prottipos. A seguinte chamada cria um labirinto com uma BombedDoor e um RoomWithABomb:

Um objeto que pode ser usado como um prottipo, tal como uma instncia de
Wall, deve suportar a operao Clone. Ele tambm deve ter um constructor de cpias

para fazer a clonagem. Tambm pode necessitar de uma operao separada para a reinicializao do estado interno. Acrescentaremos a operao Initialize Door para permitir aos clientes inicializarem as salas do clone. Compare a seguinte definio de Door com da pgina 93.

128

CAPTULO 3 PADRES DE CRIAO

A subclasse BombedWall deve redefinir Clone e implementar um constructor de cpias correspondente.

Embora BombedWall::Clone retorne um Wall*, sua implementao retorna um ponteiro para uma nova instncia de uma subclasse, qual seja, um BombedWall*. Definimos Clone desta maneira na classe-base para nos assegurarmos de que os clientes que clonam o prottipo no tenham que conhecer suas subclasses concretas. Clientes nunca deveriam precisar fazer um downcast do valor de retorno de Clone para o tipo desejado. Em Smalltalk, voc pode reutilizar o mtodo-padro copy herdado de Object para clonar qualquer MapSite. Voc pode usar MazeFactory para produzir os prottipos de que necessita; por exemplo, pode criar uma sala fornecendo o nome #room. A MazeFactory (fbrica de labirintos) tem um dicionrio que mapeia nomes aos prottipos. Seu mtodo make: se parece com o seguinte:

Tendo mtodos apropriados para inicia a MazeFactory com prottipos, voc poderia criar um labirinto simples com o seguinte cdigo:

onde a definio do mtodo de classe on: para CreateMaze seria

PADRES DE PROJETO

129

Usos conhecidos
Talvez o primeiro exemplo do padro Prototype se encontre no sistema Sketchpad de Ivan Sutherland [Sut63]. A primeira aplicao amplamente conhecida do padro numa linguagem orientada a objeto foi em ThingLab, na qual os usurios poderiam formar um objeto composto e ento promov-lo a um prottipo pela sua instalao numa biblioteca de objetos reutilizveis [Bor81]. Goldberg e Robson mencionam prottipos como um padro [GR83], mas Coplien [Cop92] fornece uma descrio muito mais completa. Ele descreve idiomas relacionados ao padro prototype para C++ e d muitos exemplos e variaes. O Etgdb um depurador (debugger) de front-end, baseado em ET++, que fornece uma interface de apontar e clicar para diferentes depuradores orientados a linhas. Cada depurador tem uma subclasse DebuggerAdaptor correspondente. Por exemplo, GdbAdaptor adapta o etgdb sintaxe dos comandos do gdb de GNU, enquanto que SunDbxAdaptor adapta o etgdb ao depurador da Sun. O Etgdb no tem um conjunto de classes DebuggerAdaptor codificadas rigidamente nele prprio. Em vez disso, l o nome do adaptor a ser usado de uma varivel fornecida pelo ambiente, procura um prottipo com o nome especificado em uma tabela global e, ento, clona o prottipo. Novos depuradores podem ser acrescentados ao etgdb ligando-o ao DebuggerAdaptor que funciona para um depurador novo. A biblioteca de tcnicas de interaes, no ModeComposer, armazena prottipos de objetos que suportam vrias tcnicas de interao [Sha90]. Qualquer tcnica de interao criada pelo Mode Composer pode ser usada como um prottipo colocando-a nesta biblioteca. O padro Prototype permite ao Mode Composer suportar um conjunto ilimitado de tcnicas de interao. O exemplo do editor musical discutido anteriormente se baseia no framework para desenhos do Unidraw [VL90].

Padres relacionados
Prototype e Abstract Factory (95) so padres que competem entre si em vrias situaes, como discutimos no final deste captulo. Porm, eles tambm podem ser usados em conjunto. Um Abstract Factory pode armazenar um conjunto de prottipos a partir dos quais podem ser clonados e retornados objetos-produto.

130

CAPTULO 3 PADRES DE CRIAO

Projetos que utilizam intensamente os padres Composite (160) e Decorator (170) tambm podem se beneficiar do uso do Prototype.

SINGLETON
Inteno

criao de objetos

Garantir que uma classe tenha somente uma instncia e fornecer um ponto global de acesso para a mesma.

Motivao
importante para algumas classes ter uma, e apenas uma, instncia. Por exemplo, embora possam existir muitas impressoras em um sistema, deveria haver somente um spooler de impressoras. Da mesma forma, deveria haver somente um sistema de arquivos e um gerenciador de janelas. Um filtro digital ter somente um conversor A/ D. Um sistema de contabilidade ser dedicado a servir somente a uma companhia. Como garantimos que uma classe tenha somente uma instncia e que essa instncia seja facilmente acessvel? Uma varivel global torna um objeto acessvel, mas no impede voc de instanciar mltiplos objetos. Uma soluo melhor seria tornar a prpria classe responsvel por manter o controle da sua nica instncia. A classe pode garantir que nenhuma outra instncia seja criada (pela interceptao das solicitaes para criao de novos objetos), bem como pode fornecer um meio para acessar sua nica instncia. Este o padro Singleton.

Aplicabilidade
Use o padro Singleton quando: for preciso haver apenas uma instncia de uma classe, e essa instncia tiver que dar acesso aos clientes atravs de um ponto bem conhecido; a nica instncia tiver de ser extensvel atravs de subclasses, possibilitando aos clientes usar uma instncia estendida sem alterar o seu cdigo.

Estrutura
Singleton static Instance() SingletonOperation() GetSingletonData() return uniqueInstance

static uniqueInstance singletonData

PADRES DE PROJETO

131

Participantes
Singleton define uma operao Instance que permite aos clientes acessarem sua nica instncia. Instance uma operao de classe (ou seja, em Smalltalk um mtodo de classe e em C++ uma funo-membro esttica). pode ser responsvel pela criao da sua prpria instncia nica.

Colaboraes
Os clientes acessam uma instncia Singleton unicamente pela operao Instance do Singleton.

Conseqncias
O padro Singleton apresenta vrios benefcios: 1. Acesso controlado instncia nica. Como a classe Singleton encapsula a sua nica instncia, possui controle total sobre como e quando os clientes a acessam. 2. Espao de nomes reduzido. O padro Singleton representa uma melhoria em relao ao uso de variveis globais. Ele evita a poluio do espao de nomes com variveis globais que armazenam instncias nicas. 3. Permite um refinamento de operaes e da representao. A classe Singleton pode ter subclasses e fcil configurar uma aplicao com uma instncia dessa classe estendida. Voc pode configurar a aplicao com uma instncia da classe de que necessita em tempo de execuo. 4. Permite um nmero varivel de instncias. O padro torna fcil mudar de idia, permitindo mais de uma instncia da classe Singleton. Alm disso, voc pode usar a mesma abordagem para controlar o nmero de instncias que a aplicao utiliza. Somente a operao que permite acesso instncia de Singleton necessita ser mudada. 5. Mais flexvel do que operaes de classe. Uma outra maneira de empacotar a funcionalidade de um singleton usando operaes de classe (ou seja, funes-membro estticas em C++ ou mtodos de classe em Smalltalk). Porm, as tcnicas de ambas as linguagens tornam difcil mudar um projeto para permitir mais que uma instncia de uma classe. Alm disso, as funesmembro estticas em C++ nunca so virtuais, o que significa que as subclasses no podem redefini-las polimorficamente.

Implementao
A seguir apresentamos tpicos de implementao a serem considerados ao usar o padro Singleton: 1. Garantindo uma nica instncia. O padro Singleton torna a instncia nica uma instncia normal de uma classe, mas essa classe escrita de maneira que somente uma instncia possa ser criada. Uma forma comum de fazer isso ocultando a operao que cria a instncia usando uma operao de classe (isto , ou uma funo-membro esttica ou

132

CAPTULO 3 PADRES DE CRIAO

um mtodo de classe) que garanta que apenas uma nica instncia seja criada. Esta operao tem acesso varivel que mantm a nica instncia, e garante que a varivel seja iniciada com a instncia nica antes de retornar ao seu valor. Esta abordagem assegura que um singleton seja criado e iniciado antes da sua primeira utilizao. Em C++, voc pode definir a operao de classe com uma funo-membro esttica Instance da classe Singleton. Singleton tambm define uma varivel-membro esttica _instance que contm um apontador para sua nica instncia. A classe Singleton declarada como

A implementao correspondente a seguinte

Os clientes acessam o singleton atravs da funo membro Instance. A varivel _instance iniciada com 0, e a funo-membro esttica Instance retorna o seu valor, iniciando-a com a nica instncia se ele for 0. Instance usa lazy initialization; o valor que ela retorna no criado e armazenado at ser acessado pela primeira vez. Note que o constructor protegido. Um cliente que tenta instanciar Singleton diretamente obter como resposta um erro em tempo de compilao. Isto assegura que somente uma instncia possa ser criada. Alm do mais, uma vez que _instance um apontador para um objeto Singleton, a funo-membro Instance pode atribuir um apontador para uma subclasse de Singleton para esta varivel. Daremos um exemplo do que dissemos aqui na seo Exemplo de cdigo. H uma outra coisa a ser observada sobre a implementao em C++. No suficiente definir o singleton como um objeto global ou esttico, confiando numa inicializao automtica. Existem trs razes para isto: (a) no podemos garantir que somente uma instncia de um objeto esttico ser declarada; (b) talvez no tenhamos informao suficiente para instanciar cada singleton em tempo de inicializao esttica. Um singleton pode necessitar de valores que so computados mais tarde, durante a execuo do programa;

PADRES DE PROJETO

133

(c) C++ no define a ordem pela qual os constructors para objetos globais so chamados entre unidades de compilao [ES90]. Isso significa que no podem existir dependncias entre singletons; se alguma existir, ento inevitvel a ocorrncia de erro. Uma deficincia adicional (embora pequena) da abordagem objeto global/ esttico que ela fora a criao de todos singletons, quer sejam usados ou no. O uso de uma funo-membro esttica evita todos estes problemas. Em Smalltalk, a funo que retorna a instncia nica implementada como um mtodo de classe da classe Singleton. Para garantir que somente uma instncia seja criada, redefine-se a operao new. A classe Singleton resultante pode ter os seguintes mtodos de classe, em que SoleInstance uma varivel de classe que no usada em nenhum outro lugar:

2. Criando subclasses da classe Singleton. O ponto principal no a definio da subclasse, mas sim a instalao da sua nica instncia de maneira que os clientes possam ser capazes de us-la. Em essncia, a varivel que referencia a instncia do singleton deve ser iniciada com uma instncia da subclasse. A tcnica mais simples determinar qual singleton voc quer usar na operao Instance do Singleton. Um exemplo na seo de Exemplo mostra como implementar essa tcnica com variveis do ambiente (operacional). Uma outra maneira de escolher a subclasse de Singleton retirar a implementao de Instance da classe-me (por exemplo, MazeFactory) e coloc-la na subclasse. Isto permite a um programador C++ decidir a classe do singleton em tempo de Linkedio (link-time), mantendo-a oculta dos seus clientes (por exemplo, fazendo a ligao com um arquivo-objeto que contm uma implementao diferente). A soluo da ligao fixa a escolha da classe do singleton em tempo de linkedio, o que torna difcil escolher a classe do singleton em tempo de execuo. O uso de instrues condicionais para determinao da subclasse mais flexvel, porm codifica de maneira rgida o conjunto das classes Singleton possveis. Nenhuma abordagem flexvel o bastante em todos os casos. Uma abordagem mais flexvel utiliza um sistema de registro de singletons (registry of singletons). Em vez de ter Instance definindo o conjunto das classes Singleton possveis, as classes Singleton podem registrar suas instncias singleton por nome, num sistema de registro de conhecimento geral. O sistema de registro associa nomes e singletons. Os nomes so constitudos de cadeias de caracteres. Quando Instance necessita um singleton, ela consulta o sistema de registro, procurando o singleton pelo nome. O sistema de registro procura o singleton correspondente (se existir) e o retorna ao cliente. Essa soluo libera Instance da necessidade de ter que conhecer todas as possveis classes ou instncias do Singleton. Tudo o que necessrio uma interface comum para todas as classes Singleton, que inclua operaes de registro:

134

CAPTULO 3 PADRES DE CRIAO

Register registra a instncia de Singleton com um nome fornecido. Para

manter o registro simples, necessitaremos que armazene uma lista de objetos


NameSingletonPair. Cada NameSingletonPair mapeia (associa) um nome a um singleton. Dado um nome, a operao Lookup encontra o singleton corres-

pondente. Assumiremos que uma varivel do ambiente especifica o nome do singleton desejado.

usurio ou ambiente fornece esse valor no incio da execuo Lookup retorna 0 se no h qualquer singleton com o nome verificado

Onde as classes Singleton registram a si mesmas? Uma possibilidade fazlo no seu constructor. Por exemplo, uma subclasse MySingleton poderia fazer o seguinte:

Naturalmente, o construtor no ser chamado a menos que algum instancie a classe, o que repete o problema que o padro Singleton est tentando resolver! Ns podemos contornar este problema em C++ atravs da definio de uma instncia esttica de MySingleton. Por exemplo, podemos definir

no arquivo que contm a implementao de MySingleton. A classe Singleton no mais responsvel pela criao do singleton. Em vez disso, sua responsabilidade primria tornar acessvel o objeto singleton escolhido no sistema. A soluo que usa o objeto esttico ainda apresenta um problema potencial todas as instncias de todas as subclasses possveis de Singleton devem ser criadas, pois, caso contrrio, no sero registradas.

Exemplo de cdigo
Suponha que definimos uma classe MazeFactory para construir labirintos, conforme descrito na pgina 100. MazeFactory define uma interface para construo de diferen-

PADRES DE PROJETO

135

tes partes de um labirinto. As subclasses podem redefinir as operaes para retornar instncias de classes-produtos especializadas, tal como BombedWall no lugar de simples objetos Wall. O fato relevante aqui que a aplicao Maze necessita somente de uma instncia de uma fbrica de labirintos, e que essa instncia dever estar disponvel para o cdigo que construir qualquer parte do labirinto. a que o padro Singleton entra. Ao tornar MazeFactory um singleton, ns tornamos o objeto-labirinto (maze) acessvel globalmente sem recorrer a variveis globais. Para simplificar, suponhamos que nunca criaremos subclasses de MazeFactory (a alternativa ser considerada mais frente). Ns tornamos MazeFactory uma classe Singleton em C++, acrescentando uma operao esttica Instance e um membro esttico _instance para conter a nica instncia existente. Tambm devemos proteger o constructor para prevenir instanciaes acidentais, as quais nos levariam a ter mais que uma instncia.

interface existente vai aqui

A implementao correspondente

Agora verificaremos o que acontece quando existem subclasses de MazeFactory e a aplicao tem que decidir qual delas usar. Selecionaremos o tipo de labirinto atravs de uma varivel do ambiente e acrescentaremos o cdigo que instancia a subclasse apropriada de MazeFactory com base no valor da varivel do ambiente. Um bom lugar para colocar este cdigo a operao Instance, porque ela j instancia MazeFactory:

136

CAPTULO 3 PADRES DE CRIAO

Note que Instance deve ser modificada toda vez que voc define uma nova subclasse de MazeFactory. Isso pode no ser um problema nesta aplicao, mas pode ser um problema para as fbricas abstratas definidas num framework. Uma soluo possvel seria usar a tcnica do uso de um sistema de registro descrita na seo Implementao. A ligao dinmica (dynamic linking) poderia tambm ser til aqui ela evitaria que a aplicao tivesse que carregar para a memria todas as subclasses que no so usadas.

Usos conhecidos
Um exemplo do padro Singleton em Smalltalk-80 [Par90] o conjunto de mudanas no cdigo efetuado por ChangeSet current. Um exemplo mais sutil o relacionamento entre classes e suas metaclasses. Uma metaclasse a classe de uma classe, e cada metaclasse tem uma instncia. As metaclasses no tm nomes (exceto indiretamente, atravs do nome da sua nica instncia), mas registram e acompanham a sua nica instncia, e normalmente no criaro outra. O toolkit para construo de interfaces de usurio InterViews [LCI+92] usa o padro Singleton para acessar as nicas instncias de suas classes Session e WidgetKit, entre outras. Session define o ciclo de eventos disparveis da aplicao principal, armazena o banco de dados das preferncias de estilo do usurio e administra conexes para um ou mais dispositivos fsicos de display. WidgetKit uma Abstract Factory (95) para definir os widgets de estilo de interao. A operao WidgetKit::instance determina a subclasse especfica de WidgetKit que instanciada baseada numa varivel de ambiente que Session define. Uma operao similar em Session determina se so suportados displays monocromticos ou coloridos e configura a instncia singleton de Session de acordo.

Padres relacionados
Muitos padres podem ser implementados usando Singleton. Ver Abstract Factory (95), Builder (104) e Prototype (121).

Discusso sobre padres de criao


Existem duas maneiras comuns de parametrizar um sistema pelas classes de objetos que ele cria. Uma criar subclasses da classe que cria os objetos; isto corresponde a usar o padro Factory Method (112). A principal desvantagem desta soluo que requer a criao de uma nova subclasse somente para mudar a classe do produto. Tais mudanas podem gerar uma cascata de modificaes encadeadas. Por exemplo, quando o criador do produto ele prprio, criado por um mtodo fbrica, ento voc tem que redefinir tambm o seu criador. A outra forma de parametrizar um sistema baseia-se mais na composio de objetos: defina um objeto que seja responsvel por conhecer a classe dos objetosproduto e torne-o um parmetro do sistema. Este o aspecto-chave dos padres Abstract

PADRES DE PROJETO

137

Factory (95), Builder (104) e Prototype (121). Todos os trs padres envolvem a criao de um novo objeto-fbrica cuja responsabilidade criar objetos-produtos. Em Abstract Factory, o objeto-fbrica produz objetos de vrias classes. Em Builder, um objeto-fbrica constri incrementalmente um objeto complexo usando um protocolo igualmente complexo. O padro Prototype faz o objeto-fbrica construir um objeto-produto copiando um objeto prototpico. Neste caso, o objeto-fbrica e o prottipo so o mesmo objeto, porque o prottipo responsvel por retornar o produto. Considere o framework para um editor de desenhos descrito no padro Prototype. H vrias maneiras de parametrizar uma GraphicTool pela classe do produto: Aplicando-se o padro Factory Method, uma subclasse de GraphicTool ser criada para cada subclasse de Graphic na paleta. A GraphicTool ter uma nova operao NewGraphic, que cada subclasse de GraphicTool redefinir. Aplicando-se o padro Abstract Factory, haver uma hierarquia de classes de GraphicsFactories, uma para cada subclasse de Graphic. Neste caso, cada fbrica cria apenas o produto: CircleFactory criar crculos (Circles), LineFactory criar linhas (Lines), e assim por diante. Uma GraphicTool ser parametrizada como uma fbrica para criao do tipo apropriado de Graphics. Aplicando-se o padro Prototype, cada subclasse de Graphics implementar a operao Clone, e uma GraphicTool ser parametrizada com um prottipo da Graphic que ela cria. Definir qual o melhor padro depende de muitos fatores. No nosso framework para editores de desenhos, o padro Factory Method inicialmente mais fcil de usar. fcil definir uma subclasse de GraphicTool e as instncias de GraphicTool so criadas somente quando a paleta definida. Aqui, a principal desvantagem a proliferao de subclasses de GraphicTool, sendo que nenhuma delas faz muita coisa. O padro Abstract Factory no oferece uma grande melhoria porque tambm exige uma hierarquia de classes GraphicsFactory bastante grande. Abstract Factory seria prefervel a Factory Method somente se j houvesse uma hierarquia de classes GraphicsFactory ou se o compilador a fornecesse automaticamente (como em Smalltalk ou Objective C) ou se fosse necessria em outra parte do sistema. No geral, o padro Prototype o melhor para o framework de editores de desenho porque ele somente requer a implementao de uma operao Clone em cada classe Graphics. Isso reduz o nmero de classes, e clone pode ser usado para outras finalidades, alm de somente instanciao (por exemplo, uma operao duplicar definida no menu). O Factory Method torna um projeto mais adaptvel e apenas um pouco mais complicado. Outros padres de projeto requerem novas classes, enquanto que Factory Method somente exige uma nova operao. As pessoas freqentemente usam Factory Method como a maneira padro de criar objetos, mas no necessrio quando a classe que instanciada nunca muda ou quando a instanciao ocorre em uma operao que subclasses podem facilmente redefinir, tal como uma operao de inicializao. Projetos que usam Abstract Factory, Prototype ou Builder so ainda mais flexveis do que aqueles que utilizam Factory Method, porm, eles tambm so mais complexos. Freqentemente, os projetos comeam usando Factory Method e evoluem para outros

138

CAPTULO 3 PADRES DE CRIAO

padres de criao medida que o projetista descobre onde necessria maior flexibilidade. O conhecimento de vrios padres de projeto lhe d mais opes quando trocar um critrio de projeto por outro.

Notas
1

Tais aplicaes refletem os padres Composite (160) e Decorator (170).

4
Padres estruturais
Os padres estruturais se preocupam com a forma como classes e objetos so compostos para formar estruturas maiores. Os padres estruturais de classes utilizam a herana para compor interfaces ou implementaes. Dando um exemplo simples, considere como a herana mltipla mistura duas ou mais classes em uma outra. O resultado uma classe que combina as propriedades das suas classes ancestrais. Esse padro particularmente til para fazer bibliotecas de classes desenvolvidas independentemente trabalharem juntas. Um outro exemplo a forma de classe do padro Adapter (140). Em geral, um Adapter faz com que uma interface adaptada (em ingls, adaptee) seja compatvel com outra, dessa forma fornecendo uma abstrao uniforme de diferentes interfaces. A classe adaptadora (adapter) atinge esse objetivo herdando, privadamente, de uma classe adaptada. O adapter, ento, exprime sua interface em termos da interface da classe adaptada. Em lugar de compor interfaces ou implementaes, os padres estruturais de objetos descrevem maneiras de compor objetos para obter novas funcionalidades. A flexibilidade obtida pela composio de objetos provm da capacidade de mudar a composio em tempo de execuo, o que impossvel com a composio esttica de classes. O Composite (160) um exemplo de um padro estrutural de objetos. Ele descreve como construir uma hierarquia de classes composta para dois tipos de objetos: primitivos e compostos. Os objetos compostos permitem compor objetos primitivos e outros objetos compostos em estruturas arbitrariamente complexas. No padro Proxy (198), um procurador funciona como um substituto ou um marcador para outro objeto. Um proxy (procurador) pode ser usado de vrias formas. Ele pode atuar como um representante local para um objeto situado num espao de endereo remoto. Pode representar um grande objeto que deveria ser carregado por demanda. Pode proteger o acesso a um objeto sensvel. Proxies fornecem um nvel de referncia indireta a propriedades especficas de objetos. Da eles poderem restringir, aumentar ou alterar essas propriedades.

140

CAPTULO 4 PADRES ESTRUTURAIS

O padro Flyweight (187) define uma estrutura para o compartilhamento de objetos. Os objetos so compartilhados por pelo menos duas razes: eficincia e consistncia. O Flyweight focaliza o compartilhamento para uso eficiente de espao. As aplicaes que usam uma poro de objetos devem prestar ateno no custo de cada objeto. Pode-se obter economia substancial e usando o compartilhamento de objetos, em lugar de replic-los. Mas objetos podem ser compartilhados somente se eles no definem estados dependentes do contexto. Objetos Flyweight no possuem tais estados. Qualquer informao adicional de que necessitem para executar suas tarefas passada aos mesmos quando necessrio. No tendo estados dependentes de contexto, os objetos Flyweight podem ser compartilhados livremente. Enquanto o Flyweight mostra o que fazer com muitos objetos pequenos, Faade (179) mostra como fazer um nico objeto representar todo um subsistema. Um objeto faade (fachada) uma representao para um conjunto de objetos. Faade executa suas responsabilidades repassando mensagens para os objetos que ela representa. O padro Bridge (151) separa a abstrao de um objeto da sua implementao, de maneira que elas possam variar independentemente. O Decorator (170) descreve como acrescentar dinamicamente responsabilidades ao objetos. O Decorator um padro estrutural que compe objetos recursivamente para permitir um nmero ilimitado de responsabilidades adicionais. Por exemplo, um objeto Decorator que contm um componente de uma interface de usurio pode adicionar uma decorao, como uma borda ou sombra, ao componente, ou pode adicionar uma funcionalidade como rolamento ou zoom. Podemos adicionar duas decoraes simplesmente encaixando um objeto Decorator dentro do outro, e assim por diante, para outras decoraes adicionais. Para conseguir isto, cada objeto Decorator deve oferecer a mesma interface do seu componente e repassar mensagens para ele. O Decorator pode executar o seu trabalho (tal como desenhar uma borda em volta do componente) antes ou depois de repassar uma mensagem. Muitos padres estruturais esto relacionados de alguma forma. Discutiremos estes relacionamentos no final do captulo.

ADAPTER
Inteno

estrutural de classes e de objetos

Converter a interface de uma classe em outra interface, esperada pelos clientes. O Adapter permite que classes com interfaces incompatveis trabalhem em conjunto o que, de outra forma, seria impossvel.

Tambm conhecido como


Wrapper

Motivao
Algumas vezes, uma classe de um toolkit, projetada para ser reutilizada no reutilizvel porque sua interface no corresponde interface especfica de um domnio requerida por uma aplicao.

PADRES DE PROJETO

141

Considere, por exemplo, um editor de desenhos que permite aos usurios desenhar e arranjar elementos grficos (linhas, polgonos, texto, etc.) em figuras e diagramas. A abstrao-chave do editor de desenhos o objeto grfico, o qual tem uma forma editvel e pode desenhar a si prprio. A interface para objetos grficos definida por uma classe abstrata chamada Shape. O editor define uma subclasse de Shape para cada tipo de objeto grfico: uma classe LineShape para linhas, uma classe PolygonShape para polgonos, e assim por diante. Classes para formas geomtricas elementares, como LineShape e PolygonShape, so bastante fceis de ser implementadas porque as suas capacidades de desenho e edio so inerentemente limitadas. Mas uma subclasse TextShape que pode exibir e editar textos mais difcil de ser implementada, uma vez que mesmo a edio bsica de textos envolve atualizaes complicadas de tela e gerncia de buffer. Entretanto, pode j existir um toolkit para construo de interfaces de usurios, o qual j oferece uma sofisticada classe TextView para a exibio e edio de textos. Idealmente, gostaramos de reutilizar TextView para implementar TextShape, porm, o toolkit no foi projetado levando classes Shape em considerao. Assim, no podemos usar de maneira intercambivel objetos TextView e Shape. Como possvel que classes existentes e no-relacionadas, como TextView, funcionem em uma aplicao que espera classes com uma interface diferente e incompatvel? Poderamos mudar a classe TextView de maneira que ela fosse coerente com a interface de Shape, porm, a menos que tenhamos o cdigo-fonte do toolkit, essa opo no vivel. Mesmo que tivssemos o cdigo-fonte, no teria sentido mudar TextView; o toolkit no deveria ter que adotar interfaces especficas de domnios somente para fazer com que uma aplicao funcione. Em vez disso, poderamos definir TextShape de maneira que ele adapte a interface de TextView quela de Shape. Podemos fazer isto de duas maneiras: (1) herdando a interface de Shape e a implementao de TextView, ou (2) compondo uma instncia de TextView dentro de uma TextShape e implementando TextShape em termos da interface de TextView. Essas duas abordagens correspondem s verses do padro Adapter para classes e para objetos. Chamamos TextShape um adaptador.

Este diagrama ilustra o caso de um adaptador para objetos. Ele mostra como solicitaes de BoundingBox, declarada na classe Shape, so convertidas em solicitaes para GetExtent, definida em TextView. Uma vez que TextShape adapta Text View interface de Shape, o editor de desenhos pode reutilizar a classe TextView que seria incompatvel de outra forma.

142

CAPTULO 4 PADRES ESTRUTURAIS

Freqentemente, um adaptador responsvel por funcionalidades no oferecidas pela classe adaptada. O diagrama mostra como um adaptador pode atender tais responsabilidades. O usurio deveria ser capaz de arrastar cada objeto Shape para uma nova posio de forma interativa, porm, TextView no est projetada para fazer isso. TextShape pode acrescentar essa funo atravs da implementao da operao CreateManipulator, de Shape, a qual retorna uma instncia da subclasse Manipulator apropriada. Manipulator uma classe abstrata para objetos que sabem como animar um Shape em resposta entrada de usurio, tal como arrastar a forma geomtrica para uma nova localizao. Existem subclasses de Manipulator para diferentes formas; por exemplo, TextManipulator a subclasse correspondente para TextShape. Pelo retorno de uma instncia de TextManipulator, TextShape acrescenta a funcionalidade que Shape necessita mas que TextView no tem.

Aplicabilidade
Use o padro Adapter quando: voc quiser usar uma classe existente, mas sua interface no corresponder interface de que necessita; voc quiser criar uma classe reutilizvel que coopere com classes no-relacionadas ou no-previstas, ou seja, classes que no necessariamente tenham interfaces compatveis; (somente para adaptadores de objetos) voc precisar usar vrias subclasses existentes, porm, for impraticvel adaptar essas interfaces criando subclasses para cada uma. Um adaptador de objeto pode adaptar a interface da sua classe-me.

Estrutura
Um adaptador de classe usa a herana mltipla para adaptar uma interface outra:
Client

Target Request()

Adaptee SpecificRequest()

(implementation) Adapter Request() SpecificRequest()

Um adaptador de objeto depende da composio de objetos:


Client

Target Request()

Adaptee SpecificRequest()

adaptee Adapter Request() adaptee>SpecificRequest()

PADRES DE PROJETO

143

Participantes
Target (Shape) define a interface especfica do domnio que Client usa. Client (DrawingEditor) colabora com objetos compatveis com a interface de Target. Adaptee (TextView) define uma interface existente que necessita ser adaptada. Adapter (TextShape) adapta a interface do Adaptee interface de Target.

Colaboraes
Os clientes chamam operaes em uma instncia de Adapter. Por sua vez, o adapter chama operaes de Adaptee que executam a solicitao.

Conseqncias
Os adaptadores de classes e de objetos tm diferentes solues de compromisso. Um adaptador de classe: adapta Adaptee a Target atravs do uso efetivo de uma classe Adapter concreta. Em conseqncia, um adaptador de classe no funcionar quando quisermos adaptar uma classe e todas as suas subclasses; permite a Adapter substituir algum comportamento do Adaptee, uma vez que Adapter uma subclasse de Adaptee; introduz somente um objeto, e no necessrio endereamento indireto adicional por ponteiros para chegar at o Adaptee. Um adaptador de objeto: permite a um nico Adapter trabalhar com muitos Adaptees isto , o Adaptee em si e todas as suas subclasses (se existirem). O Adapter tambm pode acrescentar funcionalidade a todos os Adaptees de uma s vez; torna mais difcil redefinir um comportamento de Adaptee. Ele exigir a criao de subclasses de Adaptee e far com que Adapter referencie a subclasse ao invs do Adaptee em si. Aqui apresentamos outros pontos a serem considerados quando usamos o padro Adapter: 1. Quanta adaptao Adapter faz? Os Adapters variam no volume de trabalho que executam para adaptar o Adaptee interface de Target. Existe uma variao do trabalho possvel, desde a simples converso de interface por exemplo, mudar os nomes das operaes at suportar um conjunto de operaes inteiramente diferente. O volume de trabalho que o Adapter executa depende de quo similar a interface de Target a dos seus Adaptees. 2. Adaptadores conectveis (pluggable). Uma classe mais reutilizvel quando voc minimiza as suposies que outras classes devem fazer para utiliz-la. Atravs da construo da adaptao de interface em uma classe, voc elimina a suposio de que outras classes vm a mesma interface. Dito de outra maneira,

144

CAPTULO 4 PADRES ESTRUTURAIS

a adaptao de interfaces permite incorporar a nossa classe a sistemas existentes que podem estar esperando interfaces diferentes para a classe. ObjectWorks/ Smalltalk [Par90] usa o termo pluggable adapter para descrever classes com adaptao de interfaces incorporadas. Considere um widget TreeDisplay que pode exibir graficamente estruturas de rvore. Se este fosse um widget com uma finalidade especial para uso em apenas uma aplicao, ento, poderamos requerer uma interface especfica dos objetos que ele exibisse; ou seja, todos deveriam descender de uma classe abstrata Tree. Mas se quisssemos tornar TreeDisplay mais reutilizvel (digamos que quisssemos torn-la parte de um toolkit de widgets teis), ento essa exigncia no seria razovel. Aplicaes definiro suas prprias classes para estruturas de rvore. Elas no deveriam ser foradas a usar a nossa classe abstrata Tree. Diferentes estruturas de rvores tero diferentes interfaces. Por exemplo, numa hierarquia de diretrio, os descendentes podem ser acessados com uma operao GetSubdirectories, enquanto que numa hierarquia de herana, a operao correspondente poderia ser chamada GetSubclasses. Um widget TreeDisplay reutilizvel deve ser capaz de exibir ambos os tipos de hierarquias ainda que usem interfaces diferentes. Em outras palavras, TreeDisplay deveria ter uma adaptao de interface incorporada a ele. Examinaremos diferentes maneiras de construir adaptaes de interfaces, dentro de classes, na seo Implementao. 3. Utilizao de adaptadores de dois sentidos para fornecer transparncia. Um problema potencial com adaptadores decorre do fato de que eles no so transparentes para todos os clientes. Um objeto adaptado no oferece a interface do objeto original, por isso ele no pode ser usado onde o original o for. Adaptadores de dois sentidos (two-way adapters) podem fornecer essa transparncia. Eles so teis quando dois clientes diferentes necessitam ver um objeto de forma diferente. Considere o adaptador de dois sentidos que integra o Unidraw, um framework para editores grficos [VL90], e QOCA, um toolkit para soluo de restries [HHMV92]. Ambos os sistemas possuem classes que representam variveis explicitamente: Unidraw tem StateVariable e QOCA tem Constraint Variable. Para fazer com que Unidraw trabalhe com QOCA, Constraint Variable deve ser adaptada a StateVariable; para permitir que QOCA propague solues para Unidraw, StateVariable deve ser adaptada a ConstraintVariable.

PADRES DE PROJETO

145

A soluo envolve o uso de um adaptador de classe ConstraintStateVariable de dois sentidos, uma subclasse tanto de StateVariable como de ConstraintVariable que adapta as duas interfaces uma outra. Neste caso, a herana mltipla uma soluo vivel porque as interfaces das classes adaptadas so substancialmente diferentes. O adaptador de classe de dois sentidos compatvel com ambas as classes adaptadas, podendo funcionar em ambos os sistemas.

Implementao
Embora a implementao do padro Adapter seja normalmente simples e direta, apresentamos aqui alguns tpicos a serem sempre considerados: 1. Implementando adaptadores de classe em C++. Numa implementao em C++ uma classe Adapter deveria herdar publicamente de Target e privadamente de Adaptee. Assim, Adapter seria um subtipo de Target, mas no de Adaptee. 2. Adaptadores conectveis. Vamos examinar trs maneiras de implementar adaptadores plugveis para o widget TreeDisplay descrito anteriormente, os quais podem formatar e exibir uma estrutura hierrquica automaticamente. O primeiro passo, comum a todas as trs implementaes discutidas aqui, encontrar uma interface mnima para Adaptee, ou seja, o menor subconjunto de operaes que permite fazer a adaptao. Uma interface mnima, consistindo em somente um par de operaes, mais fcil de adaptar que uma interface com dzias de operaes. Para o TreeDisplay, o adaptee qualquer estrutura hierrquica. Uma interface minimalista pode incluir duas operaes, uma que define como apresentar graficamente um n na estrutura hierrquica e outra que recupera os filhos do n. A interface mnima conduz a trs abordagens de implementao: (a) Utilizando operaes abstratas. Defina na classe TreeDisplay operaes abstratas correspondentes interface mnima de Adaptee. As operaes abstratas devem ser implementadas por subclasses que tambm adaptam o objeto hierarquicamente estruturado. Por exemplo, uma subclasse DirectoryTreeDisplay implementar essas operaes acessando a estrutura do diretrio.
TreeDisplay (Client, Target) GetChildren(Node) CreateGraphicNode(Node) Display() BuildTree(Node n)

GetChildren(n) for each child { AddGraphicNode(CreateGraphicNode(child)) Builde Tree(child) }

DirectoryTreeDisplay (Adapter) GetChildren(Node) CreateGraphicNode(Node) FileSystemEntity (Adaptee)

146

CAPTULO 4 PADRES ESTRUTURAIS

DirectoryTreeDisplay especializa a interface mnima de maneira que possa exibir estruturas de diretrio compostas de objetos FileSystemEntity. (b) Utilizando objetos delegados. Nesta abordagem, o TreeDisplay repassa solicitaes de acesso estrutura hierrquica para um objeto delegado. O TreeDisplay pode usar uma estratgia de adaptao diferente pela substituio de um delegado por outro. Por exemplo, suponha que exista um DirectoryBrowser que usa um TreeDisplay. Directory Browser pode vir a ser um bom delegado para adaptar TreeDisplay estrutura do diretrio hierrquico. Em linguagens com tipos definidos dinamicamente, como Smalltalk ou Objective C, essa abordagem somente exige uma interface para registrar o delegado junto ao adapter. Ento, o TreeDisplay simplesmente repassa as solicitaes para o delegado. NEXTSTEP [Add94] usa essa abordagem intensamente para reduzir o uso de subclasses. As linguagens com tipos estaticamente definidos, como C++, requerem uma definio explcita da interface para o delegado. Podemos especificar tal interface colocando a interface mnima requerida por TreeDisplay em uma classe abstrata TreeAccessorDelegate. Ento, podemos misturar essa interface com o delegado de nossa escolha DirectoryBrowser, neste caso usando herana. Usamos herana simples se o DirectoryBrowse no tiver classe-me, caso contrrio, usamos herana mltipla. Misturar classes dessa maneira mais fcil do que introduzir uma nova subclasse TreeDisplay e implementar suas operaes individualmente.
TreeAccessorDelegate (Target) TreeDisplay (Client)
SetDelegate(Delegate) Display() BuildTree(Node n) delegate

GetChildren(TreeDisplay, Node) CreateGraphicNode(TreeDisplay, Node)

DirectoryBrowser (Adapter) GetChildren(TreeDisplay, Node) CreateGraphicNode(TreeDisplay, Node) CreateFile() Delete()

delegate>GetChildren(this, n) for each child { AddGraphicNodel( delegate>CreateGraphicNode(this, child) ) BuildTree(child) }

FileSystemEntity (Adaptee)

(c) Adapters parametrizados. A maneira usual de suportar adaptadores conectveis em Smalltalk parametrizar um adaptador com um ou mais blocos (Smalltalk). A primitiva block suporta a adaptao sem usar subclasses. Um bloco pode adaptar uma solicitao e o adaptador pode armazenar um bloco para cada solicitao individual. No nosso exemplo, isso significa que

PADRES DE PROJETO

147

TreeDisplay armazena um bloco para converter um n em um GraphicNode e outro bloco para acessar os filhos de um n. Por exemplo, para criar um TreeDisplay em uma hierarquia de diretrio, escrevemos

Se voc est construindo uma adaptao de interface em uma classe, essa abordagem oferece uma alternativa conveniente ao uso de subclasses.

Exemplo de cdigo
Apresentaremos um breve esboo da implementao de adaptadores de classes e de objetos para o exemplo apresentado na seo Motivao, comeando com as classes Shape e TextView.

Shape supe uma caixa delimitadora definida pelos seus cantos opostos. Em contraste, TextView definido por origem, altura e largura. Shape tambm define uma operao CreateManipulator para criar um objeto Manipulator, o qual sabe como animar uma forma quando um usurio a manipula.1 O TextView no tem uma operao equivalente. A classe TextShape um adaptador entre essas diferentes interfaces. Um adaptador de classe utiliza a herana mltipla para adaptar interfaces. O ponto-chave dos adaptadores de classe a utilizao de um ramo de herana para herdar a interface e de outro ramo para herdar a implementao. A maneira usual de fazer essa distino em C++ herdar a interface publicamente e herdar a implementao privadamente. Usaremos essa conveno para definir o adaptador TextShape. A operao BoundingBox converte a interface de TextView para que esta fique de acordo com a interface de Shape.

148

CAPTULO 4 PADRES ESTRUTURAIS

A operao IsEmpty demonstra o repasse direto de solicitaes, comum em implementaes de adaptadores:

Finalmente, definimos CreateManipulator (a qual no suportada por TextView) a partir do zero. Assumimos que j implementamos uma classe TextManipulator que suporta manipulao de um TextShape.

O adaptador de objeto utiliza composio de objetos para combinar classes que tm interfaces diferentes. Nesta abordagem, o adaptador TextShape mantm um apontador para TextView.

TextShape deve iniciar o apontador para instncia de TextView, e ele faz isso no constructor. Ele tambm deve invocar operaes no seu objeto TextView sempre que

PADRES DE PROJETO

149

suas prprias operaes forem invocadas. Neste exemplo, presuma que o cliente crie o objeto TextView e o passe para o constructor de TextShape:

A implementao de CreateManipulator no muda em relao verso para o adaptador de classe, uma vez que implementada do zero e no reutiliza qualquer funcionalidade existente de TextView.

Compare este cdigo com o cdigo do caso do adaptador de classe. O adaptador de objeto exige um pouco mais de esforo para escrever, porm, mais flexvel. Por exemplo, a verso do adaptador de objeto de TextShape funcionar igualmente bem com subclasses de TextView o cliente simplesmente passa uma instncia de uma subclasse de TextView para o constructor de TextShape.

Usos conhecidos
O exemplo da seo Motivao vem do ET++Draw, uma aplicao de desenho baseada na ET++ [WGM88]. O ET++Draw reutiliza as classes para edio de texto da ET++ usando um adapter de classe TextShape. O InterViews 2.6 define uma classe abstrata Interactor para elementos da interface do usurio, tais como barras de rolamento, botes e menus [VL88]. Ele tambm define uma classe abstrata Graphic para objetos grficos estruturados, tais como linhas, crculos, polgonos e splines. Tanto Interactors como Graphics tm aparncias grficas, porm, diferentes interfaces e implementaes (eles no tm uma classe ancestral compartilhada) e so, portanto, incompatveis voc no pode incluir diretamente um objeto grfico estruturado em, por exemplo, uma caixa de dilogo. Em vez disso, o InterViews 2.6 define um adaptador de objeto chamado GraphicBlock, uma subclasse de Interactor que contm uma instncia de Graphic. O GraphicBlock adapta a interface da classe Graphic quela de Interactor. O GraphicBlock permite que uma instncia de Graphic seja exibida, rolada e possa sofrer zooms dentro de uma estrutura Interactor.

150

CAPTULO 4 PADRES ESTRUTURAIS

Os adaptadores conectveis so comuns em ObjectWorks\Smalltalk [Par90]. O Smalltalk-padro define uma classe ValueModel para vises (views) que exibem um nico valor. ValueModel define uma interface value, value: para acessar o valor. Estes so mtodos abstratos. Programadores de aplicao acessam o valor usando nomes mais especficos do domnio, tais como width e width:, mas eles no deveriam ter que criar subclasses de ValueModel para adaptar tais nomes especficos da aplicao interface de ValueModel. Em vez disso, ObjectWorks\Smalltalk inclui uma subclasse de ValueModel chamada PluggableAdaptor. Um objeto PluggableAdaptor adapta outros objetos interface de ValueModel (value, value:). Ela pode ser parametrizada com blocos para obter e atualizar (set) o valor desejado. PluggableAdaptor utiliza internamente estes blocos para implementar a interface value, value:. PluggableAdaptor tambm permite passar nomes no Selector (por exemplo, width, width:) diretamente por convenincia sinttica. Ela converte estes seletores (selectors) nos blocos correspondentes, de forma automtica.
ValueModel value: value

Object

adaptee

PluggableAdaptor value: value getBlock setBlock

^ getBlock value: adaptee

Outro exemplo proveniente de ObjectWorks\Smalltalk a classe TableAdaptor. Uma TableAdaptor pode adaptar uma seqncia de objetos numa apresentao tabular. A tabela exibe um objeto por linha. O cliente parametriza TableAdaptor com um conjunto de mensagens que uma tabela pode usar para obter os valores de coluna de um objeto. Algumas classes no AppKit do NeXT [Add94] usam objetos delegados para executar adaptao de interfaces. Um exemplo a classe NXBrowser que pode exibir listas hierrquicas de dados. NXBrowser usa um objeto delegado para acessar e adaptar dados. O Casamento de Convenincia (Marriage of Convenience), [Mey88] de Meyer uma forma de um adaptador de classe. Meyer descreve como uma classe FixedStack adapta a implementao de uma classe Array interface de uma classe stack. O resultado uma pilha (Stack) que contm um nmero fixo de entradas.

Padres relacionados
O padro Bridge(151) tem uma estrutura similar a um adaptador de objeto, porm, Bridge tem uma inteno diferente: tem por objetivo separar uma interface da sua implementao, de modo que elas possam variar fcil e independentemente. Um adaptador se destina a mudar a interface de um objeto existente. O padro Decorator (170) aumenta outro objeto sem mudar sua interface. Desta forma, um Decorator mais transparente para a aplicao do que um adaptador.

PADRES DE PROJETO

151

Como conseqncia, Decorator suporta a composio recursiva, a qual no possvel com adaptadores puros. O Proxy (198) define um representante ou procuradorpara outro objeto e no muda a sua interface.

BRIDGE
Inteno

estrutural de objetos

Desacoplar uma abstrao da sua implementao, de modo que as duas possam variar independentemente.

Tambm conhecido como


Handle/Body

Motivao
Quando uma abstrao pode ter uma entre vrias implementaes possveis, a maneira usual de acomod-las usando a herana. Uma classe abstrata define a interface para a abstrao, e subclasses concretas a implementam de formas diferentes. Mas essa abordagem nem sempre suficientemente flexvel. A herana liga uma implementao abstrao permanentemente, o que torna difcil modificar, aumentar e reutilizar abstraes e implementaes independentemente. Considere a implementao de uma Janela portvel em um toolkit para construir interfaces de usurios. Por exemplo, essa abstrao deveria nos habilitar a escrever aplicaes que trabalham tanto com o sistema XWindow quanto com o PresentationManager (PM), da IBM. Usando a herana, poderamos definir uma classe abstrata Window e subclasses XWindow e PMWindow que implementam a interface Janela para diferentes plataformas. Porm, essa abordagem tem dois problemas: 1. inconveniente estender a abstrao Window para cobrir diferentes tipos de janela ou novas plataformas. Imagine uma subclasse IconWindow de Window que especializa a abstrao Window para cones. Para suportar IconWindows para ambas as plataformas, temos que implementar duas classes novas, XIconWindow e PMIconWindow. Pior ainda, teremos que definir duas classes para cada tipo de janela. Suportar uma terceira plataforma exige ainda uma outra subclasse de Window para cada tipo de janela.
Window Window

XWindow

PMWindow

XWindow

PMWindow

IconWindow

XIconWindow

PMIconWindow

152

CAPTULO 4 PADRES ESTRUTURAIS

2. Ela torna o cdigo do cliente dependente de plataforma. Sempre que um cliente cria uma janela, instancia uma classe concreta que tem uma implementao especfica. Por exemplo, a criao de um objeto Xwindow amarra a abstrao Window implementao do XWindow, o que torna o cdigo do cliente dependente da implementao do XWindow. Isso, por sua vez, torna mais difcil portar o cdigo do cliente para outras plataformas. Os clientes deveriam ser capazes de criar uma janela sem se prenderem a uma implementao concreta. Somente a implementao da janela deveria depender da plataforma na qual a aplicao executada. Portanto, o cdigo do cliente deveria instanciar janelas sem mencionar plataformas especficas. O padro Bridge trata desses problemas colocando a abstrao Window e sua implementao em hierarquias de classes separadas. Existe somente uma hierarquia de classes para interfaces de janelas (Window, IconWindow, TransientWindow) e uma hierarquia separada para implementaes de janelas especficas das plataformas, tendo como sua raiz WindowImp. Por exemplo, a subclasse XWindowImp fornece uma implementao baseada no sistema XWindow.
bridge
imp

Window
DrawText() DrawRect()

WindowImp DevDrawText() DevDrawLine()

imp>DevDrawLine() imp>DevDrawLine() imp>DevDrawLine() imp>DevDrawLine()

IconWindow DrawBorder()

TransienWindow DrawCloseBox()

XWindowImp DevDrawText() DevDrawLine()

PMWindowImp DevDrawLine() DevDrawText()

DrawRect() DrawText()

DrawRect()

XDrawLine()

XDrawString()

Todas as operaes das subclasses de Window so implementadas em termos das operaes abstratas da interface WindowImp. Isso desacopla as abstraes de janelas das vrias implementaes especficas para cada plataforma. Referimo-nos ao relacionamento entre Window e WindowImp como uma ponte (bridge) porque ela forma uma ponte entre abstrao e sua implementao, permitindo que variem de forma independente.

Aplicabilidade
Use o padro Bridge quando: desejar evitar um vnculo permanente entre uma abstrao e sua implementao. Isso pode ocorrer, por exemplo, quando a implementao deve ser selecionada ou alterada em tempo de execuo;

PADRES DE PROJETO

153

tanto as abstraes como suas implementaes tiverem de ser extensveis por meio de subclasses. Neste caso, o padro Bridge permite combinar as diferentes abstraes e implementaes e estend-las independentemente; mudanas na implementao de uma abstrao no puderem ter impacto sobre os clientes; ou seja, quando o cdigo dos mesmos no puder ser recompilado. (C++) voc desejar ocultar completamente a implementao de uma abstrao dos clientes. Em C++, a representao de uma classe visvel na interface da classe; tiver uma proliferao de classes, como foi mostrado no primeiro diagrama da seo Motivao. Tal hierarquia de classes indica necessidade de separar um objeto em duas partes. Rumbaugh usa o termo generalizaes aninhadas (nested generalizations) [RPB+91] para se referir s hierarquias de cada classe; desejar compartilhar uma implementao entre mltiplos objetos (talvez usando a contagem de referncias) e este fato deve estar oculto do cliente. Um exemplo simples a classe String mencionada por Coplien [Cop92], na qual mltiplos objetos podem compartilhar a mesma representao de uma string (StringRep).

Estrutura
Client

imp

Abstraction
Operation() imp>OperationImp();

Implementor OperationImp()

ConcreteImplementorA RefinedAbstraction OperationImp()

ConcreteImplementorB OperationImp()

Participantes
Abstraction (Window) define a interface da abstrao; mantm uma referncia para um objeto do tipo Implementor. RefinedAbstraction (IconWindow). estende a interface definida por Abstraction. Implementor (WindowImp) define a interface para as classes de implementao. Essa interface no precisa corresponder exatamente interface de Abstraction; de fato, as duas interfaces podem ser bem diferentes. A interface de Implementor fornece somente operaes primitivas e Abstraction define operaes de nvel mais alto baseadas nessas primitivas. ConcreteImplementor (XwindowImp, PMWindowImp) implementa a interface de Implementor e define sua implementao concreta.

154

CAPTULO 4 PADRES ESTRUTURAIS

Colaboraes
Abstraction repassa as solicitaes dos clientes para o seu objeto Implementor.

Conseqncias
O padro Bridge tem as seguintes conseqncias: 1. Desacopla a interface da implementao. Uma implementao no fica permanentemente presa a uma interface. A implementao de uma abstrao pode ser configurada em tempo de execuo. at mesmo possvel para um objeto mudar sua implementao em tempo de execuo. O desacoplamento de Abstraction e Implementor tambm elimina dependncias em tempo de compilao da implementao. Mudar uma classe de implementao no requer a recompilao da classe Abstraction e seus clientes. Essa propriedade essencial quando voc quer assegurar compatibilidade no nvel binrio entre diferentes verses de uma biblioteca de classes. Alm disso, esse desacoplamento encoraja o uso de camadas que podem melhorar a estruturao de um sistema. A parte de alto nvel de um sistema somente tem que ter conhecimento de Abstraction e Implementor. 2. Extensibilidade melhorada. Voc pode estender as hierarquias de Abstraction e Implementor independentemente. 3. Ocultao de detalhes de implementao dos clientes. Voc pode proteger e isolar os clientes de detalhes de implementao, tais como o compartilhamento de objetos Implementor e o mecanismo de contagem de referncias que os acompanham (se houver).

Implementao
Considere os seguintes aspectos de implementao quando aplicar o padro Bridge: 1. H somente um Implementor. Em situaes onde h somente uma implementao, no necessrio criar uma classe abstrata Implementor. Trata-se aqui de um caso degenerado do padro Bridge; existe uma relao um para um entre Abstraction e Implementor. No obstante, essa separao ainda til quando uma mudana na implementao numa classe no deve afetar seus clientes existentes ou seja, no deveriam ser recompilados, apenas linkeditadas novamente. Carolan [CAR89] usa o termo Cheshire Cat, para descrever esta separao. Em C++, a interface da classe Implementor pode ser definida num arquivo header privado, o qual no fornecido aos clientes. Isso permite ocultar completamente uma implementao de uma classe dos seus clientes. 2. Criar o objeto Implementor correto. Como, quando e onde voc decide qual classe Implementor instanciar, se existe mais do que uma? Se a Abstraction tem conhecimento de todas as classes ConcreteImplementor, ento pode instanciar uma delas no seu constructor; pode decidir por uma delas atravs de parmetros passados para o seu constructor. Se, por exemplo, uma classe da categoria coleo suporta mltiplas implementaes, a deciso pode se basear no tamanho da coleo. Uma implementao com

PADRES DE PROJETO

155

uma lista ligada pode ser usada para colees pequenas, e uma tabela de randomizao (hash table) para colees maiores. Uma outra abordagem escolher, inicialmente, uma implementao-padro e mud-la mais tarde, de acordo com a utilizao. Por exemplo, se a coleo cresce alm de um certo limiar, ento ela muda sua implementao para uma mais apropriada, para um nmero maior de itens. possvel tambm delegar totalmente a deciso para outro objeto. No exemplo Window/WindowImp, ns podemos introduzir um objeto-fbrica (ver Abstract Factory (95)) cuja nica funo encapsular aspectos especficos de plataformas. A fbrica sabe que tipo de objeto WindowImp criar para a plataforma em uso; uma Window simplesmente solicita isso para uma WindowImp, e esta retorna o tipo correto. Um benefcio dessa abordagem que a Abstraction no est acoplada diretamente a nenhuma das classes Implementor. 3. Compartilhando Implementors. Coplien ilustra como a expresso Handle/ Body em C++ pode ser usado para compartilhar implementaes entre vrios objetos [Cop92]. O Body armazena um contador de referncias que a classe Handle incrementa e decrementa. O cdigo para atribuir handles com bodies compartilhados tm a seguinte forma geral:

4. Utilizao de herana mltipla. Voc pode usar a herana mltipla em C++ para combinar uma interface com a sua implementao [Mar91]. Por exemplo, uma classe pode herdar publicamente de Abstraction e privadamente de um ConcreteImplementor. Porm, porque esta abordagem usa herana esttica, ela liga uma implementao permanentemente sua interface. Portanto, voc no pode implementar um autntico padro Bridge com herana mltipla pelo menos no em C++.

Exemplo de cdigo
O cdigo C++ a seguir implementa o exemplo Window/WindowImp da seo Motivao. A classe Window define a abstrao de janela para aplicaes de clientes:

156

CAPTULO 4 PADRES ESTRUTURAIS

Window mantm uma referncia para uma WindowImp, a classe abstrata que declara uma interface para o sistema de janelas subjacente.

mais funes de desenho em janelas

As subclasses de Window definem os diferentes tipos de janelas que a aplicao pode usar, tais como janelas de aplicao, cones, janelas transitrias para dilogos, palettes flutuantes de ferramentas, e assim por diante. Por exemplo, a ApplicationWindow implementar a DrawContents para desenhar a instncia de View por ela armazenada:

IconWindow armazena o nome de um mapa de bits para o cone que ela exibe...

PADRES DE PROJETO

157

... e implementa os DrawContents para desenhar um mapa de bits na janela:

So possveis muitas outras variaes de Window. Uma TransientWindow pode necessitar se comunicar com a janela criada por ela durante o dilogo; da manter uma referncia para aquela janela. Uma PalleteWindow sempre flutua acima de outras janelas. Uma IconDockWindow contm IconWindows e as arranja de maneira ordenada. As operaes de Window so definidas em termos da interface de WindowImp. Por exemplo, a DrawRect extrai quatro coordenadas dos seus dois parmetros Point antes de chamar a operao de WindowImp que desenha o retngulo na janela:

As subclasses concretas de WindowImp suportam diferentes sistemas de janelas. A subclasse XWindowImp suporta o sistema XWindow:

Para o PresentationManager (PM), ns definimos uma classe PMWindowImp:

158

CAPTULO 4 PADRES ESTRUTURAIS

Estas subclasses implementam operaes de WindowImp em termos do sistema de janelas primitivo. Por exemplo, DeviceRect implementada para X como segue:

A implementao para o PM pode se assemelhar ao seguinte:

Como uma janela obtm uma instncia da subclasse correta de WindowImp? Neste exemplo, assumiremos que Window tem essa responsabilidade. A sua operao GetWindowImp obtm a instncia correta de uma fbrica abstrata (ver Abstract Factory (95)) que efetivamente encapsula todos os aspectos especficos do sistema de janelas.

PADRES DE PROJETO

159

O mtodo WindowSystemFactory::Instance () retorna uma fbrica abstrata que manufatura todos os objetos especficos do sistema de janelas. Para simplificar, ns o tornaremos um Singleton (130) e deixaremos a classe Window acessar a fbrica diretamente.

Usos conhecidos
O exemplo de Window acima provm da ET++ [WGM88]. Em ET++, a WindowImp chamada de WindowPort e tem subclasses como XWindowPort e SunWindowPort. O objeto Window cria o seu correspondente Implementor solicitando-o de uma fbrica abstrata chamada WindowSystem. WindowSystem fornece uma interface para criao de objetos especficos da plataforma, tais como fontes tipogrficas, cursores, mapas de bits e assim por diante. O projeto da ET++ de Window/WindowPort estende o padro Bridge no sentido de que WindowPort tambm mantm uma referncia de volta para a Window. A classe implementor de WindowPort usa essa referncia para notificar Window sobre eventos especficos de WindowPort: a chegada de eventos de entrada, redimensionamento de janelas, etc. Tanto Coplien [Cop92] como Stroustrup [Str91] mencionam classes Handle e do alguns exemplos. Seus exemplos enfatizam tpicos de administrao da memria, tais como compartilhamento de representaes de strings, e suporte para objetos de tamanho varivel. Nosso foco est mais em suportar extenses independentes, tanto de abstraes quanto de suas implementaes. libg++[Lea88] define classes que implementam estruturas de dados comuns, tais como Set, LinkedSet, HashSet, LinkedList e HashTable. Set uma classe abstrata que define uma abstrao de um conjunto, enquanto que LinkedList e HashTable so implementadores concretos (concrete implementors) para uma lista ligada e uma tabela de randomizao, respectivamente. LinkedSet e HashSet so implementadores que fazem uma ponte entre Set e seus correspondentes concretos LinkedList e HashTable. Esse um exemplo de um padro Bridge degenerado porque no h uma classe abstrata Implementor. O AppKit, da NeXT, [Add94] usa o padro Bridge na implementao e exibio de imagens grficas. Uma imagem pode ser representada de diversas maneiras. A forma de exibio tima de uma imagem depende das propriedades do dispositivo de display, especificamente de suas capacidades quanto a cores e resoluo. Sem a ajuda do AppKit, os desenvolvedores teriam que determinar qual seria a implementao a ser usada sob vrias circunstncias em cada aplicao. Para aliviar os desenvolvedores desta responsabilidade, a AppKit fornece uma bridge (ponte) chamada NXImage/NXImageRep. NXImage define a interface para a manipulao de imagens. A implementao de imagens definida numa hierarquia de classes separada NXImageRep, a qual tem subclasses, tais como NXEPSImageRep, NXCachedImageRep e NXBitMapImageRep. NXImage mantm uma referncia para um ou mais objetos NXImageRep. Se existe mais do que uma implementao de imagem, ento NXImage seleciona a mais adequada para o dispositivo de display que est sendo usado. NXImage at mesmo capaz de converter de uma implementao para outra, se necessrio. O aspecto interessante dessa variante do Bridge, que NXImage pode armazenar mais do que uma implementao NXImageRep ao mesmo tempo.

160

CAPTULO 4 PADRES ESTRUTURAIS

Padres relacionados
Um padro Abstract Factory (95) pode criar e configurar uma Bridge especfica. O padro Adapter (140) orientado para fazer com que classes no-relacionadas trabalhem em conjunto. Ele normalmente aplicado a sistemas que j foram projetados. Por outro lado, Bridge usado em um projeto, desde o incio, para permitir que abstraes e implementaes possam variar independentemente.

COMPOSITE
Inteno

estrutural de objetos

Compor objetos em estruturas de rvore para representarem hierarquias partes-todo. Composite permite aos clientes tratarem de maneira uniforme objetos individuais e composies de objetos.

Motivao
Aplicaes grficas, tais como editores de desenhos e sistemas de captura esquemtica, permitem aos usurios construir diagramas complexos a partir de componentes simples. O usurio pode agrupar componentes para formar componentes maiores, os quais, por sua vez, podem ser agrupados para formar componentes ainda maiores. Uma implementao simples poderia definir classes para primitivas grficas, tais como Texto e Linhas, alm de outras classes que funcionam como recipientes (containers) para essas primitivas. Porm, h um problema com essa abordagem: o cdigo que usa essas classes deve tratar objetos primitivos e objetos recipientes de modo diferente, mesmo se na maior parte do tempo o usurio os trata de forma idntica. Ter que distinguir entre esses objetos torna a aplicao mais complexa. O padro Composite descreve como usar a composio recursiva de maneira que os clientes no tenham que fazer essa distino.
Graphic Draw() Add(Graphic) Remove(Graphic) GetChild(int)

graphics Line Draw() Rectangle Draw() Text Draw() Picture Draw() Add(Graphic g) Remove(Graphic) GetChild(int) forall g in graphics g.Draw() add g to list of graphics

PADRES DE PROJETO

161

A chave para o padro Composite uma classe abstrata que representa tanto as primitivas como os seus recipientes. Para o sistema grfico, esta classe Graphic. A Graphic declara operaes como Draw, que so especficas de objetos grficos. Ela tambm declara operaes que todos os objetos compostos compartilham, tais como operaes para acessar e administrar seus filhos. As subclasses Line, Rectangle e Text (ver diagrama de classes precedente) definem objetos grficos primitivos. Essas classes implementam Draw para desenhar linhas, retngulos e textos, respectivamente. Uma vez que as primitivas grficas no tm filhos grficos, nenhuma dessas subclasses implementa operaes relacionadas com filhos. A classe Picture define um agregado de objetos Graphic. Picture implementa Draw para chamar Draw em seus filhos e implementa operaes relacionadas com filhos da maneira necessria. Como a interface de Picture segue a interface de Graphic, os objetos Picture podem compor outros objetos Picture recursivamente. O diagrama a seguir mostra uma tpica estrutura de objeto composto, composta recursivamente por objetos grficos:
aPicture

aPicture

aLine

aRectangle

aText

aLine

aRectangle

Aplicabilidade
Use o padro Composite quando: quiser representar hierarquias partes-todo de objetos; quiser que os clientes sejam capazes de ignorar a diferena entre composies de objetos e objetos individuais. Os clientes trataro todos os objetos na estrutura composta de maneira uniforme.

Estrutura
Client

Component Operation() Add(Component) Remove(Component) GetChild(int)

children Leaf Operation() Composite Operation() Add(Component) Remove(Component) GetChild(int) forall g in children g.Operation();

162

CAPTULO 4 PADRES ESTRUTURAIS

Uma tpica estrutura de um objeto Composite pode se parecer com esta:


aComposite

aLeaf

aLeaf

aComposite

aLeaf

aLeaf

aLeaf

aLeaf

Participantes
Component (Graphic) declara a interface para os objetos na composio; implementa comportamento-padro para a interface comum a todas as classes, conforme apropriado; declara uma interface para acessar e gerenciar os seus componentes-filhos; (opcional) define uma interface para acessar o pai de um componente na estrutura recursiva e a implementa, se isso for apropriado. Leaf (Rectangle, Line, Text, etc.) representa objetos-folha na composio. Uma folha no tem filhos; define comportamento para objetos primitivos na composio. Composite (Picture) define comportamento para componentes que tm filhos; armazena os componentes-filho; implementa as operaes relacionadas com os filhos presentes na interface de Component. Client manipula objetos na composio atravs da interface de Component.

Colaboraes
Os clientes usam a interface da classe Component para interagir com os objetos na estrutura composta. Se o receptor uma Leaf (Folha), ento a solicitao tratada diretamente. Se o receptor um Composite, ele normalmente repassa as solicitaes para os seus componentes-filhos, executando operaes adicionais antes e/ou depois do repasse.

Conseqncias
O padro Composite: define hierarquias de classe que consistem de objetos primitivos e objetos compostos. Os objetos primitivos podem compor objetos mais complexos, os quais, por sua vez, tambm podem compor outros objetos, e assim por diante, recursivamente. Sempre que o cdigo do cliente esperar um objeto primitivo, ele tambm poder aceitar um objeto composto. torna o cliente simples. Os clientes podem tratar estruturas compostas e objetos individuais de maneira uniforme. Os clientes normalmente no sabem (e no

PADRES DE PROJETO

163

deveriam se preocupar com isto) se esto tratando com uma folha ou um componente composto. Isto simplifica o cdigo a ser escrito nas classes- cliente, porque evita o uso de comandos do tipo CASE com os rtulos classes que definem a composio. torna mais fcil de acrescentar novas espcies de componentes. Novas subclasses definidas, Composite ou Leaf, funcionam automaticamente com as estruturas existentes e o cdigo do cliente. Os clientes no precisam ser alterados para tratar novas classes Component. pode tornar o projeto excessivamente genrico. A desvantagem de facilitar o acrescimo de novos componentes que isso torna mais difcil restringir os componentes de uma composio. Algumas vezes, voc deseja uma composio que tenha somente certos componentes. Com Composite, voc no pode confiar no sistema de tipos para garantir a obedincia a essas restries. Ao invs disso, ter que usar verificaes e testes em tempo de execuo.

Implementao
H muitos aspectos a serem considerados quando da implementao do padro Composite: 1. Referncias explcitas aos pais. Manter referncias dos componentes-filhos para seus pais pode simplificar o percurso e a administrao de uma estrutura composta. A referncia aos pais simplifica mover-se para cima na estrutura e deletar um componente. As referncias ao pai tambm ajudam a suportar o padro Chain of Responsibility (212). O lugar usual para definir a referncia ao pai a classe Component. As classes Leaf e Composite podem herdar a referncia e as operaes que a gerenciam. Com referncias aos pais, necessrio manter a condio invariante de que todos os descendentes de um composto tenham um pai. A maneira mais fcil de garantir isso mudar o pai de um componente somente quando ele est sendo acrescentado ou removido de um composto. Se isso puder ser implementado uma nica vez nas operaes Add e Remove da classe Composite, ento poder ser herdado por todas as subclasses, e a condio invariante ser mantida automaticamente. 2. Compartilhamento de componentes. til compartilhar componentes, por exemplo, para reduzir os requisitos de espao de armazenamento. Porm, quando um componente no pode ter mais do que um pai, o compartilhamento de componentes torna-se difcil. Uma soluo possvel fazer com que os filhos armazenem mltiplos pais. Porm, isso pode conduzir a ambigidades, na medida em que uma solicitao se propagar para cima na estrutura. O padro Flyweight (187) mostra como retrabalhar um projeto para no ter que armazenar os pais. Ele funciona nos casos onde os filhos podem evitar o envio de solicitaes aos pais atravs da externalizao de alguns ou de todos os seus estados. 3. Maximizao da interface de Component. Um dos objetivos do padro Composite tornar os clientes desconhecedores das classes especficas Leaf ou Composite que esto usando. Para atingir este objetivo, a classe Component deve definir

164

CAPTULO 4 PADRES ESTRUTURAIS

tantas operaes comuns quanto possvel para as classes Composite e Leaf. A classe Component usualmente fornece implementaes-padro para essas operaes, e as subclasses Leaf e Composite as redefiniro. Contudo, esse objetivo algumas vezes ir conflitar com o princpio do projeto de hierarquia de classes, que afirma que uma classe deveria somente definir operaes que tm significado para suas subclasses. Existem muitas operaes suportadas por Component que no parecem fazer sentido para as classes Leaf. Como pode, ento, Component fornecer uma implementaopadro para elas? s vezes, um pouco de criatividade mostra como uma operao que teria sentido somente para Composite pode ser implementada para todos os Components movendo-a para a classe Component. Por exemplo, a interface para acessar os filhos uma parte fundamental de uma classe Composite, mas no necessariamente de classes Leaf. Mas se virmos uma Leaf como um Component que nunca tem filhos, podemos definir uma operao-padro para o acesso a filhos na classe Component que nunca retorna quaisquer filhos. As classes Leaf podem usar a implementao-padro, porm, as classes Composite a reimplementaro de maneira que ela retorne seus filhos. O gerenciamento das operaes sobre filhos mais problemtica e discutida no prximo item. 4. Declarando as operaes de gerenciamento de filhos. Embora a classe Composite implemente as operaes Add e Remove para gerenciar os filhos, um aspecto importante do padro Composite quais classes declaram essas operaes na hierarquia de classes. Deveramos declarar essas operaes em Component e fazer com que tenham sentido para as classes Leaf, ou deveramos declarlas e defini-las somente em Composite e suas subclasses? A deciso envolve uma soluo de compromisso entre a segurana e a transparncia: Definir a interface de gerenciamento dos filhos na raiz da hierarquia de classes lhe d transparncia porque voc pode tratar todos os componentes uniformemente. No entanto, isso diminui a segurana porque os clientes podem tentar fazer coisas sem sentido, como acrescentar e remover objetos das folhas. Definir o gerenciamento de filhos na classe Composite lhe d segurana porque qualquer tentativa de acrescentar ou remover objetos das folhas ser detectada em tempo de compilao em uma linguagem com tipos estticos, como C++. Porm, voc perde transparncia porque as folhas e os compostos tm interfaces diferentes. Neste padro ns enfatizamos a transparncia em detrimento da segurana. Se voc optar pela segurana, algumas vezes poder perder informaes sobre tipos e ter que converter um componente num composto. Como isso pode ser feito sem recorrer a uma converso de tipo insegura? Uma abordagem declarar uma operao Composite* GetComposite() na classe Component. Component fornece uma operao-padro que retorna um apontador nulo. A classe Composite redefine essa operao para retornar a si mesma atravs do apontador this:

PADRES DE PROJETO

165

GetComposite permite fazer uma consulta a um componente para verificar se uma composio. Voc pode executar as operaes Add e Remove com

segurana na composio que o GetComposite retorna.

Testes semelhantes para um Composite podem ser efetuados usando a construo dynamic_cast da C++. Naturalmente, o problema aqui que ns no tratamos todos os componentes uniformemente. Temos que voltar a testar diferentes tipos antes de tomar a ao apropriada. A nica maneira de oferecer transparncia definindo operaes-padro para Add e Remove em Component. Isso cria um novo problema: no h uma maneira de implementar Component::Add sem introduzir a possibilidade de ela falhar. Voc poderia fazer com que ela no fizesse nada, mas isso ignora uma considerao importante, ou seja, uma tentativa de adicionar algo a uma folha provavelmente indica um erro (bug). Nesse caso, a operao Add produziria lixo. Voc poderia fazer com que ela deletasse seu argumento, mas isso pode no ser o que os clientes esperam. Normalmente, melhor fazer Add e Remove falharem como caso-padro (talvez gerando uma exceo) se o componente no puder ter filhos ou se o argumento de Remove no for um filho do componente.

166

CAPTULO 4 PADRES ESTRUTURAIS

5.

6.

7.

8.

9.

Uma outra alternativa mudar levemente o significado de remover. Se o componente mantiver uma referncia para um pai, ento poderemos redefinir Component::Remove para remover ele prprio do seu pai. Contudo, ainda no existe uma interpretao com significado para um Add correspondente. Component deveria implementar uma lista de Components? Voc pode ser tentado a definir o conjunto de filhos como uma varivel de instncia na classe Component, em que as operaes de acesso e o gerenciamento dos filhos so declaradas. Porm, colocar o apontador para o filho na classe-base incorre em uma penalizao de espao para todas as folhas, mesmo que uma folha nunca tenha filhos. Isto vale a pena somente se houver poucos filhos na estrutura. Ordenao dos filhos. Muitos projetos especificam uma ordenao dos filhos de Composite. No exemplo Graphics anterior, ordenar pode significar ordenar da frente para trs. Se os Composites representam rvores de anlise (parse trees), ento comandos compostos (de uma linguagem de programao) podem ser instncias de um Composite cujos filhos devem ser ordenados para refletir o programa. Quando a ordenao de filhos um aspecto a ser considerado, voc deve desenhar interfaces de acesso e o gerenciamento de filhos cuidadosamente, para administrar a seqncia dos filhos. O padro Iterator (244) pode gui-lo na soluo desse problema. Uso de caching para melhorar o desempenho. Se necessrio percorrer ou pesquisar composies com freqncia, a classe Composite pode fazer um cache de informaes sobre seus filhos para navegao. O Composite pode fazer um cache dos resultados obtidos ou somente de informaes que lhe permitam abreviar o percurso ou a busca. A classe Picture do exemplo na seo Motivao poderia fazer um cache das caixas delimitadoras dos seus filhos. Durante o desenho ou a seleo, essa caixa delimitadora colocada no cache permite Picture evitar o desenho ou a busca quando seus filhos no estiverem visveis na janela corrente. As mudanas de um componente exigiro a invalidao dos caches dos seus pais. Isso funciona melhor quando os componentes conhecem os seus pais. Assim, se voc est usando caching, precisar definir uma interface para dizer aos compostos que seus caches esto invlidos. Quem deveria deletar componentes? Em linguagens sem garbage collection, normalmente melhor tornar um Composite responsvel pela deleo de seus filhos, quando for destrudo. Uma exceo a essa regra ocorre quando objetos Leaf so imutveis e assim podem ser compartilhados. Qual a melhor estrutura de dados para o armazenamento de componentes? Os Composites podem usar vrias estruturas de dados para armazenar os seus filhos, incluindo listas ligadas, rvores, vetores (arrays) e tabelas de randomizao (hash tables). A escolha da estrutura de dados depende (como sempre) da eficincia. De fato, no nem mesmo necessrio usar uma estrutura de dados de propsito geral. Algumas vezes, os compostos tm uma varivel para cada filho, embora isto requeira que cada subclasse de Composite implemente sua prpria interface de gerncia. Veja Interpreter (231) para um exemplo.

PADRES DE PROJETO

167

Exemplo de cdigo
Equipamentos tais como computadores e componentes para sistemas de som estereofnicos so freqentemente organizados em hierarquias partes/todo ou de contedo. Por exemplo, um chassis pode conter dispositivos perifricos e placas-me, um barramento (bus) pode conter cartes e um gabinete pode conter chassis, barramentos, e assim por diante. Tais estruturas podem ser modeladas naturalmente usando o padro Composite. A classe Equipment define uma interface para todos os equipamentos na hierarquia partes-todo.

Equipment declara operaes que retornam os atributos de um equipamento ou parte de um equipamento, tais como o seu consumo de energia e seu custo. As subclasses implementam essas operaes para tipos especficos de equipamentos. Equipment tambm declara uma operao CreateIterator a qual retorna um Iterator (ver Apndice C) para acessar suas partes. A implementao-padro para essa operao retorna uma NullIterator (iterador nulo), o qual itera sobre o conjunto vazio. As subclasses de Equipment podem incluir classes Leaf que representam dispositivos ou acionadores de disco, circuitos integrados e chaves:

CompositeEquipment a classe base para equipamentos que contm outros equipamentos. Ela tambm uma subclasse de Equipment.

168

CAPTULO 4 PADRES ESTRUTURAIS

CompositeEquipment define as operaes para acesso e gerenciamento de subequipamentos. As operaes Add e Remove inserem e deletam equipamentos da lista de equipamentos armazenada num membro _equipment. A operao CreateIterator retorna um iterador (especificamente, uma instncia de ListIterator) que percorrer esta lista. Uma implementao-padro de NetPrice pode utilizar CreateIterator para efetuar a soma dos preos lquidos dos subequipamentos 2:

Agora podemos representar o chassis de um computador como uma subclasse de


CompositeEquipment chamada Chassis. Chassis, que herda as operaes relacionadas com os filhos de CompositeEquipment.

Podemos definir outros recipientes (containers) de equipamentos, tais como Cabinet e Bus de uma maneira similar. Isso nos d tudo que necessitamos para montar um equipamento como um computador pessoal bastante simples.

PADRES DE PROJETO

169

Usos conhecidos
Exemplos do padro Composite podem ser encontrados em quase todos os sistemas orientados a objetos . A classe View original do Model/View/Controller da Smalltalk [Kp88] era um Composite, e quase todo toolkit para construo de interfaces de usurio e frameworks seguiram os seus passos, incluindo ET++ (com o seu VObjects [WGM88]) e InterViews (Styles [LCI+92], Graphics [VL88] e Glyphs [CL90]). interessante observar que a verso original de View do Model/View/Controller tinha um conjunto de subviews; em outras palavras, view era tanto a classe Component como a classe Composite. O Release 4.0 da Smalltalk-80 revisou o Model/View/Controller como uma classe VisualComponent que tem subclasses View e CompositeView. O framework compilador RTL Smalltalk [JML92] usa o padro Composite extensamente. A RTL Expression uma classe Component para rvores de anlise (parse trees). Ela tem subclasses, tais como BinaryExpression, que contm objetos-filho RTLExpression. Essas classes definem uma estrutura composta para rvores de anlise. RegisterTransfer a classe Component para uma forma intermediria Single Static Assigment (SSA) de um programa. As subclasses Leaf, de RegisterTransfer, definem diferentes atribuies estticas (static assignments), tais como: atribuies primitivas que executam uma operao em dois registradores, atribuindo o resultado a um terceiro; uma atribuio com um registrador de origem, mas sem registrador de destino, o que indica que o registrador usado aps o retorno de uma rotina; e uma atribuio com registrador de destino, mas sem registrador de origem, o que indica que o registrador recebe a atribuio antes do incio da rotina. Outra subclasse, RegisterTransferSet, uma classe Composite para representar atribuies que mudam vrios registradores de uma s vez. Outro exemplo desse padro ocorre no domnio financeiro, em que um portflio agrega ativos individuais. Voc pode suportar agregaes complexas de ativos implementando um portflio como um Composite compatvel com a interface de um ativo individual [BE93]. O padro Command (222) descreve como objetos Command podem ser compostos e seqenciados com uma classe Composite MacroCommand.

170

CAPTULO 4 PADRES ESTRUTURAIS

Padres relacionados
Freqentemente, a ligao componente-pai usada para o padro Chain of Responsibility (212). O padro Decorator (170) freqentemente usado com o padro Composite. Quando decoradores e composies so usados juntos, eles tm normalmente uma classe-me comum. Assim, decoradores tero que suportar a interface de Component com operaes como Add, Remove e GetChild. O Flyweight (187) permite compartilhar componentes, porm estes no mais podem referenciar seus pais. O padro Iterator (244) pode ser usado para percorrer os compostos. O padro Visitor (305) pode ser usado para localizar operaes e comportamentos que seriam de outra forma distribudos entre classes Composite e Leaf.

DECORATOR
Inteno

estrutural de objetos

Dinamicamente, agregar responsabilidades adicionais a um objeto. Os Decorators fornecem uma alternativa flexvel ao uso de subclasses para extenso de funcionalidades.

Tambm Conhecido Como


Wrapper

Motivao
Algumas vezes queremos acrescentar responsabidades a objetos individuais, e no a toda uma classe. Por exemplo, um toolkit para construo de interfaces grficas de usurio deveria permitir a adio de propriedades, como bordas, ou comportamentos, como rolamento, para qualquer componente da interface do usurio. Uma forma de adicionar responsabilidades a herana. Herdar uma borda de uma outra classe coloca uma borda em volta de todas as instncias de uma subclasse. Contudo, essa abordagem inflexvel, porque a escolha da borda feita estaticamente. Um cliente no pode controlar como e quando decorar o componente com uma borda. Uma abordagem mais flexvel embutir o componente em outro objeto que acrescente a borda. O objeto que envolve o primeiro chamado de decorator. O decorator segue a interface do componente que decora, de modo que sua presena transparente para os clientes do componente. O decorator repassa solicitaes para o componente, podendo executar aes adicionais (tais como desenhar uma borda) antes ou depois do repasse. A transparncia permite encaixar decoradores recursivamente, desta forma permitindo um nmero ilimitado de responsabilidades adicionais. Suponha que tenhamos um objeto TextView que exibe texto numa janela. Como padro, TextView no tem barras de rolamento porque nem sempre a necessitamos. Quando as necessitarmos, poderemos usar um ScrollDecorator para acrescent-las.

PADRES DE PROJETO

171

Suponha, tambm, que queiramos acrescentar uma borda preta espessa ao redor do objeto TextView. Tambm podemos usar um objeto BorderDecorator para esta finalidade. Simplesmente compomos os decoradores com TextView para produzir o resultado desejado. O diagrama de objetos abaixo mostra como compor um objeto TextView com objetos BorderDecorator e ScrollDecorator para produzir uma viso do texto cercada por bordas e rolvel:
aBorderDecorator component aScrollDecorator component

aTextView

As classes ScrollDecorator e BorderDecorator so subclasses de Decorator, uma classe abstrata destinada a componentes visuais que decoram outros componentes visuais.
VisualComponent
Draw() VisualComponent

Draw()

component
Draw() TextView

TextView

Draw() Decorator

Decorator component>Draw()

Draw()

Draw()

ScrollDecorator Draw() Scrollto() scrollPosition

BorderDecorator Draw() DrawBorder() borderWidth Decorator::Draw(); DrawBorder();

VisualComponent a classe abstrata para objetos visuais. Ela define suas interface de desenho e de tratamento de eventos. Observe como a classe Decorator simplesmente repassa as solicitaes de desenho para o seu componente e como as subclasses de Decorator podem estender essa operao. As subclasses de Decorator so livres para acrescentar operaes para funcionalidades especficas. Por exemplo, a operao ScrollTo, de ScrollDecorator, permite a

172

CAPTULO 4 PADRES ESTRUTURAIS

outros objetos fazerem rolamento na interface se eles souberem que existe um objeto ScrollDecorator na interface. O aspecto importante deste padro que ele permite que decoradores (decorators) apaream em qualquer lugar no qual possa aparecer um VisualComponent. Desse modo, os clientes em geral no podero distinguir entre um componente decorado e um no-decorado, e assim sero totalmente independentes das decoraes.

Aplicabilidade
Use Decorator: para acrescentar responsabilidades a objetos individuais de forma dinmica e transparente, ou seja, sem afetar outros objetos; para responsabilidades que podem ser removidas; quando a extenso atravs do uso de subclasses no prtica. s vezes, um grande nmero de extenses independentes possvel e isso poderia produzir uma exploso de subclasses para suportar cada combinao. Ou a definio de uma classe pode estar oculta ou no estar disponvel para a utilizao de subclasses.

Estrutura
Component Operation()

ConcreteComponent Operation()

component

Decorator
Operation() component>Operation()

ConcreteDecoratorA Operation() addedState

ConcreteDecoratorB Operation() AddedBehavior() Decorator::Operation(); AddedBehavior();

Participantes
Component (VisualComponent) define a interface para objetos que podem ter responsabilidades acrescentadas aos mesmos dinamicamente. ConcreteComponent (TextView) define um objeto para o qual responsabilidades adicionais podem ser atribudas. Decorator mantm uma referncia para um objeto Component e define uma interface que segue a interface de Component. ConcreteDecorator (BorderDecorator, ScrollDecorator) acrescenta responsabilidades ao componente.

PADRES DE PROJETO

173

Colaboraes
Decorator repassa solicitaes para o seu objeto Component. Opcionalmente, ele pode executar operaes adicionais antes e depois de repassar a solicitao.

Conseqncias
O padro Decorator tem pelo menos dois benefcios-chaves e duas deficincias: 1. Maior flexibilidade do que a herana esttica. O padro Decorator fornece uma maneira mais flexvel de acrescentar responsabilidades a objetos do que pode ser feito com herana esttica (mltipla). Com o uso de decoradores, responsabilidades podem ser acrescentadas e removidas em tempo de execuo simplesmente associando-as e dissociando-as de um objeto. Em comparao, a herana requer a criao de uma nova classe para cada responsabilidade adicional (por exemplo, BorderScrollableTextView, BorderedTextView). Isso d origem a muitas classes e aumenta a complexidade de um sistema. Alm do mais, fornecer diferentes classes Decorator para uma especfica classe Component permite combinar e associar (match) responsabilidades. Os Decorators tambm tornam fcil acrescentar uma propriedade duas vezes. Por exemplo, para dar a um TextView uma borda dupla, simplesmente associe dois BorderDecorators. Herdar de uma classe Border duas vezes um procedimento sujeito a erros, na melhor das hipteses. 2. Evita classes sobrecarregadas de caractersticas na parte superior da hierarquia. Um Decorator oferece uma abordagem do tipo use quando for necessrio para adio de responsabilidades. Em vez de tentar suportar todas as caractersticas previsveis em uma classe complexa e customizada, voc pode definir uma classe simples e acrescentar funcionalidade de modo incremental com objetos Decorator. A funcionalidade necessria pode ser composta a partir de peas simples. Como resultado, uma aplicao no necessita incorrer no custo de caractersticas e recursos que no usa. Tambm fcil definir novas espcies de Decorators independentemente das classes de objetos que eles estendem, mesmo no caso de extenses no-previstas. Estender uma classe complexa tende a expor detalhes no-relacionados com as responsabilidades que voc est adicionando. 3. Um decorador e o seu componente no so idnticos. Um decorador funciona como um envoltrio transparente. Porm, do ponto de vista da identidade de um objeto, um componente decorado no idntico ao prprio componente. Da no poder depender da identidade de objetos quando voc utiliza decoradores. 4. Grande quantidade de pequenos objetos. Um projeto que usa o Decorator freqentemente resulta em sistemas compostos por uma grande quantidade de pequenos objetos parecidos. Os objetos diferem somente na maneira como so interconectados, e no nas suas classes ou no valor de suas variveis. Embora esses sistemas sejam fceis de customizar por quem os compreende, podem ser difceis de aprender e depurar.

174

CAPTULO 4 PADRES ESTRUTURAIS

Implementao
Vrios tpicos deveriam ser levados em conta quando da aplicao do padro Decorator: 1. Conformidade de interface. A interface do objeto decorador deve estar em conformidade com a interface do componente que ele decora. Portanto, classes ConcreteDecorator devem herdar de uma classe comum (ao menos em C++). 2. Omisso da classe abstrata Decorator. No h necessidade de definir uma classe abstrata Decorator quando voc necessita acrescentar uma responsabilidade. Isso acontece freqentemente quando voc est lidando com uma hierarquia de classes existente em vez de estar projetando uma nova hierarquia. Nesse caso, pode fundir a responsabilidade de Decorator de repassar solicitaes para o componente, com o ConcreteDecorator. 3. Mantendo leves as classes Component. Para garantir uma interface que apresente conformidade, componentes e decoradores devem descender de uma classe Component comum. importante manter leve essa classe comum; ou seja, ela deve focalizar a definio de uma interface e no o armazenamento de dados. A definio da representao dos dados deve ser transferida para subclasses; caso contrrio, a complexidade da classe Component pode tornar os decoradores muito pesados para serem usados em grande quantidade. A colocao de um volume grande de funcionalidades em Component tambm aumenta a probabilidade de que as subclasses concretas paguem por caractersticas e recursos de que no necessitam. 4. Mudar o exterior de um objeto versus mudar o seu interior. Podemos pensar em um decorador como sendo uma pele sobre um objeto que muda o seu comportamento. Uma alternativa mudar o interior do objeto. O padro Strategy (292) um bom exemplo de um padro para mudana do interior de um objeto. Strategies representam uma escolha melhor em situaes onde a classe Component intrinsicamente pesada, dessa forma tornando a aplicao do padro Decorator muito onerosa. No padro Strategy, o componente repassa parte do seu comportamento para um objeto strategy separado. O padro Strategy permite alterar ou estender a funcionalidade do componente pela substituio do objeto strategy. Por exemplo, podemos suportar diferentes estilos de bordas fazendo com que o componente transfira o desenho de bordas para um objeto Border separado. O objeto Border um objeto Strategy que encapsula uma estratgia para desenhar bordas. Atravs da extenso do nmero de estratgias de apenas uma para uma lista aberta (ilimitada) das mesmas, obtemos o mesmo efeito que quando encaixamos decoradores recursivamente. Por exemplo, no MacApp 3.0 [App89] e no Bedrock [Sym93a] componentes grficos (chamados views(vises) mantm uma lista de objetos adornadores (adorners) que podem ligar adornos adicionais, tais como bordas, a um componente viso. Se uma viso tem qualquer adorno ligado ou associado, ento ela lhes d a oportunidade de desenhar elementos adicionais. O MacApp e o Bedrock precisam usar essa abordagem porque a classe View bastante pesada. Seria muito caro usar uma View totalmente completa somente para acrescentar uma borda.

PADRES DE PROJETO

175

Uma vez que o padro Decorator somente muda o componente do ponto de vista exterior, o componente no precisa saber coisa alguma sobre seus decoradores; ou seja, os decoradores so transparentes para o componente.
aDecorator component

aDecorator component

aComponent

funcionalidade estendida pelo decorador(decorator)

Quando se usam estratgias, o prprio componente conhece as extenses possveis. Assim, ele tem que referenciar e manter as estratgias correspondentes:
aComponent strategies

aStrategy next

aStrategy next

funcionalidade estendida pela estratgia(strategy)

A abordagem baseada no Strategy pode exigir a modificao do componente para acomodar novas extenses. Por outro lado, uma estratgia pode ter uma interface especializada, enquanto que a interface de um decorador deve estar em conformidade com a do componente. Por exemplo, uma estratgia para implementar uma borda necessita somente definir a interface para definir uma borda (DrawBorder, GetWidth, etc.), o que significa que a estratgia pode ser leve, ainda que a classeComponent seja pesada. O MacApp e o Bedrock utilizam essa abordagem para mais do que simplesmente adornar vises. Eles tambm a usam para estender o comportamento de tratamento de eventos dos objetos. Em ambos os sistemas, uma viso mantm uma lista de objetos behavior (comportamento), que pode modificar e interceptar eventos. A viso d a cada um dos objetos comportamento registrados uma chance de manipular o evento antes de comportamentos no-registrados, na prtica redefinindo-os. Voc pode decorar uma viso com suporte especial para tratamento do teclado, por exemplo, registrando um objeto comportamento que intercepta e trata eventos de teclas.

Exemplo de cdigo
O cdigo a seguir mostra como implementar decoradores de interfaces de usurio em C++. Assumiremos que existe uma classe Component chamada VisualComponent.

176

CAPTULO 4 PADRES ESTRUTURAIS

Definiremos uma subclasse de VisualComponent chamada Decorator, para a qual introduziremos subclasses para obter diferentes decoraes.

Decorator decora o VisualComponent referenciado pela varivel de instncia _component, a qual iniciada no constructor. Para cada operao na interface de VisualComponent, Decorator define uma implementao-padro que passa a solicitao para _component:

As subclasses de Decorator definem decoraes especficas. Por exemplo, a classe BorderDecorator acrescenta uma borda ao seu componente circunscrito. BorderDecorator uma subclasse de Decorator que redefine a operao Draw para desenhar a borda. BorderDecorator tambm define uma operao de ajuda privada DrawBorder, que executa o desenho. A subclasse herda todas as outras implementaes de operaes de Decorator.

Uma implementao similar seria adotada para ScrollDecorator e DropShadowDecorator, a qual adicionaria recursos de rolamento e sombreamento a um componente visual. Agora podemos compor instncias destas classes para oferecer

PADRES DE PROJETO

177

diferentes decoraes. O cdigo a seguir ilustra como podemos usar decoradores para criar um TextView rolvel com bordas. Em primeiro lugar, temos que colocar um componente visual num objeto janela. Assumiremos que a nossa classe Window oferece uma operao SetContents com esta finalidade:

Agora podemos criar a viso do texto e uma janela para coloc-la:

TextView um VisualComponent que nos permite coloc-lo na janela:

Mas queremos uma TextView rolvel com bordas. Assim, ns a decoramos de maneira apropriada antes de coloc-la na janela.

Uma vez que Window acessa os seus contedos atravs da interface de VisualComponent, ele no ciente da presena do decorador. Voc, sendo cliente, pode ainda acompanhar a viso do texto se tiver que interagir diretamente com ela, por exemplo, quando precisar invocar operaes que no so parte da interface de VisualComponent. Clientes que dependem da identidade do componente tambm poderiam referenci-lo diretamente.

Usos conhecidos
Muitos toolkits orientados a objetos para construo de interfaces de usurio utilizam decoradores para acrescentar adornos grficos a widgets. Os exemplos incluem o InterViews [LVC89, LCI+92], a ET++ [WGM88] e a biblioteca de classes ObjectWorks/ Smalltalk [Par90]. Aplicaes mais exticas do Decorator so o DebuggingGlyph, do InterViews, e o PassivityWrapper, do ParcPlace Smalltalk. Um DebbuggingGlyph imprime informao para depurao (debbugging) antes e depois que ele repassa uma solicitao de layout para o seu componente. Esta informao para rastreamento pode ser usada para analisar e depurar o comportamento de layout de objetos participantes numa composio complexa. O PassivityWrapper pode habilitar ou desabilitar interaes do usurio com o componente. Porm, o padro Decorator no tem seu uso limitado a interfaces grficas do usurio, como ilustra o exemplo a seguir, baseado nas classes streaming da ET++[WGM88].

178

CAPTULO 4 PADRES ESTRUTURAIS

Streams so uma abstrao fundamental em muitos recursos de programas para entrada e sada. Um stream pode fornecer uma interface para converso de objetos em uma seqncia de bytes ou caracteres. Isso nos permite transcrever um objeto para um arquivo, ou para um string na memria, para posterior recuperao. Uma maneira simples e direta de fazer isso definir uma classe abstrata Stream com as subclasses MemoryStream e FileStream. Mas suponha que tambm queiramos ser capazes de fazer o seguinte: Comprimir os lados do stream usando diferentes algoritmos de compresso (runlength encoding, Lempel-Ziv, etc.). Reduzir os dados do stream a caracteres ASCII de sete bits, de maneira que possa ser transmitido por um canal de comunicao ASCII. O padro Decorator fornece uma maneira elegante de acrescentar estas responsabilidades a streams. O diagrama abaixo mostra uma soluo para o problema:

A classe abstrata Stream mantm um buffer interno e fornece operaes para armazenar dados no stream (PutInt, PutString). Sempre que o buffer fica cheio, Stream chama a operao abstrata HandleBufferFull, que faz a transferncia efetiva dos dados. A verso FileStream desta operao redefine a mesma para transferir o buffer para um arquivo. Aqui a classe chave StreamDecorator, a qual mantm uma referncia para um componente stream e repassa solicitaes para o mesmo. As subclasses de StreamDecorator substituem HandleBufferFull e executam aes adicionais antes de chamar a operao HandleBufferFull de StreamDecorator. Por exemplo, a subclasse CompressingStream comprime os dados, e ASCII7Stream converte os dados para ASCII de sete bits. Agora, para criar uma FileStream que comprime os seus dados e converte os dados binrios comprimidos para ASCII de sete bits, ns decoramos um FileStream com um CompressingStream e um ASCII7Stream:

PADRES DE PROJETO

179

Padres relacionados
Adapter (140): Um padro Decorator diferente de um padro Adapter no sentido de que um Decorator somente muda as responsabilidades de um objeto, no a sua interface; j um Adapter dar a um objeto uma interface completamente nova. Composite (160): Um padro Decorator pode ser visto como um padro Composite degenerado com somente um componente. Contudo, um Decorator acrescenta responsabilidades adicionais ele no se destina a agregao de objetos. Strategy (292): Um padro Decorator permite mudar a superfcie de um objeto, um padro Strategy permite mudar o seu interior. Portanto, essas so duas maneiras alternativas de mudar um objeto.

FAADE
Inteno

estrutural de objetos

Fornecer uma interface unificada para um conjunto de interfaces em um subsistema. Faade define uma interface de nvel mais alto que torna o subsistema mais fcil de ser usado.

Motivao
Estruturar um sistema em subsistemas ajuda a reduzir a complexidade. Um objetivo comum de todos os projetos minimizar a comunicao e as dependncias entre subsistemas. Uma maneira de atingir esse objetivo introduzir um objeto faade (fachada), o qual fornece uma interface nica e simplificada para os recursos e facilidades mais gerais de um subsistema.
classes-clientes

Faade

classes do subsistema

Considere, por exemplo, um ambiente de programao que fornece acesso s aplicaes para o seu subsistema compilador. Esse subsistema contm classes, tais como Scanner, Parser, ProgramNode, BytecodeStream e ProgramNodeBuilder, que implementam o compilador. Algumas aplicaes especializadas podem precisar acessar essas classes diretamente. Mas a maioria dos clientes de um compilador geralmente no se preocupa com detalhes tais como anlise e gerao de cdigo; eles apenas querem compilar seu cdigo. Para eles, as interfaces poderosas, porm de baixo nvel, do subsistema compilador somente complicam sua tarefa.

180

CAPTULO 4 PADRES ESTRUTURAIS

Para fornecer uma interface de nvel mais alto que pode isolar os clientes dessas classes, o subsistema compilador tambm inclui uma classe Compiler. A classe Compiler funciona como uma fachada: oferece aos clientes uma interface nica e simples para o subsistema compilador. Junta as classes que implementam a funcionalidade de um compilador, sem ocult-las completamente. O compilador Faade torna a vida mais fcil para a maioria dos programadores, sem, entretanto, ocultar a funcionalidade de nvel mais baixo dos poucos que a necessitam.

Compiler Compile()

classes do subsistema compilador


Stream

Scanner Parser

Token Symbol

BytecodeStream

ProgramNodeBuilder

ProgramNode

CodeGenerator

StatementNode ExpressionNode

StackMachineCodeGenerator

RISCCodeGenerator

V ariableNode

Aplicabilidade
Use o padro Faade quando: voc desejar fornecer uma interface simples para um subsistema complexo. Os subsistemas se tornam mais complexos medida que evoluem. A maioria dos padres, quando aplicados, resulta em mais e menores classes. Isso torna o subsistema mais reutilizvel e mais fcil de customizar, porm, tambm se torna mais difcil de usar para os clientes que no precisam customiz-lo. Uma fachada pode fornecer, por comportamento-padro, uma viso simples do sistema, que boa o suficiente para a maioria dos clientes. Somente os clientes que demandarem maior customizao necessitaro olhar alm da fachada; existirem muitas dependncias entre clientes e classes de implementao de uma abstrao. Ao introduzir uma fachada para desacoplar o subsistema dos clientes e de outros subsistemas, estar-se- promovendo a independncia e portabilidade dos subsistemas.

PADRES DE PROJETO

181

voc desejar estruturar seus subsistemas em camadas. Use uma fachada para definir o ponto de entrada para cada nvel de subsistema. Se os subsistemas so independentes, voc pode simplificar as dependncias entre eles fazendo com que se comuniquem uns com os outros exclusivamente atravs das suas fachadas.

Estrutura
classes do subsistema Faade

Participantes
Faade (Compiler) conhece quais as classes do subsistema so responsveis pelo atendimento de uma solicitao; delega solicitaes de clientes a objetos apropriados do subsistema. Classes de subsistema (Scanner, Parser, ProgramNode, etc.) implementam a funcionalidade do subsistema; encarregam-se do trabalho atribudo a elas pelo objeto Faade; no tm conhecimento da faade; isto , no mantm referncias para a mesma.

Colaboraes
Os clientes se comunicam com um subsistema atravs do envio de solicitaes para Faade, que as repassa para o(s) objeto(s) apropriado(s) do subsistema. Embora os objetos do subsistema executem o trabalho real, a faade pode ter de efetuar trabalho prprio para traduzir a sua interface para as interfaces de subsistemas. Os clientes que usam a faade no acessam os objetos do subsistema diretamente.

Conseqncias
O padro Faade oferece os seguintes benefcios: 1. Isola os clientes dos componentes do subsistema, dessa forma reduzindo o nmero de objetos com que os clientes tm que lidar e tornando o subsistema mais fcil de usar;

182

CAPTULO 4 PADRES ESTRUTURAIS

2. Promove um acoplamento fraco entre o subsistema e seus clientes. Freqentemente, os componentes num subsistema so fortemente acoplados. O acoplamento fraco permite variar os componentes do subsistema sem afetar os seus clientes. As Faades ajudam a estratificar um sistema e as dependncias entre objetos. Elas podem eliminar dependncias complexas ou circulares. Isso pode ser uma conseqncia importante quando o cliente e o subsistema so implementados independentemente. Reduzir as dependncias de compilao um aspecto vital em grandes sistemas de software. Voc deseja economizar tempo atravs da minimizao da recompilao, quando as classes do subsistema sofrem transformaes. A reduo das dependncias de compilao com o uso de faades pode limitar a recompilao necessria para uma pequena mudana num subsistema importante. Uma faade tambm pode simplificar a migrao de sistemas para outras plataformas, porque menos provvel que a construo de um subsistema exija construir todos os outros. 3. No impede as aplicaes de utilizarem as classes do subsistema caso necessitem faz-lo. Assim, voc pode escolher entre a facilidade de uso e a generalidade.

Implementao
Considere os seguintes aspectos quando implementar uma faade: 1. Reduo do acoplamento cliente-subsistema. O acoplamento entre os clientes e o subsistema pode ser ainda mais reduzido tornando Faade uma classe abstrata com subclasses concretas para diferentes implementaes de um subsistema. Ento, os clientes podem se comunicar com o subsistema atravs da interface da classe abstrata Faade. Este acoplamento abstrato evita que os clientes saibam qual a implementao de um subsistema que est sendo usada. Uma alternativa ao uso de subclasses configurar um objeto Faade com diferentes objetos-subsistema. Para customizar Faade simplesmente substitua um ou mais dos seus objetos-subsistema. 2. Classes de subsistemas: pblicas ou privadas? Um subsistema anlogo a uma classe no sentido de que ambos possuem interfaces e de que ambos encapsulam algo uma classe encapsula estado e operaes, enquanto um subsistema encapsula classes. E da mesma forma que til pensar na interface pblica e na interface privada de uma classe, podemos pensar na interface pblica e na interface privada de um subsistema. A interface pblica de um subsistema consiste de classes que todos os clientes podem acessar; a interface privada destina-se somente aos encarregados de estender o subsistema. A classe Faade naturalmente parte da interface pblica, porm, no a nica parte. Tambm outras classes do subsistema so usualmente pblicas. Por exemplo, as classes Parser e Scanner no subsistema compilador so parte da interface pblica. Tornar privadas as classes do subsistema seria til, porm, poucas linguagens orientadas a objetos fornecem suporte para isso. Tradicionalmente, tanto C++ como Smalltalk tm tido um espao global de nomes para classes. Contudo, recentemente o comit de padronizao de C++ acrescentou espaos de nomes linguagem [Str94], o que lhe permitir expor somente as classes pblicas do subsistema.

PADRES DE PROJETO

183

Exemplo de cdigo
Vamos imaginar mais de perto como colocar uma fachada num subsistema compilador. O subsistema compilador define uma classe BytecodeStream que implementa um stream de objetos Bytecode. Um objeto Bytecode encapsula um cdigo de bytes, o qual pode especificar instrues de mquina. O subsistema tambm define uma classe Token para objetos que encapsulam tokens na linguagem de programao. A classe Scanner aceita um stream em caracteres e produz um stream de tokens, um de cada vez.

A classe Parser usa um ProgramNodeBuilder para construir uma rvore de anlise a partir dos tokens de Scanner.

Parser chama ProgramNodeBuilder para construir a rvore de anlise incrementalmente. Estas classes interagem de acordo com o padro Builder (104).

184

CAPTULO 4 PADRES ESTRUTURAIS

A rvore de anlise composta de instncias de subclasses de ProgramNode tais como StatementNode, ExpressionNode, e assim por diante. A hierarquia ProgramNode um exemplo do padro Composite (160). ProgramNode define uma interface para manipular o n do programa e seus descendentes, se houver.

A operao Traverse aceita um objeto CodeGenerator. As subclasses de ProgramNode usam esse objeto para gerar cdigo de mquina na forma de objetos Bytecode num BytecodeStream. A classe CodeGenerator um visitor (ver Visitor, 305).

CodeGenerator tem subclasses, como StackMachineCodeGenerator e RISCCodeGenerator, que geram cdigo de mquina para diferentes arquiteturas de hardware. Cada subclasse de ProgramNode implementa Traverse para chamar Traverse nos seus objetos ProgramNode descendentes. Por sua vez, cada descendente faz o

mesmo para os seus descendentes, e assim por diante, recursivamente. Por exemplo, ExpressionNode define Traverse como segue:

PADRES DE PROJETO

185

As classes que discutimos at aqui compem o subsistema compilador. Agora introduziremos uma classe Compiler, uma faade que junta todas estas peas. Compiler fornece uma interface simples para compilar cdigo-fonte e gerar cdigo de mquina para uma mquina especfica.

Essa implementao codifica de maneira rgida o tipo de gerador de cdigo a ser usado, de modo que os programadores no especificam a arquitetura para a qual o cdigo est sendo gerado. Isso pode ser razovel se for sempre a mesma arquitetura para a qual ser gerado cdigo. Porm, se esse no for o caso, poderemos querer mudar o constructor de Compiler para aceitar como parmetro um CodeGenerator. Ento, os programas podero especificar o gerador a ser usado quando eles instanciarem Compiler. A fachada do Compilador pode tambm parametrizar outros participantes, tais como Scanner e ProgramNodeBuilder, o que acrescenta flexibilidade, mas tambm se desvia da misso do padro Faade, que simplificar a interface para os casos comuns.

Usos conhecidos
O exemplo de Compilador na seo Exemplo de cdigo foi inspirado pelo sistema Compilador do ObjectWorks/Smalltalk [Par90]. No framework para aplicaes da ET++[WGM88], uma aplicao pode ter incorporadas ferramentas de browsing para inspecionar os seus objetos em tempo de execuo. Essas ferramentas para inspeo (browsers) so implementadas num subsistema separado que inclui uma classe Faade chamada ProgrammingEnvironment. Essa fachada define operaes, tais como InspectObject e InspectClass, para acessar os browsers. Uma aplicao ET++ tambm pode ignorar o suporte incorporado para browsing. Nesse caso, ProgrammingEnvironment implementa essas solicitaes como operaes nulas; ou seja, no executam nada. Somente a subclasse ETProgrammingEnvironment implementa essas solicitaes como operaes que exibem os browsers correspondentes.

186

CAPTULO 4 PADRES ESTRUTURAIS

A aplicao no tem conhecimento se um ambiente de browsing est disponvel ou no; existe acoplamento abstrato entre aplicao e o subsistema de browsing. O sistema operacional Choices [CIRM93] usa fachadas para compor muitos frameworks num s. As abstraes-chave em Choices so processos, recursos de armazenamento e espaos de endereamento. Para cada uma dessas abstraes existe um subsistema correspondente, implementado como framework, que suporta a probabilidade do Choices para uma variedade de plataformas diferentes. Dois desses subsistemas tm um representante (isto , fachada). Esses representantes so FileSystemInterface (armazenamento) e Domain (espaos de endereamento).

Process

Domain Add(Memory, Address) Remove(Memory) Protect(Memory, Protection) RepairFault()

framework para memria virtual

AddressTranslation FindMemory(Address)

MemoryObject
BuildCache()

MemoryObjectCache

TwoLevelPageTable

PersistentStore

PagedMemoryObjectCache

File

Disk

Por exemplo, o framework para memria virtual tem Domain como sua fachada. Um Domain representa um espao de endereamento. Ele fornece o mapeamento entre endereos virtuais e deslocamentos para objetos na memria, arquivos ou armazenamento de suporte. As principais operaes no Domain suportam a adio de um objeto na memria em um endereo especfico, a remoo de um objeto da memria e o tratamento de um page fault. Como mostra o diagrama precedente, o subsistema para memria virtual usa os seguintes componentes internamente: MemoryObject (objeto de memria) representa um depsito de dados. MemoryObjetctCache acumula os dados de MemoryObject na memria fsica. MemoryObjectCache , na realidade, um padro Strategy (292) que localiza a poltica de caching. AddressTranslation encapsula a traduo de endereos do hardware. A operao RepairFault chamada sempre que uma interrupo de page fault ocorre. Domain acha o objeto de memria no endereo que est causando a falta da pgina e delega a operao RepairFault para o cache associado com aquele objeto de memria. Domains podem ser customizados mudando os seus componentes.

PADRES DE PROJETO

187

Padres relacionados
Abstract Factory (95) pode ser usado com Faade para fornecer uma interface para criao de objetos do subsistema de forma independente do subsistema. Uma Abstract Factory pode ser usada como uma alternativa a Faade para ocultar classes especficas de plataformas. Mediator (257) semelhante a Faade no sentido em que ele abstrai a funcionalidade de classes existentes. Contudo, a finalidade de Mediator abstrair comunicaes arbitrrias entre objetos-colegas, freqentemente centralizando funcionalidades que no pertencem a nenhum deles. Os colegas do Mediator esto cientes do mesmo e se comunicam com o Mediator em vez de se comunicarem uns com os outros diretamente. Por contraste, uma fachada meramente abstrai uma interface para objetos subsistemas para torn-los mais fceis de serem utilizados; ela no define novas funcionalidades e as classes do subsistema no tm conhecimento dela. Normalmente, somente um objeto Faade necessrio. Desta forma, objetos Faade so freqentemente Singletons (130).

FLYWEIGHT
Inteno

estrutural de objetos

Usar compartilhamento para suportar eficientemente grandes quantidades de objetos de granularidade fina.

Motivao
Algumas aplicaes poderiam se beneficiar da sua estruturao em objetos em todo o seu projeto, porm, uma implementao ingnua seria proibitivamente cara. Por exemplo, a maioria das implementaes de editores de documentos tem recursos para formatao e edio de textos que so, at certo ponto, modularizados. Editores de documento orientados a objetos usam objetos para representar elementos embutidos, tais como tabelas e figuras. No entanto, normalmente eles no chegam a usar objetos para cada caractere do documento, mesmo que, se assim o fizessem, promovessem ao mximo a flexibilidade na aplicao. Caracteres e elementos embutidos poderiam, ento, ser tratados uniformemente com relao maneira como so desenhados e formatados. A aplicao poderia ser estendida para suportar novos conjuntos de caracteres sem afetar outras funcionalidades. A estrutura dos objetos da aplicao poderia imitar a estrutura fsica do documento. O diagrama da pgina 188 mostra como o editor de documentos pode usar objetos para representar caracteres. O aspecto negativo de tal estruturao o seu custo. Mesmo documentos de tamanhos moderados podem requerer centenas de milhares de objetos-caracteres, o que consumir uma grande quantidade de memria, podendo incorrer num custo inaceitvel em tempo de execuo. O padro Flyweight descreve como compartilhar objetos para permitir o seu uso em granularidades finas sem incorrer num custo proibitivo.

188

CAPTULO 4 PADRES ESTRUTURAIS

objetos caracter

objetos linha

objeto coluna

Um flyweight (peso-mosca)* um objeto compartilhado que pode ser usado em mltiplos contextos simultaneamente. O flyweight funciona como um objeto independente em cada contexto ele indistinguvel de uma instncia do objeto que no compartilhada. Os flyweights no podem fazer hipteses ou asseres sobre o contexto no qual operam. Aqui, o conceito-chave entre estado intrnseco e extrnseco. O estado intrnseco armazenado no flyweight; ele consiste de informaes independentes do contexto do flyweight, desta forma tornando-o compartilhado. O estado extrnseco depende de e varia com o contexto do flyweight e, portanto, no pode ser compartilhado. Os objetos-cliente so responsveis pela passagem de estados extrnsecos para o flyweight quando necessrio. Os flyweights modelam conceitos ou entidades e so normalmente muito numerosos para serem representados por objetos. Por exemplo, um editor de documentos pode criar um flyweight para cada letra do alfabeto. Cada flyweight armazena o cdigo de um caractere, mas as coordenadas da sua posio do documento e seu estilo tipogrfico podem ser determinados a partir de algoritmos de layout do texto e dos comandos de formatao que esto em vigor sempre que o caractere aparece. O cdigo do caractere o estado intrnseco, enquanto que as outras informaes so extrnsecas. Logicamente, existe um objeto para cada ocorrncia de um dado caractere no documento:

coluna

linha

linha

linha

Fisicamente, contudo, existe um objeto compartilhado flyweight por caractere e ele aparece em diferentes contextos na estrutura do documento. Cada ocorrncia de um objeto de caractere referencia a mesma instncia no pool de objetos flyweight.
* N. de R.T.: Flyweight a categoria de lutadores mais leves nas lutas de box.

PADRES DE PROJETO

189

coluna

linha

linha

linha

a b c d e f g h i j k l m n o p q r s t u v w x y z

pool flyweight

A estrutura de classes para esses objetos mostrada a seguir. Glyph a classe abstrata de objetos grficos, alguns dos quais podem ser flyweights. As operaes que podem depender de um estado extrnseco recebem-no como um parmetro. Por exemplo, Draw e Intersects devem conhecer em qual contexto o glifo est, antes que possam executar o seu trabalho.
Glyph Draw(Context) Intersects(Point, Context)

Row filhos Draw(Context) Intersects(Point, Context)

Character Draw(Context) Intersects(Point, Context) char c

Column children Draw(Context) Intersects(Point, Context)

Um flyweight que representa a letra a somente armazena o correspondente cdigo de caractere; ele no necessita armazenar a sua localizao ou fonte tipogrfica. Os clientes fornecem a informao dependente do contexto que o flyweight necessita para desenhar a si prprio. Por exemplo, um glifo Row sabe onde seus filhos deveriam desenhar a si prprios, de maneira que sejam arrumados horizontalmente. Assim, ele pode passar para cada filho sua localizao na solicitao do desenho. Uma vez que o nmero de objetos de caracteres diferentes muito menor que o nmero de caracteres do documento, o nmero total de objetos substancialmente menor do que aquele que seria usado por uma implementao ingnua. Um documento no qual todos os caracteres aparecem na mesma fonte tipogrfica e na mesma cor colocar algo na ordem de 100 objetos de caracteres (aproximadamente o tamanho do conjunto de caracteres ASCII), independente do comprimento do documento. E uma vez que a maioria dos documentos no usa mais do que 10 diferentes combinaes de fonte-cor, esse nmero no crescer muito. Dessa maneira, uma abstrao de objeto se torna prtica para caracteres individuais.

Aplicabilidade
A eficincia do padro Flyweight depende muito de como e onde ele usado. Aplique o padro Flyweight quando todas as condies a seguir forem verdadeiras:

190

CAPTULO 4 PADRES ESTRUTURAIS

uma aplicao utiliza um grande nmero de objetos; os custos de armazenamento so altos por causa da grande quantidade de objetos; a maioria dos estados de objetos pode ser tornada extrnseca; muitos grupos de objetos podem ser substitudos por relativamente poucos objetos compartilhados, uma vez que estados extrnsecos so removidos; a aplicao no depende da identidade dos objetos. Uma vez que objetos Flyweights podem ser compartilhados, testes de identidade produziro o valor verdadeiro para objetos conceitualmente distintos.

Estrutura
FlyweighFactory GetFlyweight(key) flyweights

Flyweight Operation(extrinsicState)

if (flyweyght[key] exists) { return existing flyweight; } else { create new flyweight; add it to pool of flyweights; return the new flyweight; }

ConcreteFlyweight Operation(extrinsicState) IntrinsicState

UnsharedConcreteFlyweight Operation(extrinsicState) allState

Client

O seguinte diagrama de objetos mostra como flyweights so compartilhados.


aClient aClient

flyweight pool
aFlyweightFactory flyweights aConcreteFlyweight intrinsicState aConcreteFlyweight intrinsicState

Participantes
Flyweight (Glyph) declara uma interface atravs da qual flyweights podem receber e atuar sobre estados extrnsecos.

PADRES DE PROJETO

191

ConcreteFlyweight (Character) implementa a interface de Flyweight e acrescenta armazenamento para estados intrnsecos, se houver. Um objeto ConcreteFlyweight deve ser compartilhvel. Qualquer estado que ele armazene deve ser intrnseco, ou seja, independente do contexto do objeto ConcreteFlyweight. UnsharedConcreteFlyweight (Row, Column) nem todas as subclasses de Flyweight necessitam ser compartilhadas. A interface de Flyweight habilita o compartilhamento; ela no o fora ou o garante. comum para objetos UnsharedConcreteFlyweight no compartilhar objetos ConcreteFlyweight como filhos em algum nvel da estrutura de objetos de Flyweight (tal como o fazem as classes Row e Column). FlyweightFactory cria e gerencia objetos flyweight; garante que os flyweights sejam compartilhados apropriadamente. Quando um cliente solicita um flyweight, um objeto FlyweightFactory fornece uma instncia existente ou cria uma, se nenhuma existir. Client mantm uma referncia para flyweight(s); computa ou armazena o estado extrnseco do flyweight(s).

Colaboraes
O estado que um flyweight necessita para funcionar deve ser caracterizado como intrnseco ou como extrnseco. Um estado intrnseco armazenado no objeto ConcreteFlyweight; um estado extrnseco armazenado ou computado por objetos Client. Os clientes passam este estado para o flyweight quando invocam suas operaes. Os clientes no deveriam instanciar ConcreteFlyweights diretamente. Eles devem obter objetos ConcreteFlyweight exclusivamente do objeto FlyweightFactory para garantir que sejam compartilhados de forma apropriada.

Conseqncias
Os flyweights podem introduzir custos de tempo de execuo associados com a transferncia, procura e/ou computao de estados extrnsecos, especialmente se esses anteriormente estavam armazenados como um estado intrnseco. Contudo, tais custos so compensados pelas economias de espao, as quais aumentam medida que mais flyweights so compartilhados. As economias de armazenamento so uma funo de vrios fatores: a reduo do nmero total de instncias obtida com o compartilhamento; a quantidade de estados intrnsecos por objeto; se o estado extrnseco computado ou armazenado. Quanto mais flyweights so compartilhados, maior a economia de armazenagem. A economia aumenta com a quantidade de estados compartilhados. As maiores economias ocorrem quando os objetos usam quantidades substanciais tanto de estados intrnsecos como de estados extrnsecos, e os estados extrnsecos podem ser melhor computados do que armazenados. Ento voc economiza a armazenagem de

192

CAPTULO 4 PADRES ESTRUTURAIS

duas maneiras: o compartilhamento reduz o custo dos estados intrnsecos e voc troca estados extrnsecos por tempo de computao. O padro Flyweight freqentemente combinado com o padro Composite (160) para representar uma estrutura hierrquica tal como um grfico com ns de folhas compartilhados. Uma conseqncia do compartilhamento que os ns de folhas flyweight no podem armazenar um apontador para os seus pais. Em vez disso, o apontador do pai passado para o flyweight como parte do seu estado extrnseco. Isso tem um impacto importante sobre a forma como os objetos na hierarquia se comunicam uns com os outros.

Implementao
Considere os seguintes aspectos ao implementar o padro Flyweight: 1. Remoo dos estados extrnsecos. A aplicabilidade do padro determinada em grande medida pela facilidade de identificao de estados extrnsecos e pela sua remoo dos objetos compartilhados. A remoo dos estados extrnsecos no ajudar a reduzir os custos de armazenamento se existirem tantos tipos diferentes de estados extrnsecos quanto existem objetos antes do compartilhamento. Idealmente, o estado extrnseco pode ser computado a partir de uma estrutura de objeto separada, estrutura essa com requisitos de armazenamento muito menores. Por exemplo, no nosso editor de documento, podemos armazenar um mapa de informaes tipogrficas numa estrutura separada ao invs de armazenar a fonte tipogrfica e o estilo do tipo com cada objeto do caracter. O mapa mantm um acompanhamento dos blocos de caracteres com os mesmos atributos tipogrficos. Quando um caractere desenha a si prprio, recebe seus atributos tipogrficos como um efeito colateral do percurso desenvolvido pelo processo de desenho. Uma vez que documentos normalmente usam poucas fontes de estilos tipogrficos diferentes, armazenar essa informao externamente a cada objeto- caractere muito mais eficiente do que armazenla internamente. 2. A gerncia dos objetos compartilhados. Se objetos so compartilhados, os clientes no devem instanci-los diretamente. FlyweightFactory permite aos clientes a localizao de um flyweight especfico. Objetos FlyweightFactory freqentemente usam uma memria associativa para permitir aos clientes encontrar os flyweights de seu interesse. Por exemplo, a flyweight factory no exemplo do editor de documentos pode manter uma tabela de flyweights indexada por cdigos de caracteres. O gerenciador retorna o flyweight apropriado, uma vez que ele recebeu o seu cdigo, criando o flyweight se ele ainda no existe. A capacidade de compartilhamento tambm implica em alguma forma de contagem de referncias ou de garbage collection para recuperar o espao de um flyweight quando este no for mais necessrio. Contudo, nenhum desses recursos necessrio se o nmero de flyweights for fixo e pequeno (por exemplo, flyweights para o conjunto de caracteres ASCII). Nesse caso, vale a pena manter os flyweights disponveis permanentemente.

PADRES DE PROJETO

193

Exemplo de cdigo
Retornando para o nosso exemplo do formatador de documentos, podemos definir uma classe base Glyph para objetos grficos flyweight. Logicamente, glifos so Composite (ver Composite (160), que tm atributos grficos e que podem desenhar a si mesmos. Aqui, ns focalizamos somente o atributo fonte tipogrfica. Porm, a mesma abordagem pode ser usada para quaisquer outros atributos grficos que um glifo possa ter.

A subclasse Character simplesmente armazena um cdigo de caractere:

Para no ter que alocar espao para um atributo fonte em cada glifo, armazenaremos o atributo extrinsecamente num objeto GlyphContext. Um GlyphContext atua como um repositrio de estados extrnsecos. Ele mantm um mapeamento compacto entre um glifo e sua fonte tipogrfica (e quaisquer outros atributos grficos que ele possa ter) em diferentes contextos. Qualquer operao que necessita conhecer a fonte do glifo num dado contexto ter uma instncia de GlyphContext passada para ela como um parmetro. A operao pode ento consultar o GlyphContext para a fonte tipogrfica naquele contexto. O contexto depende da localizao do glifo na estrutura dos glifos. Portanto, as operaes de iterao e manipulao de filhos de Glyph devero atualizar GlyphContext sempre que forem utilizadas.

194

CAPTULO 4 PADRES ESTRUTURAIS

GlyphContext deve ser mantido informado da posio atual na estrutura de glifos durante o percurso. GlyphContext::Next incrementa _index medida que o percurso se processa. As subclasses de Glyph que tm filhos (por exemplo, Row e Column) devem implementar Next de maneira que ele chame GlyphContext::Next em cada ponto do percurso. GlyphContext::GetFont usa o ndice como uma chave para uma estrutura BTree que armazena o mapeamento entre glifo e fonte. Cada n na rvore rotulado com o comprimento da string para a qual ele fornece informao sobre fontes tipogrficas. As folhas na rvore apontam para uma fonte tipogrfica, enquanto que os ns interiores quebram o string em substrings, uma para cada filho. Considere o seguinte extrato de uma composio de glifos:

A estrutura BTree para informaes sobre fontes tipogrficas pode se assemelhar a

PADRES DE PROJETO

195

500

300

199

100

194

187

Times 24

Times Italic 12

Times 12

Time Bold 12

Courier 24

Os ns interiores definem intervalos para ndices de glifos. A BTree atualizada em resposta a mudanas nas fontes e sempre que forem acrescentados ou removidos glifos estrutura de glifo. Por exemplo, assumindo que estamos no ndice 102 do percurso, o seguinte cdigo determina a fonte de cada caractere na palavra expect para aquele do texto adjacente (isto , times12, uma instncia de Font para Times Roman de 12 pontos):

A nova estrutura BTree (com as mudanas mostradas em preto) se assemelha a:

500

300

199

187

Times 24

Times 12

Time Bold 12

Courier 24

Suponha que adicionemos a palavra dont (incluindo um espao posterior), em Times Italic de 12 pontos, antes de expect. O seguinte cdigo informa o gc desse evento, assumindo que ele ainda est no ndice 102: a estrutura BTree se torna

196

CAPTULO 4 PADRES ESTRUTURAIS

506

306

199

100

200

187

Times 24

Times Italic 12

Times 12

Time Bold 12

Courier 24

Quando o GlyphContext consultado acerca da fonte do glifo corrente, ele desce a BTree, acrescentando ndices medida que procede at encontrar a fonte para o ndice corrente. Porque a freqncia das mudanas de fontes relativamente baixa, a rvore permanece pequena em relao ao tamanho da estrutura de glifo. Isso mantm os custos de armazenagem baixos sem um aumento desproporcional no tempo de pesquisa.3 O ltimo objeto de que necessitamos um FlyweightFactory que cria glifos e garante que eles so compartilhados apropriadamente. A classe GlyphFactory instancia Character e outros tipos de glifos. Ns somente compartilhamos objetos Character; glifos compostos so muito menos numerosos e os seus estados importantes (isto , seus filhos) so intrnsecos.

O array _character contm apontadores para glifos Character indexados por cdigo do caractere. O array iniciado com zeros no constructor

PADRES DE PROJETO

197

CreateCharacter procura por um caractere no glifo-caractere no array e retorna o correspondente glifo, se ele existir. Se no existir, CreateCharacter o criar, o colocar no array, retornando-o:

As outras operaes simplesmente instanciaro um novo objeto a cada vez que forem chamadas, uma vez que glifos de no-caracteres no sero compartilhados:

Poderamos omitir essas operaes e permitir aos clientes instanciarem diretamente glifos no-compartilhados. Contudo, se mais tarde decidirmos tornar esses glifos compartilhveis, teremos que mudar o cdigo do cliente que os cria.

Usos conhecidos
O conceito de objetos flyweight foi primeiramente descrito e explorado como uma tcnica de projeto no InterViews 3.0 [CL90]. Os seus desenvolvedores construram um poderoso editor de documentos chamado Doc como demonstrao do conceito [CL92]. Doc usa objetos de glifos para representar cada caractere no documento. O editor constri uma instncia de Glyph para cada caractere de um estilo particular (o que define seus atributos grficos); da o estado intrnseco de um caractere consistir no cdigo de um caractere e sua informao de estilo (um ndice para uma tabela de estilos)4. Isso significa que somente a posio extrnseca, tornando Doc rpido. Os documentos so representados por uma classe Document, que tambm funciona como FlyweightFactory. Medies efetuadas com o Doc mostraram que o compartilhamento de caracteres flyweight bastante eficiente. Num caso tpico, um documento contendo 180 mil caracteres exigiu a alocao de somente 480 objetos-caractere. O ET++ [WGM88] utiliza flyweights para suportar a independncia de estilos de apresentao5. O estilo de apresentao afeta o layout dos elementos da interface do usurio (por exemplo, barras de rolamento, botes, menus conhecidos coletivamente como widgets) e suas decoraes (por exemplo, sombreamentos, chanframentos). Um widget delega todo o seu comportamento de layout e de desenho para um objeto Layout separado. Mudar o objeto Layout tambm muda o estilo de aparncia, mesmo em tempo de execuo Para cada classe Widget h uma classe Layout correspondente (p. ex., ScrollbarLayout, MenubarLayout, etc). Um problema bvio dessa abordagem que a utlizao de objetos de Layout separados duplica o nmero de objetos de interface para o usurio. Para cada objeto de interface h um objeto de Layout adicional. Para evitar essa sobrecarga, os objetos de Layout so implementados como flyweights. Eles so bons flyweights porque lidam principalmente com a definio do comportamento e fcil pass-los com o pouco de estado intrnseco de que necessitam para desenhar e formatar um objeto.

198

CAPTULO 4 PADRES ESTRUTURAIS

Os objetos Layout so criados e administrados por objetos Look. A classe Look um Abstract Factory (95) que recupera um determinado objeto Layout com operaes, tais como GetButtonLayout, GetMenuBar, e assim por diante. Para cada estilo de aparncia existe uma correspondente subclasse Look (por exemplo, MotifLook, OpenLook) que fornece os objetos Layout apropriados. A propsito, os objetos Layout so essencialmente estratgias (ver Strategy, 292). Eles so um exemplo de um objeto strategy implementado como um flyweight.

Padres Relacionados
O padro Flyweight freqentemente combinado com o padro Composite para implementar uma estrutura hierrquica lgica em termos de um grfico acclico direcionado com ns de folhas compartilhados. Freqentemente melhor implementar objetos State (284) e Strategy (292) como flyweights.

PROXY
Inteno

estrutural de objetos

Fornece um substituto (surrogate) ou marcador da localizao de outro objeto para controlar o acesso a esse objeto.

Tambm conhecido como


Surrogate

Motivao
Uma razo para controlar o acesso a um objeto adiar o custo integral de sua criao e inicializao at o momento em que realmente necessitamos us-lo. Considere um editor de documentos que pode embutir objetos grficos num documento. Alguns objetos grficos, tais como grandes imagens rasterizadas, podem ser muito caros para serem criados. A abertura de documentos deveria ser rpida, assim, deveramos evitar a criao, de uma s vez, de todos os objetos caros quando o documento aberto. De qualquer forma, isso no necessrio porque nem todos esses objetos estaro visveis no documento ao mesmo tempo. Essas restries sugerem a criao de tais objetos caros sob demanda, o que, neste caso, ocorre quando uma imagem se torna visvel. Mas o que colocamos num documento no lugar da imagem? E como podemos ocultar o fato de que o objeto criado sob demanda de maneira que no compliquemos a implementao do editor? Por exemplo, essa implementao no deveria produzir impacto sobre o cdigo de apresentao e formatao.

PADRES DE PROJETO

199

A soluo usar outro objeto, um proxy (procurador), que funciona como um substituto temporrio da imagem real. O proxy funciona exatamente como a imagem e toma conta da sua instanciao quando a mesma for necessria.
aTextDocument image anImageProxy fileName anImage data

in memory

on disk

O proxy de imagem cria a imagem real somente quando o editor de documentos solicita ao mesmo exibir a si prprio invocando sua operao Draw. O proxy repassa as solicitaes subseqentes diretamente para a imagem. Portanto, ele deve manter uma referncia para a imagem aps cri-la. Vamos assumir que as imagens so armazenadas em arquivos separados. Neste caso, podemos usar o nome do arquivo como referncia para o objeto real. O proxy tambm armazena sua extenso, ou seja, sua largura e altura. A extenso permite ao proxy esconder as solicitaes sobre o seu tamanho, oriundas do formatador, sem ter que efetivamente instanciar a imagem. O seguinte diagrama de classe ilustra esse exemplo em mais detalhes.
DocumentEditor

Graphic Draw() GetExtent() Store() Load()

Image Draw() GetExtent() Store() Load() imageImp extent

ImageProxy image Draw() GetExtent() Store() Load() fileName extent

if (image == 0) { image = LoadImage(fileName); } image>Draw() if (image == 0) { return extent } else { return image>GetExtent(); }

O editor de documentos acessa as imagens embutidas atravs da interface definida pela classe abstrata Graphic. ImageProxy uma classe para imagens que so criadas sob demanda. ImageProxy mantm o nome do arquivo como uma referncia para a imagem no disco. O nome do arquivo passado como um argumento para o constructor de ImageProxy. Um ImageProxy tambm armazena os limites da imagem (extent) e uma referncia para a instncia real de Image (filename). Essa referncia no ser vlida at que o Proxy instancie a imagem real. A operao Draw garante que a imagem instanciada antes de repassar a ela a solicitao. GetExtent repassa a solicitao para a imagem somente se ela estiver instanciada; caso contrrio, ImageProxy retorna a extenso (extent) armazenada.

200

CAPTULO 4 PADRES ESTRUTURAIS

Aplicabilidade
O padro Proxy aplicvel sempre que h necessidade de uma referncia mais verstil, ou sofisticada, do que um simples apontador para um objeto. Aqui apresentamos diversas situaes comuns nas quais o padro Proxy aplicvel: 1. Um remote proxy fornece um representante local para um objeto num espao de endereamento diferente. NEXTSTEP[Add94] usa a classe NXProxy para esta finalidade. Coplien [Cop92] chama este tipo de proxy de um embaixador (ambassador). 2. Um virtual proxy cria objetos caros sob demanda. O ImageProxy descrito na seo Motivao um exemplo de um proxy deste tipo. 3. Um protection proxy controla o acesso ao objeto original. Os proxies de proteo so teis quando os objetos devem ter diferentes direitos de acesso. Por exemplo, KernelProxies, no sistema operacional Choices [CIRM93], fornece um acesso protegido aos objetos do sistema operacional. 4. Um smart reference um substituto para um simples pointer que executa aes adicionais quando um objeto acessado. Usos tpicos incluem: contar o nmero de referncias para o objeto real, de modo que o mesmo possa ser liberado automaticamente quando no houver mais referncias (tambm chamadas de smart pointers [Ede92]); carregar um objeto persistente para a memria quando ele for referenciado pela primeira vez; verificar se o objeto real est bloqueado antes de ser acessado, para assegurar que nenhum outro objeto possa mud-lo.

Estrutura
Client

Subject Request() ...

RealSubject Request() ...

realSubject

Proxy Request() ...

... realSubject>Request(); ...

Participantes
Proxy (ImageProxy) mantm uma referncia que permite ao proxy acessar o objeto real (real subject). O proxy pode referenciar um Subject se as interfaces de RealSubject e Subject forem as mesmas; fornece uma interface idntica a de Subject, de modo que o proxy possa substitur o objeto real (real subject).

PADRES DE PROJETO

201

controla o acesso ao objeto real e pode ser responsvel pela sua criao e excluso. outras responsabilidades dependem do tipo de proxy: remote proxies so responsveis pela codificao de uma solicitao e de seus argumentos, bem como pelo envio da solicitao codificada para o objeto real num espao de endereamento diferente; virtual proxies podem manter informaes adicionais sobre o objeto real, de maneira que possam postergar o acesso ao mesmo. Por exemplo, o ImageProxy da seo Motivao armazena a extenso da imagem real; protection proxies verificam se quem chama tem as permisses de acesso requeridas para executar uma consulta. Subject (Graphic) define uma interface comum para RealSubject e Proxy, de maneira que um Proxy possa ser usado em qualquer lugar em que um RealSubject esperado. RealSubject (Image) Define o objeto real que o proxy representa.

Colaboraes
Dependendo de seu tipo, Proxy repassa solicitaes para RealSubject quando apropriado.

Conseqncias
O padro Proxy introduz um nvel de referncia indireta no acesso a um objeto. A referncia indireta adicional tem muitos usos, dependendo do tipo de proxy: 1. Um proxy remoto pode ocultar o fato de que um objeto reside num espao de endereamento diferente. 2. Um proxy virtual pode executar otimizaes, tais como a criao de um objeto sob demanda. 3. Tanto proxies de proteo como smart references permitem tarefas adicionais de organizao (housekeeping) quando um objeto acessado. Existe uma outra otimizao que o padro Proxy pode ocultar do cliente. Chamada de copy-on-write, est relacionada criao de um objeto sob demanda. Copiar um objeto grande e complicado pode ser uma operao cara do ponto de vista computacional. Se a cpia nunca modificada, ento no h necessidade de incorrer neste custo. Pela utilizao de um proxy para postergar o processo de cpia, garantimos que pagamos o preo da cpia do objeto somente se ele for modificado. Para copy-on-write funcionar, o objeto deve ter suas referncias contadas. Copiar o proxy no far nada mais do que incrementar esta contagem de referncias. Somente quando o cliente solicita uma operao que modifica o objeto, o proxy realmente o copia. Nesse caso, o proxy tambm deve diminuir a contagem de referncias do objeto. Quando a contagem de referncias se torna zero, o objeto deletado. A abordagem copy-on-write pode reduzir significativamente o custo computacional da cpia de objetos muito pesados.

202

CAPTULO 4 PADRES ESTRUTURAIS

Implementao
O padro Proxy pode explorar as seguintes caractersticas de linguagem: 1. Sobrecarregar o operador de acesso a membros em C++. C++ suporta a sobrecarga (overloading) de operator>, o operador de acesso a membros. A sobrecarga desse operador permite a execuo um trabalho adicional sempre que um objeto de referenciado. Isso pode ser til para implementar alguns tipos de proxy; o proxy se comporta exatamente como um apontador. O exemplo seguinte ilustra o uso dessa tcnica para implementar um virtual proxy chamado ImagePtr.

Os operadores > e * usam LoadImage para retornar _image para os clientes (fazendo a carga, se necessrio).

Essa abordagem permite invocar operaes de Image por meio de objetos ImagePtr sem ter o trabalho de tornar as operaes parte da interface de
ImagePtr:

PADRES DE PROJETO

203

Observe como o proxy image funciona como apontador, mas no declarado como tal para uma Image. Isso significa que voc no pode us-lo exatamente como um apontador real para um Image. Por isso, nesta abordagem, os clientes devem tratar os objetos Image e ImagePtr de modo diferente. A sobrecarga do operador de acesso a membro no uma boa soluo para todo tipo de proxy. Alguns proxies precisam conhecer precisamente qual a operao chamada, e a sobrecarga do operador de acesso a membro no funciona nesses casos. Considere o exemplo de um proxy virtual da seo Motivao. A imagem deveria ser carregada num momento especfico quando a operao Draw chamada e no sempre que a imagem referenciada. A sobrecarga do operador de acesso no permite esta distino. Nesse caso, devemos implementar manualmente cada operao de proxy que repassa a solicitao para o objeto. Normalmente, essas operaes so muito similares umas s outras, como demonstra o Exemplo de cdigo. Todas as operaes verificam se a solicitao legal, se existe objeto original, etc, antes de repassar a solicitao para o objeto. tedioso escrever este cdigo toda vez. Assim, comum usar um pr-processador para ger-lo automaticamente. 2. Usando doesNotUnderstand em Smalltalk. Smalltalk fornece um gancho que voc pode usar para suportar um encaminhamento automtico de solicitaes. Smalltalk chama doesNotUnderstand: aMessage quando um cliente envia uma mensagem para um receptor que no tem um mtodo correspondente. A classe Proxy pode redefinir doesNotUnderstand de maneira que a mensagem seja repassada para o seu objeto. Para garantir que uma solicitao seja repassada para o objeto e no simplesmente absorvida em silncio pelo proxy, possvel definir uma classe Proxy que no entende nenhuma mensagem. Smalltalk permite a voc fazer isso definindo Proxy como uma classe que no tem uma superclasse6. A principal desvantagem de doesNotUnderstand: que a maioria dos sistemas Smalltalk tem algumas poucas mensagens especiais que so tratadas diretamente pela mquina virtual, e essas no produzem a usual procura de mtodos ( method look-up ). A nica que usualmente implementada em Object (podendo, assim, afetar proxies) a operao identidade ==. Se voc pretende usar doesNotUnderstand: para implementar Proxy, deve contornar esse problema. No deve esperar operadores de identidade em proxies com o significado de identidade nos seus objetos reais. Uma desvantagem adicional que doesNotUnderstand: foi desenvolvido para o tratamento de erros, e no para o uso com proxies, e assim, em geral, no muito rpida. 3. Proxy no tem sempre que conhecer o tipo do objeto real. Se uma classe Proxy puder tratar com seu objeto exclusivamente atravs de uma interface abstrata, no h necessidade de criar uma classe Proxy para cada classe RealSubject; o proxy

204

CAPTULO 4 PADRES ESTRUTURAIS

pode tratar todas as classes RealSubject de maneira uniforme. Porm, se os Proxies forem instanciar RealSubjects (como no caso de um proxy virtual), ento eles tm que conhecer a classe concreta. Um outro tpico de implementao envolve a maneira de referenciar o objeto antes de ele ser instanciado. Alguns proxies tm que referenciar seu objeto quer ele esteja no disco ou na memria. Isso significa que eles devem usar alguma forma de identificador de objeto independente do espao de endereamento. Com essa finalidade, na seo Motivao usamos o nome do arquivo.

Exemplo de cdigo
O cdigo seguinte implementa dois tipos de proxy: o proxy virtual descrito na seo Motivao e um proxy implementado com doesNotUnderstand: 7. 1. Um proxy virtual. A classe Graphic define a interface para objetos grficos:

A classe Image implementa a interface de Graphic para exibir arquivos de imagens. Image redefine HandleMouse para permitir aos usurios redimensionar interativamente a imagem.

O ImageProxy tem a mesma interface que Image:

PADRES DE PROJETO

205

O constructor salva uma cpia local do nome do arquivo que armazena a imagem, e inicializa _extent e _image:

A implementao de GetExtent retorna a extenso armazenada, se possvel; caso contrrio, a imagem carregada a partir do arquivo. Draw carrega a imagem e HandleMouse repassa o evento para a imagem real.

A operao Save salva a extenso armazenada da imagem e o nome do arquivo de imagem em um stream. Load recupera esta informao e inicia os membros correspondentes.

206

CAPTULO 4 PADRES ESTRUTURAIS

Finalmente, suponha que temos uma classe TextDocument que pode conter objetos Graphic:

Podemos inserir um ImageProxy no texto de documento desta forma:

2. Proxies que usam doesNotUnderstand. Voc pode criar proxies genricos em Smalltalk atravs da definio de classes cuja superclasse nil8 e definindo o mtodo doesNotUnderstand: para tratar mensagens. A seguinte soluo assume que o proxy tem um mtodo realSubject que retorna seu objeto real. No caso de ImageProxy, este mtodo verificaria se o objeto Image foi criado, criaria, se necessrio, e, finalmente, o retornaria. Ele utiliza perform:withArguments: para executar a mensagem que est sendo interceptada e tratada no objeto real.

O argumento para doesNotUnderstand: uma instncia de Message que representa a mensagem no compreendida pelo proxy. Assim, o proxy responde a todas as mensagens, garantindo que o objeto real existe antes de repassar a mensagem para ele. Uma das vantagens de doesNotUnderstand: poder executar processamentos arbitrrios. Por exemplo, poderamos produzir um proxy de proteo, especificando um conjunto de mensagens legalMessages para serem aceitas, dando ento ao proxy o seguinte mtodo:

PADRES DE PROJETO

207

Esse mtodo verifica se a mensagem uma mensagem legal antes de repassla para o objeto real. Se no for uma mensagem legal, ento ele enviar error: para o proxy, o que resultar num ciclo infinito de erros, a menos que o proxy defina error:. Conseqentemente, a definio de error: deveria ser copiada da classe Object juntamente com quaisquer outros mtodos que ela (error:) usa.

Usos conhecidos
O exemplo de um proxy virtual na seo Motivao proveniente das classes de blocos de construo de textos da ET++. O NEXTSTEP [Add94] usa proxies (instncias da classe NXProxy) como representantes locais de objetos que podem ser distribudos. Um servidor cria proxies para objetos remotos quando so solicitados pelos clientes. Ao receber uma mensagem, o proxy a codifica junto com os seus argumentos e ento repassa a mensagem codificada para o objeto remoto. De maneira similar, o objeto codifica quaisquer resultados que retornem, mandando-os de volta para o objeto NXProxy. McCullough [McC87] discute o uso de proxies em Smalltalk para acessar objetos remotos. Pascoe [Pas86] discute como obter efeitos colaterais nas chamadas de mtodos e controle de acesso com encapsulators (encapsuladores).

Padres relacionados
Adapter (140): um adaptador fornece uma interface diferente para o objeto que adapta. Em contraste, um proxy fornece a mesma interface como seu objeto. Contudo, um proxy usado para proteo de acesso pode se recusar a executar uma operao que o objeto executar. Assim, na prtica, sua interface pode ser efetivamente um subconjunto da interface do objeto. Decorator (170): embora decoradores possam ter implementaes semelhantes s de proxies, os decoradores tm uma finalidade diferente. Um decorador acrescenta uma ou mais responsabilidades a um objeto, enquanto que um proxy controla o acesso a um objeto. Proxies variam em grau com relao maneira em que eles podem ser implementados como um decorador (decorator). Um proxy de proteo pode ser implementado exatamente como um decorador. Por outro lado, um proxy remoto no conter uma referncia direta para o seu objeto real, mas somente uma referncia indireta, tal como host ID e endereo local no host. Um proxy virtual comear com uma referncia indireta, tal como um nome de arquivo, mas eventualmente obter e usar uma referncia direta.

Discusso sobre padres estruturais


Voc deve ter observado semelhanas entre os padres estruturais, especialmente nos seus participantes e suas colaboraes. Provavelmente isso acontece porque os padres estruturais dependem do mesmo pequeno conjunto de mecanismos de

208

CAPTULO 4 PADRES ESTRUTURAIS

linguagem para estruturao do cdigo e dos objetos: herana simples e mltipla para padres baseados em classes e composio de objetos para padres de objetos. Porm, as semelhanas escondem as diferentes intenes destes padres. Nesta seo comparamos e contrastamos grupos de padres estruturais para dar uma idia dos seus mritos relativos.

Adapter versus Bridge


Os padres Adapter (140) e Bridge (151) tm alguns atributos em comum. Ambos promovem a flexibilidade ao fornecer um nvel de endereamento indireto para outro objeto. Ambos envolvem o repasse de solicitaes para este objeto, partindo de uma interface diferente da sua prpria. A diferena-chave entre esses padres est na suas intenes. O Adapter focaliza na soluo de incompatibilidades entre duas interfaces existentes. Ele no focaliza em como essas interfaces so implementadas, tampouco considera como podem evoluir independentemente. uma forma de tornar duas classes, projetadas independentemente, capazes de trabalhar em conjunto sem ter que reimplementar uma ou outra. Por outro lado, Bridge estabelece uma ponte entre uma abstrao e suas (potencialmente numerosas) implementaes. Ele fornece uma interface estvel aos clientes, ainda que permita variar as classes que a implementam. Tambm acomoda novas implementaes medida que o sistema evolui. Como resultado dessas diferenas, Adapter e Bridge so freqentemente usados em pontos diferentes do ciclo de vida do software. Um adapter freqentemente se torna necessrio quando voc descobre que duas classes incompatveis deveriam trabalhar em conjunto, em geral, para evitar a replicao de cdigo. O acoplamento no foi previsto. Em contraste, o usurio de bridge compreende desde o incio que uma abstrao deve ter vrias implementaes, e ambas (abstrao e implementao) podem evoluir independentemente. O padro Adapter faz as coisas funcionarem aps elas terem sido projetadas; o Bridge as faz funcionarem antes que existam. Isso no significa que o Adapter seja algo inferior ao Bridge; cada padro meramente trata de um problema diferente. Voc pode pensar em uma faade (fachada, ver Faade, pg. 179) como um Adapter para um conjunto de outros objetos. Porm, essa interpretao se esquece do fato de que faade define uma nova interface, enquanto que um adaptador reutiliza uma interface pr-existente. Lembre-se de que um adaptador faz com que duas interfaces existentes funcionem em conjunto, ao contrrio de definir uma interface inteiramente nova.

Composite versus Decorator versus Proxy


Os padres Composite (160) e Decorator (170) tm diagramas de estrutura similares, refletindo o fato de que ambos dependem de composio recursiva para organizar um nmero indefinido de objetos. Esse aspecto comum pode tent-lo a pensar que um objeto decorator um composite degenerado, mas isso ignora o significado do padro Decorator. A similaridade termina na composio recursiva, uma vez mais, porque as intenes so diferentes. O Decorator projetado para permitir acrescentar responsabilides a objetos sem usar subclasses. Ele evita a exploso de subclasses que podem surgir da tentativa de cobrir estaticamente todas as combinaes de responsabilidades. O Composite tem uma inteno diferente. Ele focaliza a estruturao de classes de maneira que muitos

PADRES DE PROJETO

209

objetos relacionados possam ser tratados de maneira uniforme, e mltiplos objetos possam ser tratados como um s. O seu foco no est na decorao, mas sim na representao. Essas intenes so distintas, porm complementares. Em conseqncia disso, os padres Composite e Decorator so freqentemente usados em conjunto. Ambos conduzem ao tipo de projeto no qual voc pode construir aplicaes apenas conectando objetos uns aos outros, sem definir novas classes. Haver uma classe abstrata com algumas subclasses que so composites, algumas que so decorators e algumas que implementam os blocos de construo fundamentais do sistema. Neste caso, tanto composites como decorators tero uma interface comum. Do ponto de vista do padro Decorator, um composite um ConcreteComponent. Do ponto de vista do padro Composite, um decorator uma Leaf (folha). Naturalmente, no precisam ser usados juntos e, como vimos, suas intenes so bem diferentes. Um outro padro com uma estrutura similar do Decorator o Proxy (198). Ambos os padres descrevem como fornecer um nvel de endereamento indireto para um objeto, e as implementaes, tanto do objeto proxy como do objeto decorator; mantm uma referncia para um outro objeto para o qual repassam solicitaes. Contudo, uma vez mais, eles tm finalidades diferentes. Como o Decorator, o padro Proxy compe o objeto e fornece uma interface idntica para os clientes. Diferentemente do Decorator, o padro Proxy no est preocupado em incluir ou excluir propriedades dinamicamente e no est projetado para composio recursiva. Sua inteno fornecer um substituto para um objeto quando for inconveniente ou indesejvel acess-lo diretamente porque , por exemplo, est numa mquina remota, tem acesso restrito ou persistente. No padro Proxy, o objeto fornece as funcionalidades-chave e o proxy fornece (ou recusa) acesso ao objeto. No Decorator, o componente fornece somente parte da funcionalidade e um ou mais decoradores fornecem o restante. O Decorator trata a situao em que a funcionalidade total de um objeto no pode ser determinada em tempo de compilao, pelo menos no de maneira conveniente. Essa abertura torna a composio recursiva uma parte essencial do Decorator. Esse no o caso do Proxy porque o Proxy focaliza um relacionamento entre o proxy e seu objeto e esse relacionamento pode ser expresso estaticamente. Essas diferenas so significativas porque capturam solues para problemas recorrentes especficos no projeto orientado a objetos. Mas isso no significa que esses padres no possam ser combinados. Voc pode imaginar um proxy-decorator que acrescenta funcionalidade a um proxy, ou um decorator-proxy que adorna um objeto remoto. Embora tais hbridos possam ser teis (ns no temos exemplos reais para mostrar), eles so divisveis em padres que so teis.

Notas
1 2

3 4

CreateManipulator um exemplo de um Factory Method (112). fcil esquecer de deletar o iterador uma vez que voc acabou de us-lo. O padro Iterator mostra como se proteger quanto a tais erros (bugs) na pgina 252. O tempo de pesquisa neste esquema proporcional freqncia de mudanas das fontes. No Exemplo de cdigo dado anteriormente, a informao de estilo tornada extrnseca, deixando o cdigo de caractere como o nico estado intrnseco.

210
5 6

CAPTULO 4 PADRES ESTRUTURAIS

7 8

Veja Abstract Factory (95) para uma outra abordagem que trata a independncia de aparncia. A implementao de objetos distribudos NEXTSTEP [Add94] (especificamente a classe NXProxy) utiliza esta tcnica. A implementao redefine forward, o equivalente de gancho em NEXTSTEP. Iterator (244) descreve um outro tipo de proxy na pgina 252. Em ltima instncia, quase todas as classes tm Object como sua superclasse. Da isso significar o mesmo que dizer definir uma classe que no tem Object como sua superclasse.

5
Padres comportamentais
Os padres comportamentais se preocupam com algoritmos e a atribuio de responsabilidades entre objetos. Os padres comportamentais no descrevem apenas padres de objetos ou classes, mas tambm os padres de comunicao entre eles. Esses padres caracterizam fluxos de controle difceis de seguir em tempo de execuo. Eles afastam o foco do fluxo de controle para permitir que voc se concentre somente na maneira como os objetos so interconectados. Os padres comportamentais de classe utilizam a herana para distribuir o comportamento entre classes. Este captulo inclui dois padres desse tipo. O Template Method (301) o mais simples e o mais comum dos dois. Um mtodo template uma definio abstrata de um algoritmo. Ele define o algoritmo passo a passo. Cada passo invoca uma operao abstrata ou uma operao primitiva. Uma subclasse encarna um algoritmo atravs da definio das operaes abstratas. O outro padro comportamental de classe Interpreter (231), o qual representa uma gramtica como uma hierarquia de classes e implementa um interpretador como uma operao em instncias destas classes. Os padres comportamentais de objetos utilizam a composio de objetos em vez da herana. Alguns descrevem como um grupo de objetos-pares cooperam para a execuo de uma tarefa que nenhum objeto sozinho poderia executar por si mesmo. Um aspecto importante aqui como os objetos-pares conhecem uns aos outros. Os pares poderiam manter referncias explcitas uns para os outros, mas isso aumentaria o seu acoplamento. Levado ao extremo, cada objeto teria conhecimento de cada um dos demais. O padro Mediator (257) evita essa situao pela introduo de um objeto mediador entre os objetos-pares. Um mediador fornece o referenciamento indireto necessrio para um acoplamento fraco. O padro Chain of Responsibility (212) fornece um acoplamento ainda mais fraco. Ele permite enviar solicitaes implicitamente para um objeto atravs de uma cadeia de objetos candidatos. Qualquer candidato pode satisfazer a solicitao dependendo de

212

CAPTULO 5 PADRES

COMPORTAMENTAIS

condies em tempo de execuo. O nmero de candidatos aberto e voc pode selecionar quais candidatos participam da cadeia em tempo de execuo. O padro Observer (274) define e mantm uma dependncia entre objetos. O exemplo clssico do Observer est no Model/View/Controller da Smalltalk, onde todas as vises do modelo so notificadas sempre que o estado do modelo muda. Outros padres comportamentais de objetos se preocupam com o encapsulamento de comportamento em um objeto e com a delegao de solicitaes para ele. O padro Strategy (292) encapsula um algoritmo num objeto. Strategy torna fcil especificar e mudar o algoritmo que um objeto usa. O padro Command (222) encapsula uma solicitao num objeto, de maneira que possa ser passada como um parmetro, armazenada numa lista histrica ou manipulada de outras formas. O padro State (284) encapsula os estados de um objeto, de maneira que o objeto possa mudar o seu comportamento quando o seu objeto-estado mudar. Visitor (305) encapsula comportamento que, de outra forma, seria distribudo entre classes, Iterator (244) abstrai a maneira como objetos de um agregado so acessados e percorridos.

CHAIN

OF

RESPONSIBILITY

comportamental de objetos

Inteno
Evitar o acoplamento do remetente de uma solicitao ao seu receptor, ao dar a mais de um objeto a oportunidade de tratar a solicitao. Encadear os objetos receptores, passando a solicitao ao longo da cadeia at que um objeto a trate.

Motivao
Considere um recurso de help sensvel ao contexto para uma interface grfica de usurio. O usurio pode obter informao de ajuda em qualquer parte da interface simplesmente pressionando o boto do mouse sobre ela. A ajuda que fornecida depende da parte da interface que selecionada e do seu contexto; por exemplo, um boto numa caixa de dilogo pode ter uma informao de ajuda diferente da de um boto similar na janela principal. Se no houver uma informao especfica de ajuda para aquela parte da interface, ento o sistema de ajuda deveria exibir uma mensagem de ajuda mais genrica sobre o contexto imediato por exemplo, a caixa de dilogo como um todo. Da ser natural organizar a informao de ajuda de acordo com a sua generalidade do mais especfico para o mais genrico. Alm do mais, est claro que uma solicitao de ajuda tratada por um entre vrios objetos da interface do usurio; qual objeto depende do contexto e de quo especfica a ajuda disponvel. O problema aqui que o objeto que na prtica fornece a ajuda no conhecido explicitamente pelo objeto (por exemplo, o boto) que inicia a solicitao de ajuda. O que necessitamos de uma maneira de desacoplar o boto que inicia a solicitao de ajuda dos objetos que podem fornecer informao de ajuda. O padro Chain of Responsibility define como isso acontece.

PADRES DE PROJETO

213

A idia desse padro desacoplar remetentes e receptores fornecendo a mltiplos objetos a oportunidade de tratar uma solicitao. A solicitao passada ao longo de uma cadeia de objetos at que um deles a trate.

aSaveDialog handler aPrintButton handler aPrintDialog handler anOKButton handler anApplication handler

especfico

geral

O primeiro objeto na cadeia que recebe a solicitao trata a mesma ou a repassa para o prximo candidato na cadeia, que faz a mesma coisa. O objeto que fez a solicitao no tem conhecimento explcito de quem a tratar dizemos que a solicitao tem um receptor implcito. Vamos assumir que o usurio pressiona o boto do mouse solicitando ajuda sobre o boto marcado Print. O boto est contido numa instncia de PrintDialog, o qual conhece o objeto da aplicao ao qual ele pertence (ver o diagrama de objeto precedente). O seguinte diagrama de interao ilustra como a solicitao de ajuda repassada ao longo da cadeia:
aPrintButton aPrintDialog anApplication

HandleHelp()

HandleHelp()

Neste caso, nem aPrintButton nem aPrintDialog trata a solicitao; ela pra em um anApplication, o qual a trata ou a ignora. O cliente que emitiu a solicitao no possui uma referncia direta para o objeto que efetivamente a realizou. Para repassar a solicitao ao longo da cadeia e para garantir que os receptores permaneam implcitos cada objeto na cadeia compartilha uma interface comum para o tratamento de solicitaes e para acessar seu sucessor na cadeia. Por exemplo, o sistema de ajuda pode definir uma classe HelpHandler, com uma correspondente operao HandleHelp. HelpHandler pode ser a classe-me para as classes de objetos-candidatos, ou ela pode ser definida como uma classe mixin*. Ento, classes que desejam tratar solicitaes de ajuda podem tornar HelpHandler uma classe me:

* N. de T.: Uma classe mixin uma classe usada para definir outras classes, atravs da combinao de suas propriedades e de outras classes mixin, pelo uso da herana mltipla.

214

CAPTULO 5 PADRES

COMPORTAMENTAIS

handler

HelpHandler HandleHelp()
handler>HandleHelp()

Application

Widget

Dialog

Button HandleHelp() ShowHelp()

if can handle { ShowHelp() } else { Handler::HandleHelp() }

As classes Button, Dialog e Application usam as operaes de HelpHandler para tratar solicitaes de ajuda. A operao HandlerHelp, de HelpHandler, repassa a solicitao para o sucessor, por falta. Subclasses podem substituir esta operao para fornecer ajuda sob as circunstncias apropriadas; caso contrrio, podem usar a implementao por falta para repassar a solicitao.

Aplicabilidade
Utilize Chain of Responsibility quando: mais de um objeto pode tratar uma solicitao e o objeto que a tratar no conhecido a priori. O objeto que trata a solicitao deve ser escolhido automaticamente; voc quer emitir uma solicitao para um dentre vrios objetos, sem especificar explicitamente o receptor; o conjunto de objetos que pode tratar uma solicitao deveria ser especificado dinamicamente.

Estrutura
successor

Client

Handler HandleRequest()

ConcreteHandler1 HandleRequest()

ConcreteHandler2 HandleRequest()

Uma tpica estrutura de objeto pode se parecer com esta:


aClient aHandler aConcreteHandler successor

aConcreteHandler successor

PADRES DE PROJETO

215

Participantes
Handler (HelpHandler) define uma interface para tratar solicitaes. (opcional) implementa o elo (link) ao sucessor. ConcreteHandler (PrintButton, PrintDialog) trata de solicitaes pelas quais responsvel. pode acessar seu sucessor. se o ConcreteHandler pode tratar a solicitao, ele assim o faz; caso contrrio, ele repassa a solicitao para o seu sucessor. Cliente inicia a solicitao para um objeto ConcreteHandler da cadeia.

Colaboraes
Quando um cliente emite uma solicitao, a solicitao se propaga ao longo da cadeia at que um objeto ConcreteHandler assume a responsabilidade de trat-la.

Conseqncias
A Chain of Responsibility tem os seguintes benefcios e deficincias: 1. Acoplamento reduzido. O padro libera um objeto de ter que conhecer qual o outro objeto que trata de uma solicitao. Um objeto tem que saber somente que uma solicitao ser tratada apropriadamente. Tanto o receptor como o remetente no tm conhecimento explcito um do outro, e um objeto que est na cadeia no necessita conhecer a estrutura da mesma. Como resultado, Chain of Responsibility pode simplificar as interconexes de objetos. Ao invs de os objetos manterem referncias para todos os receptorescandidatos, eles mantm uma referncia nica para o seu sucessor. 2. Flexibilidade adicional na atribuio de responsabilidades a objetos. O Chain of Responsibility d uma flexibilidade adicional na distribuio de responsabilidades entre objetos. possvel acrescentar ou mudar responsabilidades para o tratamento de uma solicitao pelo acrscimo ou mudana da cadeia em tempo de execuo. Voc pode combinar isto com subclasses para especializar estaticamente os handlers*. 3. A recepo no garantida. Uma vez que uma solicitao no tem um receptor explcito, no h garantia de que ela ser tratada a solicitao pode sair pelo final da cadeia sem ter sido tratada. Uma solicitao tambm pode no ser tratada quando a cadeia no est configurada apropriadamente.

Implementao
Aqui apresentamos aspectos da implementao a serem considerados ao usar Chain of Responsibility: 1. Implementando a cadeia de sucessores. H duas maneiras possveis de implementar a cadeia de sucessores: (a) definir novos elos (normalmente no Handler, porm, em seu lugar os ConcreteHandlers poderiam defini-las); (b) utilizar os existentes.
* N. de T.: Handler traduzido como aquele ou aquilo que trata ou manipula algo.

216

CAPTULO 5 PADRES

COMPORTAMENTAIS

At aqui nossos exemplos definem novas ligaes, mas, freqentemente, voc poder usar referncias existentes para objetos ao formar a cadeia de sucessores. Por exemplo, as referncias aos pais numa hierarquia partes-todo pode definir o sucessor de uma parte. Uma estrutura de widgets pode j ter tais ligaes. O Composite (160) discute referncias aos pais em mais detalhes. O uso de ligaes existentes funciona bem quando as ligaes suportam a cadeia necessria. Ela poupa a definio explcita de ligaes e economiza espao. Mas, se a estrutura no reflete a cadeia de responsabilidades de que sua aplicao necessita, ento voc ter que definir ligaes redundantes. 2. Conectando sucessores. Se no existem referncias pr-existentes para a definio de uma cadeia, ento voc ter que introduzi-las por conta. Nesse caso, o handler no somente define a interface para as solicitaes, mas normalmente mantm a ligao para o sucessor. Isso permite ao handler fornecer uma implementao-padro de HandleRequest que repassa a solicitao para o sucessor (se houver). Se uma subclasse ConcreteHandler no est interessada na solicitao, ela no tem que redefinir a operao de repasse, uma vez que a sua implementao default faz o repasse incondicionalmente. Aqui apresentamos uma classe-base HelpHandler que mantm uma ligao para o sucessor: 3. Representando solicitaes. Diferentes opes esto disponveis para representar solicitaes. Na forma mais simples, a solicitao a invocao de uma operao codificada de maneira rgida e fixa, como no caso de HandleHelp.

Isso conveniente e seguro, mas voc s pode repassar o conjunto fixo de solicitaes que a classe handler define. Uma alternativa usar uma nica funo handler que aceita um cdigo de solicitao como parmetro (por exemplo, uma constante inteira ou um string). Isso suporta um conjunto aberto de solicitaes. O nico requisito necessrio que o remetente e o receptor concordem sobre como a solicitao deve ser codificada. Essa abordagem mais flexvel, mas requer comandos condicionais para despachar a solicitao com base no seu cdigo de solicitao. Alm do mais, no h uma maneira segura de passar parmetros. Assim, elas devem ser empacotadas e desempacotadas manualmente. Obviamente, isso menos seguro do que invocar uma operao diretamente.

PADRES DE PROJETO

217

Para tratar o problema da passagem de parmetros, ns podemos usar objetossolicitao separados que empacotam os parmetros da solicitao. Uma classe Request pode representar solicitaes explicitamente, e novos tipos de solicitaes podem ser definidos por subclasses. As subclasses podem definir parmetros diferentes. Os handlers devem conhecer o tipo de solicitao (isto , qual subclasse de Request eles esto usando) para acessar esses parmetros. Para identificar a solicitao, Request pode definir uma funo de acesso (accessor, C++) que devolve um identificador para a classe. Como alternativa, o receptor pode usar informao sobre tipos em tempo de execuo se a linguagem de implementao permitir. Aqui apresentamos o esboo de uma funo de despacho que usa objetossolicitao para identificar solicitaes. Uma operao GetKind, definida na classe base Request, identifica o tipo de solicitao.

As subclasses podem estender a funo de despacho por redefinio de


HandleRequest. A subclasse trata somente as solicitaes nas quais ela est

interessada; outras solicitaes so repassadas para a classe me. Desse modo, as subclasses efetivamente estendem (em vez de redefinirem) a operao HandleRequest. Por exemplo, apresentamos aqui como uma subclasse ExtendedHandler estende a verso de Handler de HandleRequest:

218

CAPTULO 5 PADRES

COMPORTAMENTAIS

4. Repasse automtico em Smalltalk. Voc pode usar o mecanismo doesNotUnderstand em Smalltalk para repassar solicitaes. As mensagens que no tm mtodos correspondentes so interceptadas na implementao de doesNotUnderstand, a qual pode ser substituda para repassar a mensagem para o sucessor de um objeto. Desta forma, no necessrio implementar o repasse manualmente; a classe trata somente a solicitao na qual est interessada e confia em doesNotUnderstand para repass-la para outras.

Exemplo de cdigo
O exemplo a seguir ilustra como o padro chain of responsibility pode tratar solicitaes para um sistema de help online, como descrito anteriormente. A solicitao de ajuda uma operao explcita. Usaremos referncias existentes para os pais na hierarquia dos widgets para propagar as solicitaes entre widgets na cadeia e definiremos uma referncia na classe Handler para propagar solicitaes de ajuda entre no-widgets na cadeia. A classe HelpHandler define a interface para tratamento de solicitaes de ajuda. Ela mantm um tpico de ajuda (que, por omisso, vazio) e mantm uma referncia para o seu sucessor na cadeia de handlers de ajuda. A operao-chave HandleHelp, que as subclasses redefinem. HasHelp uma operao de convenincia para verificar se existe um tpico de ajuda associado.

PADRES DE PROJETO

219

Todos os widgets so subclasses da classe abstrata Widget. Widget uma subclasse de HelpHandler, uma vez que todos os elementos da interface do usurio podem ter ajudas associadas a eles. (Poderamos ter usado da mesma maneira uma implementao baseada em classes mixin).

No nosso exemplo, um boto o primeiro handler na cadeia. A classe Button uma subclasse de Widget. O constructor de Button aceita dois parmetros: uma referncia para o widget que o contm e para o tpico de ajuda.

A verso de Button para HandleHelp testa primeiro se existe um tpico de ajuda para botes. Se o desenvolvedor no tiver definido um tpico, ento a solicitao repassada ao sucessor usando a operao HandleHelp em HelpHandler. Se existir um tpico de ajuda, o boto o exibe e a busca termina.

Dialog implementa um esquema similar, com exceo de que o seu sucessor no um widget, mas qualquer handler de ajuda. Na nossa aplicao, esse sucessor ser uma instncia de Application.

220

CAPTULO 5 PADRES

COMPORTAMENTAIS

No fim da cadeia est uma instncia de Application. A aplicao no um widget, assim, Application uma especializao direta de HelpHandler. Quando uma solicitao se propaga at esse nvel, application pode fornecer informaes sobre a aplicao em geral ou oferecer uma lista de diversos tpicos de ajuda:

O cdigo a seguir cria e conecta esses objetos. Aqui, o dilogo trata de impresso, e, portanto, os objetos tm tpicos relacionados com impresso atribudos a eles.

Podemos invocar a solicitao de ajuda chamando HandleHelp em qualquer objeto da cadeia. Para comear a busca no objeto do boto, simplesmente chame HandleHelp nele:

PADRES DE PROJETO

221

Nesse caso, o boto tratar a solicitao imediatamente. Note que qualquer classe
HelpHandler poderia se torna sucessora de Dialog. Ainda mais, seu sucessor poderia

ser mudado dinamicamente. Desta forma, no importando onde um dilogo usado, voc obter a informao de ajuda dependente do contexto apropriada para ele.

Usos conhecidos
Vrias bibliotecas de classes usam o padro Chain of Responsibility para tratar de eventos de usurio. Elas utilizam diferentes nomes para a classe Handler, mas a idia a mesma: quando o usurio aperta o boto do mouse ou pressiona uma tecla, um evento gerado e passado ao longo da cadeia. MacApp [App89] e ET++ [WGM88] a chamam Event-Handler, a biblioteca TCL da Symantec [Sym93b] a chama Bureaucrat e o AppKit de NeXT [Add94] usa o nome Responder. O framework Unidraw para editores grficos define objetos Command que encapsulam solicitaes para objetos Component e ComponentView [VL90]. Comandos so solicitaes no sentido de que um componente ou a viso de um componente pode interpretar um comando para executar uma operao. Isso corresponde abordagem solicitaes como objetos, descrita na seo de Implementao. Components e component views podem ser estruturados hierarquicamente. Um componente ou uma viso de componente pode repassar a interpretao de comandos para os seus pais, os quais, por sua vez, a repassam para seus pais, e assim por diante, desta forma formando uma cadeia de responsabilidades. O ET++ usa Chain of Responsibility para tratar atualizaes grficas. Um objeto grfico chama a operao InvalidateRct sempre que ele deve atualizar uma parte da sua apresentao. Um objeto grfico no pode tratar InvalidateRct por si prprio porque ele no sabe o suficiente sobre o seu contexto. Por exemplo, um objeto grfico pode estar contido em objetos, tais como Scrollers ou Zoomers que transformam o seu sistema de coordenadas. Isso significa que o objeto pode ser rolado ou sofrer um zoom, de maneira que fique parcialmente fora de vista. Portanto, a implementao-padro de InvalidateRect repassa a solicitao para o objeto que contm o objeto grfico. Quando Window recebe a solicitao, fica garantida a transformao apropriada do InvalidateRect. Window trata InvalidateRect pela notificao da interface do sistema de janelas, solicitando uma atualizao.

Padres relacionados
O Chain of Responsibility freqentemente aplicado em conjunto com o Composite (160). No Composite, o pai de um componente pode atuar como seu sucessor.

222

CAPTULO 5 PADRES

COMPORTAMENTAIS

COMMAND
Inteno

comportamental de objetos

Encapsular uma solicitao como um objeto, desta forma permitindo parametrizar clientes com diferentes solicitaes, enfileirar ou fazer o registro (log) de solicitaes e suportar operaes que podem ser desfeitas.

Tambm conhecido como


Action, Transaction

Motivao
Algumas vezes necessrio emitir solicitaes para objetos sem nada saber sobre a operao que est sendo solicitada ou sobre o seu receptor. Por exemplo, toolkits para construo de interfaces de usurio incluem objetos como botes de menus que executam uma solicitao em resposta entrada do usurio. Mas o toolkit no pode implementar a solicitao explicitamente no boto ou menu porque somente as aplicaes que utilizam o toolkit sabem o que deveria ser feito e em qual objeto. Como projetistas de toolkits, no temos meios de saber qual o receptor da solicitao ou as operaes que ele executar. O padro Command permite a objetos de toolkit fazer solicitaes de objetosaplicao no especificados, transformando a prpria solicitao num objeto. Esse objeto pode ser armazenado e passado como outros objetos. A chave desse padro uma classe abstrata Command, a qual declara uma interface para execuo de operaes. Na sua forma mais simples, essa interface inclui uma operao abstrata Execute. As subclasses concretas de Command especificam um par receptor-ao atravs do armazenamento do receptor como uma varivel de instncia e pela implementao de Execute para invocar a solicitao. O receptor tem o conhecimento necessrio para poder executar a solicitao.
Application Add(Document) Menu Add(MenuItem) MenuItem command Clicked()

Command Execute()

Document Open() Close() Cut() Copy() Paste()

command>Execute()

Menus podem ser implementados facilmente com objetos Command. Cada escolha num Menu uma instncia de uma classe MenuItem. Uma classe Application cria esses menus e seus itens de menus juntamente com o resto da interface do usurio. A classe Application tambm mantm um registro de acompanhamento dos objetos Document que um usurio abriu.

PADRES DE PROJETO

223

A aplicao configura cada MenuItem com uma instncia de uma subclasse concreta de Command. Quando o usurio seleciona um MenuItem, o MenuItem chama Execute no seu Command, e Execute executa a operao. MenuItens no sabem qual a subclasse de Command que usam. As subclasses de Command armazenam o receptor da solicitao que invoca uma ou mais operaes no receptor. Por exemplo, um PasteCommand suporta colar textos da rea de transferncia (clipboard) num Document. O receptor de PasteCommand o objeto Document que fornecido por instanciao. A operao Execute invoca Paste no Document que est recebendo.
Command Execute()
Document Open() Close() Cut() Copy() Paste()

document PasteCommand Execute() document>Paste()

A operao Execute do OpenCommand, diferente: ela solicita ao usurio o nome de um documento, cria o correspondente objeto Document, adiciona este documento aplicao receptora e abre o documento.
Command Execute()

Application Add(Document)
application

OpenCommand Execute() AskUser

name = AskUser() doc = new Document(name) application>Add(doc) doc>Open()

Algumas vezes, um MenuItem necessita executar uma seqncia de comandos. Por exemplo, um MenuItem para centralizar uma pgina, no tamanho normal, poderia ser construdo a partir de um objeto CenterDocumentCommand e de um objeto NormalSizeCommand. Como comum encadear comandos desta forma, ns podemos definir uma classe MacroCommand para permitir que um MenuItem execute um nmero aberto de comandos. O MacroCommand uma subclasse concreta de Command que simplesmente executa uma seqncia de Commands. O MacroCommand no tem um receptor explcito, porque os comandos que ele seqencia definem seu prprio receptor.

224

CAPTULO 5 PADRES

COMPORTAMENTAIS

Command Execute()

commands MacroCommand Execute()

for all c in commands c>Execute()

Observe em cada um destes exemplos como o padro Command desacopla o objeto que invoca a operao daquele que tem o conhecimento para execut-la. Isso nos d bastante flexibilidade no projeto da nossa interface de usurio. Uma aplicao pode oferecer tanto uma interface com menus como uma interface com botes para algum recurso seu, simplesmente fazendo com que o menu e o boto compartilhem uma instncia da mesma subclasse concreta Command. Ns podemos substituir comandos dinamicamente, o que poderia ser til para a implementao de menus sensveis ao contexto. Tambm podemos suportar scripts de comandos compondo comandos em comandos maiores. Tudo isto possvel porque o objeto que emite a solicitao somente necessita saber como emiti-la; ele no necessita saber como a solicitao ser executada.

Aplicabilidade
Use o padro Command quando voc deseja: parametrizar objetos por uma ao a ser executada, da forma como os objetos MenuItem fizeram acima. Voc pode expressar tal parametrizao numa linguagem procedural atravs de uma funo callback, ou seja, uma funo que registrada em algum lugar para ser chamada em um momento mais adiante. Os Commands so uma substituio orientada o objetos para callbacks; especificar, enfileirar e executar solicitaes em tempos diferentes. Um objeto Command pode ter um tempo de vida independente da solicitao orginal. Se o receptor de uma solicitao pode ser representado de uma maneira independente do espao de endereamento, ento voc pode transferir um objeto command para a solicitao para um processo diferente e l atender a solicitao; suportar desfazer operaes. A operao Execute, de Command, pode armazenar estados para reverter seus efeitos no prprio comando. A interface de Command deve ter acrescentada uma operao Unexecute, que reverte os efeitos de uma chamada anterior de Execute. Os comandos executados so armazenados em uma lista histrica. O nvel ilimitado de desfazer e refazer operaes obtido percorrendo esta lista para trs e para frente, chamando operaes Unexecute e Execute, respectivamente;

PADRES DE PROJETO

225

suportar o registro (logging) de mudanas de maneira que possam ser reaplicadas no caso de uma queda de sistema. Ao aumentar a interface de Command com as operaes carregar e armazenar, voc pode manter um registro (log) persistente das mudanas. A recuperao de uma queda de sistema envolve a recarga dos comandos registrados a partir do disco e sua reexecuo com a operao Execute. estruturar um sistema em torno de operaes de alto nvel construdas sobre operaes primitivas. Tal estrutura comum em sistemas de informao que suportam transaes. Uma transao encapsula um conjunto de mudanas nos dados. O padro Command fornece uma maneira de modelar transaes. Os Commands tm uma interface comum, permitindo invocar todas as transaes da mesma maneira. O padro tambm torna mais fcil estender o sistema com novas transaes.

Estrutura
Client Invoker

Command Execute()

Receiver receiver Action() ConcreteCommand Execute() state receiver>Action();

Participantes
Command declara uma interface para a execuo de uma operao. ConcreteCommand (PasteCommand, OpenCommand) define uma vinculao entre um objeto Receiver e uma ao; implementa Execute atravs da invocao da(s) correspondente(s) operao(es) no Receiver. Client (Application) cria um objeto ConcreteCommand e estabelece o seu receptor. Invoker (MenuItem) solicita ao Command a execuo da solicitao. Receiver (Document, Application) sabe como executar as operaes associadas a uma solicitao. Qualquer classe pode funcionar como um Receiver.

Colaboraes
O cliente cria um objeto ConcreteCommand e especifica o seu receptor. Um objeto Invoker armazena o objeto ConcreteCommand. O Invoker emite uma solicitao chamando Execute no Command. Quando se deseja que os comandos possam ser desfeitos, ConcreteCommand armazena estados para desfazer o comando antes de invocar Execute.

226

CAPTULO 5 PADRES

COMPORTAMENTAIS

O objeto ConcreteCommand invoca operaes no seu Receiver para executar a solicitao. O diagrama a seguir mostra as interaes entre esses objetos, ilustrando como Command desacopla o Invoker do Receiver (e da solicitao que ele executa).
aReceiver aClient
new Command(aReceiver)

aCommand

anInvoker

StoreCommand(aCommand)

Action()

Execute()

Conseqncias
O padro Command tem as seguintes conseqncias: 1. Command desacopla o objeto que invoca a operao daquele que sabe como execut-la. 2. Commands so objetos de primeira classe, ou seja, podem ser manipulados e estendidos como qualquer outro objeto. 3. Voc pode montar comandos para formar um comando composto. Um exemplo disso a classe MacroCommand descrita anteriormente. Em geral, comandos compostos so uma instncia do padro Composite (160). 4. fcil acrescentar novos Commands porque voc no tem que mudar classes existentes.

Implementao
Considere os seguintes aspectos quando implementar o padro Command: 1. Quo inteligente deveria ser um comando? Um comando pode ter uma grande gama de habilidades. Em um extremo mais simples, ele define uma vinculao entre um receptor e as aes que executam a solicitao. No outro extremo, o mais complexo, ele implementa tudo sozinho, sem delegar para nenhum receptor. Este ltimo caso extremo til quando voc deseja definir comandos que so independentes de classes existentes, quando no existe um receptor adequado ou quando um comando conhece o seu receptor implicitamente. Por exemplo, um comando que cria uma outra janela de aplicao pode ser to capaz de criar uma janela como qualquer outro objeto. Em algum ponto entre esses dois extremos esto os comandos que tm conhecimento suficiente para encontrar o seu receptor dinamicamente. 2. Suportando desfazer e refazer. Commands podem suportar capacidades de desfazer e refazer se eles fornecerem uma maneira de reverter sua execuo (por exemplo, uma operao Unexecute ou Undo). Uma classe

PADRES DE PROJETO

227

ConcreteCommand pode necessitar armazenar estados adicionais para fazer isso. Esses estados podem incluir: o objeto Receptor (Receiver), o qual efetivamente executa as operaes em resposta solicitao; os argumentos da operao executada no receptor; quaisquer valores originais no receptor que podem mudar como resultado do tratamento da solicitao. O receptor deve fornecer operaes que permitem ao comando retornar o receptor ao seu estado anterior. Para suportar um nvel apenas de desfazer, uma aplicao necessita armazenar somente o ltimo comando executado. Para suportar mltiplos nveis de desfazer e refazer, a aplicao necessita uma lista histrica de comandos que foram executados onde o mximo comprimento da lista determina o nmero de nveis de desfazer/refazer. A lista histrica armazena seqncias de comandos que foram executados. Percorrendo a lista para trs e executando de maneira reversa os comandos, cancelam-se os seus efeitos; percorrendo a lista para frente e executando os comandos, reexecuta-se os comandos a serem refeitos. Um comando que pode ser desfeito poder ter que ser copiado, antes de ser colocado na lista histrica. Isso se deve ao fato de que o objeto comando que executou a solicitao original, digamos, a partir de um MenuItem, executar outras solicitaes em instantes posteriores. A cpia necessria para distinguir diferentes invocaes do mesmo comando se o seu estado pode variar entre invocaes. Por exemplo, um DeleteCommand que deleta objetos selecionados deve armazenar diferentes conjuntos de objetos, cada vez que executado. Portanto, o objeto DeleteCommand deve ser copiado logo aps a execuo e ter a cpia colocada na lista histrica. Se na execuo o estado do comando nunca muda, ento a cpia no necessria somente uma referncia para o comando necessita ser colocada na lista histrica. Commands que devem ser copiados antes de serem colocados na lista histrica se comportam como prottipos (ver Prototype, 121). 3. Evitando a acumulao de erros no processo de desfazer. Histerese pode ser um problema ao tentarmos garantir um mecanismo confivel de desfazer/ refazer que preserve a semntica da aplicao. Erros podem se acumular medida que os comandos so executados, desexecutados e reexecutados repetidamente, de modo que o estado de uma aplicao eventualmente poderia divergir dos valores originais. Portanto, pode ser necessrio armazenar mais informaes no comando para assegurar que os objetos sejam restaurados ao seu estado original. O padro Memento pode ser aplicado para dar ao comando acesso a essas informaes, sem expor aspectos internos de outros objetos. 4. Usando templates C++. Para comandos que (1) no possam ser desfeitos e (2) no exijam argumentos, podemos usar templates em C++ a fim de evitar a criao de uma subclasse de Command para cada tipo de ao e receptor. Mostraremos como fazer isto na seo Exemplo de Cdigo.

228

CAPTULO 5 PADRES

COMPORTAMENTAIS

Exemplo de cdigo
O cdigo C++ mostrado aqui esboa a implementao das classes Command da seo de Motivao. Ns definiremos OpenCommand, PasteCommand e MacroCommand. Primeiramente, a classe abstrata Command:

Um OpenCommand abre um documento cujo nome fornecido pelo usurio. Para um OpenCommand deve ser passado um objeto Application no seu constructor. AskUser uma rotina de implementao que solicita ao usurio o nome do documento a ser aberto.

Para um PasteCommand deve ser passado um objeto Document como seu receptor. O receptor fornecido como um parmetro para o constructor de PasteCommand.

PADRES DE PROJETO

229

Para comandos simples, que no podem ser desfeitos e no necessitam de argumentos, ns podemos usar um template de uma classe para parametrizar o receptor do comando. Definiremos uma subclasse template SimpleCommand para tais comandos. O SimpleCommand parametrizado pelo tipo do Receiver e mantm uma vinculao entre um objeto receptor e uma ao armazenada como um apontador para uma funo-membro.

O construtor armazena o receptor e a ao nas variveis de instncia correspondentes. Execute simplesmente aplica a ao ao receptor.

Para criar um comando que chama Action numa instncia da classe MyClass, um cliente simplesmente escreve

230

CAPTULO 5 PADRES

COMPORTAMENTAIS

Tenha em mente que essa soluo funciona somente para comandos simples. Comandos mais complexos, que mantm controle no somente de seus receptores mas tambm de argumentos ou estados para desfazer, exigem uma subclasse Command. Um MacroCommand administra uma seqncia de subcomandos e fornece operaes para acrescentar e remover subcomandos. No necessrio um receptor explcito porque estes subcomandos j definem seus receptores.

A chave para o MacroCommand a sua funo-membro Execute. Ela percorre todos os subcomandos e executa Execute em cada um deles.

Note que se MacroCommand implementasse uma operao Unexecute, ento seus subcomandos deveriam ser revertidos na ordem inversa da implementao de Execute. Por fim, MacroCommand deve fornecer operaes para administrar seus subcomandos. A MacroCommand tambm responsvel por deletar seus subcomandos.

Usos conhecidos
Talvez o primeiro exemplo do padro Command tenha aparecido em um artigo de Lieberman [Lie85]. O MacApp [App89] popularizou a noo de comandos para implementao de operaes que podem ser desfeitas. O ET++ [WGM88], o Inter Views [LCI+92] e o Unidraw [VL90] tambm definem classes que seguem o padro Command. InterViews define uma classe abstrata Action que fornece a funcionalidade de comando. Ele tambm define um template ActionCallback, parametrizado pelo mtodo ao, que instancia automaticamente subclasses Command.

PADRES DE PROJETO

231

A biblioteca de classes THINK [Sym93b] tambm utiliza comandos para suportar aes que podem ser desfeitas. Comandos em THINK so chamados Tasks (tarefas). Os objetos Tasks so passados ao longo de uma Chain of Responsibility (212) para serem consumidos. Os objetos de comando de Unidraw so nicos no sentido de que eles se comportam como mensagens. Um comando Unidraw pode ser enviado a outro objeto para interpretao, e o resultado da interpretao varia de acordo com o objeto receptor. Alm do mais, o receptor pode delegar a interpretao para um outro objeto, normalmente o pai do receptor, numa estrutura maior como a de uma Chain of Responsibility. Assim, o receptor de um comando Unidraw computado, em vez de armazenado. O mecanismo de interpretao do Unidraw depende de informaes de tipo em tempo de execuo. Coplien descreve como implementar functors, objetos que so funes em C++ [Cop92]. Ele obtm um grau de transparncia no seu uso atravs da sobrecarga do operador de chamada (operator ()). O padro Command diferente; o seu foco a manuteno de um vnculo entre um receptor e uma funo (isto , uma ao), e no somente a manuteno de uma funo.

Padres relacionados
Um Composite (160) pode ser usado para implementar MacroCommands. Um Memento (266) pode manter estados que o comando necessita para desfazer o seu efeito. Um comando que deve ser copiado antes de ser colocado na lista histrica funciona como um Prototype (121).

INTERPRETER
Inteno

comportamental de classes

Dada uma linguagem, definir uma representao para a sua gramtica juntamente com um interpretador que usa a representao para interpretar sentenas dessa linguagem.

Motivao
Se um tipo especfico de problema ocorre com freqncia suficiente, pode valer a pena expressar instncias do problema como sentenas de uma linguagem simples. Ento, voc pode construir um interpretador que soluciona o problema interpretando estas sentenas. Por exemplo, pesquisar cadeias de caracteres que correspondem a um determinado padro (pattern matching) um tipo de problema comum. Expresses regulares so uma linguagem-padro para especificao de padres de cadeias de caracteres. Em vez de construir algoritmos customizados para comparar cada padro com as cadeias, algoritmos de busca poderiam interpretar uma expresso regular que especifica um conjunto de cadeias a serem encontradas.

232

CAPTULO 5 PADRES

COMPORTAMENTAIS

O padro Interpreter descreve como definir uma gramtica para linguagens simples, representar sentenas na linguagem e interpretar essas sentenas. Neste exemplo, o padro descreve como definir uma gramtica para expresses regulares, representar uma determinada expresso regular e como interpretar essa expresso regular. Suponha a seguinte gramtica que define expresses regulares:

O smbolo expression um smbolo inicial, e literal um smbolo terminal que define palavras simples. O padro Interpreter usa uma classe para representar cada regra da gramtica. Os smbolos do lado direito da regra so variveis de instncia dessas classes. A gramtica acima representada por cinco classes: uma classe abstrata RegularExpression e suas quatro subclasses LiteralExpression, AlternationExpression, SequenceExpression e RepetitionExpression. As ltimas trs classes definem variveis que contm subexpresses.
RegularExpression Interpret()

LiteralExpression Interpret() literal

SequenceExpression Interpret()

expression1 expression2

repetition

RepetitionExpression Interpret()

AlternationExpression Interpret()

alternative1 alternative2

Cada expresso regular definida por essa gramtica representada por uma rvore sinttica abstrata composta de instncias destas classes. Por exemplo, a rvore sinttica abstrata

PADRES DE PROJETO

233

aSequenceExpression expression1 expression2

aLiteralExpression 'raining'

aRepetitionExpression repeat

anAlternationExpression alternation1 alternation2

aLiteralExpression 'dogs'

aLiteralExpression 'cats'

representa a expresso regular

Podemos criar um interpretador para estas expresses regulares definindo a operao Interpret em cada subclasse de RegularExpression. Interpret aceita como argumento o contexto no qual interpretar a expresso. O contexto contm a cadeia de caracteres de entrada e informaes sobre quanto dela foi identificado (matched) at aqui. Cada subclasse de RegularExpression implementa Interpret para buscar a correspondncia com a parte seguinte da cadeia de entrada baseada no contexto corrente. Por exemplo, LiteralExpression verificar se a entrada corresponde ao literal por ela definido; AlternationExpression verificar se a entrada corresponde a alguma de suas alternativas; RepetitionExpression verificar se a entrada tem mltiplas cpias da expresso que ela repete; e assim por diante.

Aplicabilidade
Use o padro Interpreter quando houver uma linguagem para interpretar e voc puder representar sentenas da linguagem como rvores sintticas abstratas. O padro Interpreter funciona melhor quando: a gramtica simples. Para gramticas complexas, a hierarquia de classes para a gramtica se torna grande e incontrolvel. Em tais casos, ferramentas tais como geradores de analisadores so uma alternativa melhor. Elas podem

234

CAPTULO 5 PADRES

COMPORTAMENTAIS

interpretar expresses sem a construo de rvores sintticas abstratas, o que pode economizar espao e, possivelmente, tempo; a eficincia no uma preocupao crtica. Os interpretadores mais eficientes normalmente no so implementados pela interpretao direta de rvores de anlise sinttica, mas pela sua traduo para uma outra forma. Por exemplo, expresses regulares so freqentemente transformadas em mquinas de estado. Porm, mesmo assim, o tradutor pode ser implementado pelo padro Interpreter, sendo o padro, portanto, ainda aplicvel.

Estrutura
Context

Client

AbstractExpression Interpret(Context)

TerminalExpression Interpret(Context)

NonterminalExpression Interpret(Context)

Participantes
AbstractExpression (RegularExpression) declara uma operao abstrata Interpret comum a todos os ns na rvore sinttica abstrata. TerminalExpression (LiteralExpression) implementa uma operao Interpret associada aos smbolos terminais da gramtica; necessria uma instncia para cada smbolo terminal em uma sentena. NonterminalExpression (AlternationExpression, RepetitionExpression, SequenceExpressions) necessria uma classe desse tipo para cada regra R::=R1R2...Rn da gramtica; mantm variveis de instncia do tipo AbstractExpression para cada um dos smbolos R1 a Rn; implementa uma operao Interpret para smbolos no-terminais da gramtica. Interpret chama a si prprio recursivamente nas variveis que representam R1 a Rn. Context contm informao que global para o interpretador. Client constri (ou recebe) uma rvore sinttica abstrata que representa uma determinada sentena na linguagem definida pela gramtica. A rvore sinttica abstrata montada a partir de instncias das classes NonTerminalExpression e TerminalExpression. invoca a operao Interpret.

PADRES DE PROJETO

235

Colaboraes
O cliente constri (ou recebe) a sentena como uma rvore sinttica abstrata de instncias de NonTerminalExpression e TerminalExpression. Ento o cliente inicia o contexto e invoca a operao Interpret. Cada n NonTerminalExpression define Interpret em termos de Interpret em cada subexpresso. A operao Interpret de cada TerminalExpression define o caso-base na recurso. As operaes Interpret em cada n utilizam o contexto para armazenar e acessar o estado do interpretador.

Conseqncias
O padro Interpreter tem os seguintes benefcios e deficincias: 1. fcil de mudar e estender a gramtica. Uma vez que o padro usa classes para representar regras da gramtica, voc pode usar a herana para mudar ou estender a gramtica. Expresses existentes podem ser modificadas incrementalmente, e novas expresses podem ser definidas como variaes de velhas expresses. 2. Implementar a gramtica tambm fcil. Classes que definem ns na rvore sinttica abstrata tm implementaes similares. Essas classes so fceis de escrever e freqentemente sua gerao pode ser automatizada com um gerador de compiladores ou de analisadores sintticos. 3. Gramticas complexas so difceis de manter. O padro Interpreter define pelo menos uma classe para cada regra da gramtica (regras gramaticais definidas utilizando-se BNF podem exigir mltiplas classes). Logo, gramticas que contm muitas regras podem ser difceis de administrar e manter. Outros padres de projeto podem ser aplicados para diminuir o problema (ver Implementao). Porm, quando a gramtica muito complexa, tcnicas como geradores de analisadores ou de compiladores so mais apropriadas. 4. Acrescentando novas formas de interpretar expresses. O padro Interpreter torna mais fcil resolver uma expresso de uma maneira nova. Por exemplo, voc pode suportar pretty printing ou verificao de tipo de uma expresso pela definio de uma nova operao nas classes da expresso. Se voc continua criando novas formas de interpretar uma expresso, ento considere a utilizao do padro Visitor (305) para evitar mudana nas classes da gramtica.

Implementao
Os padres Interpreter e Composite (160) compartilham muitos aspectos de implementao. Os seguintes aspectos so especficos do Interpreter: 1. Criao da rvore sinttica abstrata. O padro Interpreter no explica como criar uma rvore sinttica abstrata. Em outras palavras, no trata de anlise sinttica. A rvore sinttica abstrata pode ser criada por um analisador baseado em tabela, por um analisador especialmente codificado (normalmente descendente recursivo) ou diretamente pelo cliente. 2. Definindo a operao Interpret. Voc no tem que definir a operao Interpret nas classes das expresses. Se for normal criar um novo interpretador, ento

236

CAPTULO 5 PADRES

COMPORTAMENTAIS

melhor utilizar o padro Visitor (305) para colocar Interpret num objeto visitor separado. Por exemplo, uma gramtica para uma linguagem de programao ter muitas operaes em rvores sintticas abstratas, tais como verificao de tipo, otimizao, gerao de cdigo, e assim por diante. Ser mais adequado usar um Visitor para evitar a definio dessas operaes em cada classe da gramtica. 3. Compartilhando smbolos terminais com o padroFlyweight. As gramticas cujas sentenas contm muitas ocorrncias de um smbolo terminal podem se beneficiar do compartilhamento de uma nica cpia daquele smbolo. As gramticas para programas de computador so bons exemplos cada varivel do programa aparecer em muitos lugares, por todo o cdigo. No exemplo da seo Motivao, uma sentena pode ter o smbolo terminal dog (modelado pela classe LiteralExpression) aparecendo muitas vezes. Ns terminais geralmente no armazenam informao sobre sua posio na rvore sinttica abstrata. Ns pais passam aos ns terminais as informaes de contexto de que eles necessitam durante a interpretao. Da haver uma distino entre estados compartilhados (intrnsecos) e estados passados (extrnsecos), sendo aplicvel o padro Flyweight (187). Por exemplo, cada instncia de LiteralExpression para dog recebe um contexto contendo a subcadeia reconhecida at ento. E cada LiteralExpression faz a mesma coisa na sua operao Interpret ela testa se a parte seguinte da entrada contm um dog no importando onde a instncia aparea na rvore.

Exemplo de cdigo
Apresentamos aqui dois exemplos. O primeiro um exemplo completo em Smalltalk para verificar se uma seqncia corresponde a uma expresso regular. O segundo um programa em C++ para calcular o resultado de expresses boleanas. O reconhecedor de expresses regulares testa se uma cadeia de caracteres pertence linguagem definida pela expresso regular. A expresso regular definida pela seguinte gramtica:

Esta gramtica uma ligeira modificao do exemplo apresentado em Motivao. Ns mudamos um pouco a sintaxe concreta de expresses regulares porque o smbolo * no pode ser uma operao ps-fixada em Smalltalk. Assim, usamos repeat no seu lugar. Por exemplo, a expresso regular

reconheceria a cadeia de entrada dog dog cat weather.

PADRES DE PROJETO

237

Para implementar o reconhecedor, ns definimos as cinco classes definidas na pgina 232. A classe SequenceExpression tem as variveis de instncia expression1 e expression2 para os seus filhos da rvore sinttica abstrata. AlternationExpression armazena suas alternativas nas suas variveis de instncia alternative1 e alternative2, enquanto que RepetitionExpression mantm a expresso que ela repete na sua varivel de instncia repetition. LiteralExpression tem uma varivel de instncia components que mantm uma lista de objetos (provavelmente caracteres). Estes representam a cadeia literal de caracteres que deve casar com a seqncia de entrada. A operao match: implementa um interpretador para a expresso regular. Cada uma das classes que define a rvore sinttica abstrata implementa essa operao. Ela aceita inputState como um argumento que representa o estado corrente do processo de comparao, tendo lido parte da cadeia de entrada. Esse estado corrente caracterizado por um conjunto de input streams que representa o conjunto de entradas que a expresso regular pode ter aceito at aqui. (Isto equivalente a registrar todos os estados que o autmato de estados finitos equivalente teria, tendo reconhecido o input stream at este ponto). O estado corrente o mais importante para a operao repeat. Por exemplo, se a expresso regular fosse

ento o interpretador poderia casar com a, aa, aaa, e assim por diante. Se ela fosse

ento poderia casar com abc, aabc, aaabc, e assim por diante. Mas se a expresso regular fosse

ento comparar a entrada aabc com a subexpresso a repreat produziria dois input streams, um tendo correspondido a um caracter da entrada, e o outro tendo correspondido a dois caracteres. Somente o stream que aceitou um caracter corresponder ao abc remanescente. Agora consideraremos as definies de match: para cada classe que define a expresso regular. A definio de SequenceExpression compara cada uma das suas subexpresses em seqncia. Usualmente, ela eliminar input streams do seu inputState.

Uma AlternationExpression retornar a um estado que consiste da unio dos estados de ambas as alternativas. A definio de match: para AlternationExpression

238

CAPTULO 5 PADRES

COMPORTAMENTAIS

A operao match: para RepetitionExpression tenta encontrar tantos potenciais estados correspondentes quanto possvel:

Seu estado de sada usualmente contm mais estados do que seu estado de entrada porque uma RepetitionExpression pode corresponder a uma, duas ou muitas ocorrncias de repetition no estado de entrada. Os estados de sada representam todas essas possibilidades, permitindo a elementos subseqentes da expresso regular decidirem qual estado correto. Finalmente, a definio de match: para LiteralExpression tenta comparar seus componentes com qualquer input stream possvel. Ela mantm somente aqueles input streams que tm um correspondente:

A mensagem nextAvailable: avana o input stream. Essa a nica operao match: que avana o stream. Observe como o estado retornado contm uma cpia do input stream, garantindo dessa forma que encontrar um literal correspondente nunca mudar o input stream. Isso importante porque cada alternativa de uma AlternationExpression deveria ter cpias idnticas do input stream. Agora que definimos as classes que compem uma rvore sinttica abstrata, podemos descrever como constru-la. Em vez de escrever um analisador de expresses regulares, definiremos algumas operaes nas classes RegularExpression, de maneira que executando uma expresso Smalltalk produz-se uma rvore sinttica abstrata para a expresso regular correspondente. Isso nos permite utilizar o compilador embutido Smalltalk como se fosse um analisador de expresses regulares. Para definir a rvore sinttica abstrata, necessitaremos definir |, repeat e &como operaes de RegularExpression. Essas operaes so definidas na classe RegularExpression como segue:

PADRES DE PROJETO

239

A operao asRExp converter literais em RegularExpressions. Essas operaes so definidas na classe String:

Se tivssemos definido essas operaes mais acima na hierarquia de classes (SequenceableCollection em Smalltalk-80, IndexedCollection, em Smalltalk/V), ento elas tambm seriam definidas para classes, tais como Array e OrderedCollection. Isso levaria expresses regulares a reconhecer seqncias de qualquer tipo de objeto. O segundo exemplo um sistema para manipulao e clculo de expresses boleanas implementado em C++. Os smbolos terminais nesta linguagem so constantes boleanas, ou seja, true e false. Smbolos no-terminais representam expresses contendo os operadores and, or, e not. A gramtica definida como segue:

Definiremos duas operaes sobre expresses boleanas. A primeira, Evaluate, resolve uma expresso boleana num contexto que atribui um valor verdadeiro ou falso para cada varivel. A segunda operao, Replace, produz uma nova expresso boleana pela substituio de uma varivel por uma expresso. Replace mostra como o padro Interpreter pode ser usado para algo mais do que apenas calcular expresses. Neste caso, ele manipula a prpria expresso.

240

CAPTULO 5 PADRES

COMPORTAMENTAIS

Aqui daremos detalhes somente das classes BooleanExp, VariableExp e AndExp. As classes OrExp e NotExp so similares a AndExp. A classe Constant representa as constantes boleanas. BooleanExp define a interface para todas as classes que definem uma expresso boleana:

A classe Context define um mapeamento de variveis para valores boleanos, os quais representamos com as constantes da C++ true e false. Context tem a seguinte interface:

A VariableExp representa uma varivel com um nome:

O construtor aceita o nome da varivel como um argumento:

Determinando o valor de uma varivel, retorna o seu valor no contexto corrente. Copiar uma varivel retorna uma nova VariableExp:

Para substituir uma varivel por uma expresso, testamos para verificar se a

PADRES DE PROJETO

241

varivel tem o mesmo nome daquela que foi passada como argumento:

Uma AndExp representa uma expresso obtida aplicando o operador boleano e a duas expresses boleanas.

Executar uma AndExp determina o valor dos seus operandos e retorna o and lgico dos resultados.

Uma AndExp implementa Copy e Replace fazendo chamadas recursivas sobre os seus operandos:

242

CAPTULO 5 PADRES

COMPORTAMENTAIS

Agora podemos definir a expresso boleana

e calcul-la para uma dada atribuio de true ou false s variveis X e Y:

A expresso tem o resultado true (verdadeiro) para estas atribuies de valores x e y. Podemos calcular a expresso com atribuies de valores diferentes das variveis, simplesmente mudando o contexto. Por fim, podemos substituir a varivel y por uma nova expresso e ento recalculla:

Este exemplo ilustra um ponto importante sobre o padro Interpreter: muitos tipos de operaes podem interpretar uma sentena. Das trs operaes definidas para BooleanExp, Evaluate se ajusta melhor nossa idia do que um interpretador deveria fazer ou seja, interpretar um programa ou expresso e retornar um resultado simples.

PADRES DE PROJETO

243

Contudo, Replace tambm pode ser visto como um interpretador. Ele um interpretador cujo contexto o nome da varivel que est sendo substituda juntamente com a expresso que a substitui e cujo resultado uma nova expresso. Mesmo Copy pode ser visto como um interpretador com um contexto vazio. Pode parecer um pouco estranho considerar Replace e Copy interpretadores, pois so apenas operaes bsicas sobre rvores. Os exemplos em Visitor (305) mostram como as trs operaes podem ser refatoradas num Visitor interpretador separado, desta forma mostrando que a semelhana profunda. O padro Interpreter mais do que somente uma operao distribuda sobre uma hierarquia de classes que usa o padro Composite (160). Ns consideramos Evaluate um interpretador, pois pensamos na hierarquia de classes de BooleanExp representando uma linguagem. Dada uma hierarquia de classes semelhante para representar as montagens de peas automotivas, seria improvvel que considerssemos operaes como Weight e Copy como interpretadores, mesmo que elas estejam distribudas sobre uma hierarquia de classes que usa o padro Composite simplesmente no pensamos em peas de automveis como uma linguagem. uma questo de perspectiva; se comessemos a publicar gramticas de peas de automveis, ento poderamos considerar as operaes sobre essas peas como maneiras de interpretar a linguagem.

Usos conhecidos
O padro Interpreter amplamente usado em compiladores implementados com linguagens orientadas a objetos, como so os compiladores de Smalltalk. SPECTalk usa o padro para interpretar descries de formatos de arquivos de entrada [Sza92]. O toolkit solucionador de restries QOCA o utiliza para avaliar restries [HHMV92]. Considerado na sua forma mais geral (isto , uma operao distribuda sobre uma hierarquia de classes baseada no padro Composite), quase todos os usos do padro Composite tambm contero o padro Interpreter. Mas o padro Interpreter deveria ser reservado para aqueles casos em que voc deseja ver a hierarquia de classes como a definio de uma linguagem.

Padres relacionados
Composite (160): A rvore sinttica abstrata uma instncia do padro Composite. Flyweight (187) mostra como compartilhar smbolos terminais dentro de uma rvore sinttica abstrata. Iterator (244): O Interpreter pode usar um Iterator para percorrer a estrutura. Visitor (305) pode ser usado para manter o comportamento em cada elemento da rvore sinttica abstrata em uma classe.

244

CAPTULO 5 PADRES

COMPORTAMENTAIS

ITERATOR
Inteno

comportamental de objetos

Fornecer um meio de acessar, seqencialmente, os elementos de um objeto agregado sem expor a sua representao subjacente.

Tambm conhecido como


Cursor

Motivao
Um objeto agregado, tal como uma lista, deveria fornecer um meio de acessar seus elementos sem expor a sua estrutura interna. Alm do mais, voc pode querer percorrer a lista de diferentes maneiras, dependendo do que quiser fazer. Mas, provavelmente, voc no ir quer inflar a interface da lista com operaes para diferentes percursos, ainda que possa antecipar aquelas de que necessitar. Voc tambm pode precisar ter mais de um percurso pendente sobre a mesma lista. O padro Iterator permite fazer tudo isto. A idia-chave nesse padro retirar a responsabilidade de acesso e percurso do objeto lista e coloc-la em um objeto iterator. A classe Iterator define uma interface para acessar os elementos da lista. Um objeto iterator responsvel por manter a posio do elemento corrente; ou seja, sabe quais elementos j foram percorridos. Por exemplo, uma classe List necessitaria uma ListIterator com o seguinte relacionamento entre elas:
List Count() Append(Element) Remove(Element) ... list ListIterator First() Next() IsDone() CurrentItem index

Antes que possa instanciar o ListIterator, voc dever fornecer a Lista a ser percorrida. Uma vez tendo a instncia de ListIterator, poder acessar os elementos da lista seqencialmente. A operao CurrentItem retorna o elemento corrente na lista, a operao First inicia o elemento corrente com o primeiro elemento da lista, a operao Next avana para o elemento seguinte e IsDone testa se avanamos para alm do ltimo elemento isto , se terminamos o percurso. Separar o mecanismo de percurso do objeto List nos permite definir Iteradores para diferentes polticas de percurso sem enumer-las na interface de List. Por exemplo, FilteringListIterator pode fornecer acesso somente queles elementos que satisfaam restries especficas de filtragem. Note que o iterador e a lista so acoplados, e que o cliente deve saber que uma lista que percorrida e no alguma outra estrutura agregada. O cliente se compromete com uma determinada estrutura agregada. Seria melhor se pudssemos mudar a classe do agregado sem ter que mudar o cdigo-cliente. Podemos fazer isto generalizando o conceito de iterador para suportar a iterao polimrfica.

PADRES DE PROJETO

245

Como exemplo, vamos assumir que tambm temos uma implementao SkipList de uma lista. Uma skiplist [Pug90] uma estrutura de dados probabilstica com caractersticas semelhantes s das rvores balanceadas. Queremos ser capazes de escrever um cdigo que funcione tanto para objetos List como para SkipList. Definimos uma classe AbstractList que fornece uma interface comum para manipulao de listas. Similarmente, necessitamos uma classe abstrata Iterator que define uma interface comum de iterao. Ento, podemos definir subclasses concretas de Iterator para as diferentes implementaes de lista. Como resultado, o mecanismo de iterao se torna independente das classes concretas de agregados.
AbstractList CreatIterator() Count() Append(Item) Remove(Item) ...
Client

Iterator First() Next() IsDone() CurrentItem()

List SkipList

ListIterator SkipListIterator

O problema remanescente como criar o iterador. Uma vez que queremos escrever cdigo que independente das subclasses concretas de List, no podemos simplesmente instanciar uma classe especfica. Em vez disso, tornamos os objetos de lista responsveis pela criao dos seus iteradores correspondentes. Isso exige uma operao como CreateIterator, pela qual os clientes solicitam um objeto iterador. O CreateIterator um exemplo de um mtodo fbrica (ver Factory Method, 112). Ns o utilizamos aqui para permitir a um cliente pedir a um objeto lista o iterador apropriado. A abordagem Factory Method d origem a duas hierarquias de classes, uma para listas e outra para iteradores. O mtodo fbrica CreateIterator conecta as duas hierarquias.

Aplicabilidade
Use o padro Iterator: para acessar os contedos de um objeto agregado sem expor a sua representao interna; para suportar mltiplos percursos de objetos agregados; para fornecer uma interface uniforme que percorra diferentes estruturas agregadas (ou seja, para suportar a iterao polimrfica).

246

CAPTULO 5 PADRES

COMPORTAMENTAIS

Estrutura
Aggregate CreateIterator()
Client

Iterator First() Next() IsDone() CurrentItem()

ConcreteAggregate ConcreteIterator CreateIterator()

return new ConcreteIterator(this)

Participantes
Iterator define uma interface para acessar e percorrer elementos. ConcreteIterator implementa a interface de Iterator. mantm o controle da posio corrente no percurso do agregado. Aggregate define uma interface para a criao de um objeto Iterator. ConcreteAggregate implementa a interface de criao do Iterator para retornar uma instncia do ConcreteIterator apropriado.

Colaboraes
Um ConcreteIterator mantm o controle do objeto corrente no agregado e pode computar o objeto sucessor no percurso.

Conseqncias
O padro Iterator tem trs conseqncias importantes: 1. Ele suporta variaes no percurso de um agregado. Os agregados complexos podem ser percorridos de muitas maneiras. Por exemplo, a gerao de cdigo e a verificao semntica envolve o percurso de rvores de derivao. A gerao de cdigo pode percorrer a rvore de derivao sinttica em ordem in-fixada ou prefixada. Iteradores tornam fcil mudar o algoritmo de percurso: simplesmente substitua a instncia do iterador por uma diferente. Voc tambm pode definir subclasses de Iterator para suportar novos percursos. 2. Iteradores simplificam a interface do agregado. A interface de percurso de Iterator elimina as necessidades de uma interface semelhante em Aggregate, dessa forma simplificando a interface do agregado.

PADRES DE PROJETO

247

3. Mais do que um percurso pode estar em curso (ou pendente), em um agregado. Um interador mantm o controle de acompanhamento do estado do seu prprio percurso. Portanto, voc pode ter mais que um percurso em andamento ao mesmo tempo.

Implementao
Iterator tem muitas variantes e alternativas de implementao. A seguir, apresentamos algumas importantes. As solues de compromisso freqentemente dependem da estrutura de controle oferecida pela linguagem. Algumas linguagens (por exemplo, CLU [LG86]), at mesmo suportam esse padro diretamente. 1. Quem controla a iterao? Um aspecto fundamental decidir qual parte controla a iterao, o iterador ou o cliente que usa o iterador. Quando o cliente controla a iterao, o iterador chamado de um iterador externo, e quando o iterador que a controla, o iterador chamado de um iterador interno2. Os clientes que usam um iterador externo devem avanar o percurso e solicitar explicitamente o prximo elemento ao iterador. Em contraste com isso, o cliente passa a um iterador interno uma operao a ser executada, e o iterador aplica essa operao a cada elemento no agregado. Iteradores externos so mais flexveis que iteradores internos. Por exemplo, fcil comparar duas colees quanto sua igualdade com um iterador externo, mas isso praticamente impossvel com iteradores internos. Iteradores internos so especialmente fracos em uma linguagem como C++, que no oferece funes annimas, fechos ou continuaes, como o fazem Smalltalk e CLOS. Mas, por outro lado, iteradores internos so mais fceis de usar porque eles definem a lgica da iterao para voc. 2. Quem define o algoritmo de percurso? O iterador no o nico lugar onde o algoritmo de percurso pode ser definido. O agregado pode definir o algoritmo de percurso e utilizar o iterador somente para armazenar o estado da iterao. Chamamos a este tipo de iterador um cursor, uma vez que ele meramente aponta para a posio corrente no agregado. Um cliente invocar a operao Next no agregado com o cursor como um argumento, e a operao Next mudar o estado do cursor3. Se o iterador responsvel pelo algoritmo de percurso, ento fcil utilizar diferentes algoritmos de iterao sobre o mesmo agregado, e tambm pode ser mais fcil reutilizar o mesmo algoritmo sobre agregados diferentes. Por outro lado, o algoritmo de percurso pode necessitar acessar as variveis privadas do agregado. Se esse for o caso, a colocao do algoritmo de percurso no iterador viola o encapsulamento do agregado. 3. Quo robusto o iterador? Pode ser perigoso modificar um agregado enquanto ele percorrido. Se so acrescentados ou deletados elementos do agregado, pode-se acabar acessando um elemento duas vezes ou perdendo-o completamente. Uma soluo simples copiar o agregado e percorrer a cpia, mas isso , em geral, muito caro de ser feito em termos computacionais. Um iterador robusto garante que inseres ou remoes no interferiro com o percurso, e ele consegue isso sem copiar o agregado. H muitas maneiras de implementar iteradores robustos. A maioria depende do registro do iterador com o agregado. Na insero ou remoo, o agregado ajusta o estado interno

248

CAPTULO 5 PADRES

COMPORTAMENTAIS

4.

5.

6.

7.

dos iteradores que ele produziu ou mantm internamente informaes para garantir um percurso adequado. Klofer fornece uma boa discusso sobre como iteradores robustos so implementados em ET++ [Kof93]. Murray discute a implementao de iteradores robustos para classe List do USL StandardComponents [Mur93]. Operaes adicionais de Iterator. A interface mnima de Iterator consiste das operaes First, Next, IsDone e CurrentItem4. Algumas operaes adicionais podem ser teis. Por exemplo, agregados ordenados podem ter uma operao Previous que posiciona o iterador no elemento prvio. Uma operao SkipTo til para colees classificadas ou indexadas. SkipTo posiciona o iterador para um objeto que satisfaz critrios especficos. Usando iteradores polimrficos em C++. Iteradores polimrficos tm o seu custo. Eles exigem que o objeto iterador seja alocado dinamicamente por um mtodo-fbrica. Da eles devem ser usados somente quando h necessidade de polimorfismo. Caso contrrio, use iteradores concretos, que podem ser alocados na pilha de execuo. Os iteratores polimrficos tm um outro inconveniente: o cliente responsvel por delet-los. Isto sujeito a erros porque fcil esquecer de liberar o espao alocado para um objeto iterador quando voc acaba de us-lo. Isso particularmente provvel quando existem mltiplos pontos de sada em uma operao. E se uma exceo disparada, o objeto iterador nunca ser liberado. O padro Proxy (198) oferece um remdio para essa situao. Podemos usar um proxy alocado na pilha como um substituto para o iterador real. O proxy deleta o iterador no seu destrutor. Assim, quando o proxy sai do escopo, o iterador real ser desalocado juntamente com ele. O proxy assegura uma limpeza adequada mesmo no caso de excees. Essa uma aplicao da tcnica bem conhecida em C++ alocao de recurso inicializao [ES90]. A seo Exemplo de Cdigo mostra um exemplo disso. Iteradores podem ter acesso privilegiado. Um iterador pode ser visto como uma extenso do agregado que o criou. O iterador e o agregado so fortemente acoplados. Podemos fortalecer essa relao em C++, tornando o iterador uma classe friend do seu agregado. Ento, voc no ter que definir operaes para o agregado cuja nica finalidade seria permitir que iteradores implementassem percursos de forma eficiente. Contudo, tal acesso privilegiado pode tornar difcil a definio de novos percursos, uma vez que isto exigir a mudana da interface do agregado para acrescentar outro friend. Para evitar este problema, a classe Iterator pode incluir operaes protected para acessar membros importantes, porm, publicamente disponveis do agregado. As subclasses de Iterator (e somente elas) podero usar essas operaes protegidas para obter acesso privilegiado ao agregado. Iterators para composites. Iteradores externos podem ser difceis de implementar sobre estruturas recursivas de agregados como aquelas encontradas no padro Composite (160) porque uma posio da estrutura pode abranger muitos nveis de agregados encaixados. Portanto, um iterador externo tem que armazenar uma trajetria atravs do Composite para manter controle do objeto corrente. Algumas vezes mais fcil s utilizar um iterador interno. Ele pode registrar a posio corrente simplesmente chamando a si prprio

PADRES DE PROJETO

249

recursivamente, dessa forma armazenando implicitamente a trajetria na pilha de chamadas. Se os ns em um Composite tm uma interface para movimentao de um n para os seus irmos, pais e filhos, ento um iterador baseado em cursor pode oferecer uma melhor alternativa. O cursor somente necessita controlar o n corrente; ele pode basear-se na interface dos ns para percorrer o Composite. Os Composites freqentemente necessitam ser percorridos de mais de uma maneira. So comuns percursos em pr-ordem, ps-ordem, in-ordem e breadth-first (passar de um n para outro n no mesmo nvel da rvore). Voc pode suportar cada tipo de percurso com uma classe diferente de iterador. 8. Iteradores nulos. Um NullIterator um iterador degenerado til para tratar condies de contorno. Por definio, um NullIterator sempre termina com um percurso; ou seja, a operao IsDone sempre devolve o valor verdadeiro (true). NullIterator pode tornar mais fcil um percurso de agregados estruturados em rvores (como os Composites). A cada ponto do percurso solicitamos ao elemento corrente que coloque um iterador para seus filhos. Os elementos do agregado normalmente retornam um iterador concreto. Porm, elementosfolhas retornam uma instncia de NullIterator. Isso nos permite implementar o percurso sobre toda estrutura de uma maneira uniforme.

Exemplo de cdigo
Examinaremos uma implementao de uma simples classe List, parte de nossa Foundation Library (Apndice C). Mostraremos duas implementaes de Iterator, uma para percorrer List da frente para trs e outra para atravess-la de trs para frente (a biblioteca suporta somente a primeira). Ento mostraremos como usar esses iteradores e como evitar o compromisso com uma implementao em particular. Depois disso, mudamos o projeto para garantir que iteradores sejam deletados de maneira apropriada. O ltimo exemplo ilustra um iterador interno e o compara sua contrapartida externa. 1. Interfaces de List e Iterator. Primeiramente, examinemos a parte da interface de List relevante para a implementao de Iteradores. Veja no Apndice C a interface completa.

A classe List fornece uma maneira razoavelmente eficiente para suportar a iterao atravs da sua interface pblica. Ela suficiente para implementar ambos os percursos. Assim, no h necessidade de dar aos iteradores um acesso privilegiado estrutura de dados subjacente; ou seja, as classes

250

CAPTULO 5 PADRES

COMPORTAMENTAIS

iterator no so friends de List. Para permitir o uso transparente de diferentes percursos, definimos uma classe abstrata Iterator que define a interface de um iterador.

2. Implementaes de subclasses de Iterator. ListIterator uma subclasse de Iterator.

A implementao de ListIterator simples e direta. Ela armazena List juntamente com um ndice _current para a lista:

First posiciona o iterador no primeiro elemento:

Next avana o elemento corrente:

IsDone testa se o ndice referencia um elemento dentro de List:

PADRES DE PROJETO

251

Finalmente, CurrentItem retorna o item para o ndice corrente. Se a iterao j terminou, ento disparamos uma exceo IteratorOutOfBounds:

A implementao de ReverseListIterator idntica, com a exceo de que sua operao First posiciona _current no fim da lista, e Next decrementa _current na direo do primeiro item. 3. Usando os iteradores. Vamos assumir que temos uma List de objetos Employee (empregado), e que queremos imprimir todos os empregados nela contidos. A classe Employee suporta isso por meio de uma operao Print. Para imprimir a lista, definimos uma operao PrintEmployees que aceita um iterador como um argumento. Ela usa o iterador para percorrer e imprimir a lista.

Uma vez que dispomos de iteradores para percursos tanto de trs para diante como da frente para trs, podemos reutilizar essa operao para imprimir os empregados em ambas as ordens.

4. Evitando comprometer-se com uma implementao especfica da lista. Vamos considerar agora como uma variao skiplist de List, afetaria nosso cdigo de iterao. Uma subclasse SkipList, de List, pode fornecer um SkipListIterator que implementa a interface de Iterator. Internamente, o SkipListIterator tem que manter mais do que somente um ndice para fazer a iterao de maneira eficiente. Mas, uma vez que SkipListIterator segue a interface de Iterator, a operao PrintEmployees tambm pode ser usada quando os empregados so armazenados num objeto SkipList.

252

CAPTULO 5 PADRES

COMPORTAMENTAIS

Embora essa abordagem funcione, seria melhor se no tivssemos que nos comprometer com uma implementao especfica de List, no caso SkipList. Podemos introduzir uma classe AbstractList para padronizar a interface da lista para diferentes implementaes. List e SkipList se tornam subclasses de AbstractList. Para permitir a iterao polimrfica, AbstractList define um mtodo fbrica CreateIterator, cujas subclasses redefinem para retornar seus iteradores correspondentes :

Uma alternativa seria definir uma classe mixin geral, chamada Traversable, que define a interface para criao de um iterador. Classes agregadas podem ser combinadas (mixin) em Traversable para suportar a iterao polimrfica. List redefine CreateIterator para retornar um objeto ListIterator:

Agora estamos em condio de escrever o cdigo para impresso dos empregados independentemente de uma representao concreta.

5. Garantindo que os iteradores sejam deletados. Observe que CreateIterator retorna um objeto iterador recm-alocado. Ns somos responsveis por delet-lo. Se esquecermos de faz-lo teremos criado uma perda de memria. Para tornar a vida mais fcil para os clientes, proveremos um IteratorPtr que funciona como um proxy para um iterador. Ele cuida da eliminao do objeto Iterator quando este sai do escopo. IteratorPtr sempre alocado na pilha5. C++ automaticamente cuida de chamar o seu destrutor, o qual deleta o iterador real. IteratorPtr sobrecarrega tanto operator -> quanto o operator*, de tal maneira que um IteratorPtr

PADRES DE PROJETO

253

possa ser tratado simplesmente como um apontador para um iterador. Os membros de IteratorPtr so todos implementados em linha (inline, ou seja, expandidos em tempo de compilao); desta forma no produzem sobrecarga. IteratorPtr permite simplificar nosso cdigo para impresso:

6. Um ListIterator interno. Como exemplo final, vamos examinar uma possvel

implementao de uma classe ListIterator interna ou passiva. Aqui o iterador controla a iterao e aplica uma operao a cada elemento. A questo, neste caso, como parametrizar o iterador com a operao que queremos executar em cada elemento. C++ no suporta funes annimas, ou fechos, que outras linguagens fornecem para esta tarefa. H pelo menos duas opes: (1) passar um apontador para uma funo (global ou esttica), ou (2) depender do uso de subclasses. No primeiro caso, o iterador chama a operao passada para ele em cada ponto da iterao. No segundo caso, o iterador chama uma operao que uma subclasse redefine para determinar comportamento especfico. Nenhuma das opes perfeita. Freqentemente, voc deseja acumular estados durante a iterao, e funes no so muito adequadas para isso; teramos que usar variveis estticas para lembrar o estado. Uma subclasse Iterator nos fornece um lugar conveniente para armazenar os estados acumulados, tal como numa varivel de instncia. Porm, a criao de uma subclasse para cada percurso diferente significa mais trabalho a ser feito. A seguir, apresentamos um esboo da segunda opo, que utiliza subclasses. Chamamos o iterador interno de ListTraverser.

254

CAPTULO 5 PADRES

COMPORTAMENTAIS

ListTraverser aceita uma instncia de List como um parmetro. Internamente, utiliza um ListIterator externo para fazer o percurso. Traverse inicia o percurso e chama ProcessItem para cada item. O iterador interno pode decidir terminar um percurso retornando false, de ProcessItem. Traverse retorna se o percurso terminou prematuramente.

Vamos usar um ListTraverser para imprimir os primeiros 10 empregados da nossa lista de empregados. Para faz-lo, teremos que criar uma subclasse de ListTraverser e redefinir ProcessItem. Contamos o nmero de empregados impressos em uma varivel de instncia _count.

PADRES DE PROJETO

255

Aqui est como PrintNEmployees imprime os primeiros 10 empregados da lista:

Observe como o cliente no especifica o ciclo de iterao. Toda a lgica da iterao pode ser reutilizada. Este o benefcio primrio de um iterador interno. Entretanto, d um pouco mais de trabalho do que um iterador externo porque temos que definir uma nova classe. Compare isto com a utilizao de um iterador externo:

Os iteradores internos podem encapsular diferentes tipos de iteraes. Por exemplo, FilteringListTraverser encapsula uma interao que processa somente itens que satisfazem um teste:

256

CAPTULO 5 PADRES

COMPORTAMENTAIS

Essa interface a mesma de ListTraverser, com exceo de uma funo membro TestItem, que foi acrescentada para definir o teste. As subclasses redefinem TestItem para especificar o teste. Traverse decide se continua o percurso baseado no resultado do teste:

Uma variante dessa classe pode definir Traverse para retornar se pelo menos um item satisfaz o teste 6.

Usos conhecidos
Os Iterators so comuns em sistemas orientados a objetos. A maioria das bibliotecas de classes de colees oferece iteradores de uma maneira ou outra. Aqui citamos um exemplo dos componentes de Booch [Boo94], uma biblioteca popular de classes de colees. Ela fornece implementao para fila tanto de tamanho fixo (limitado) quanto de crescimento dinmico (ilimitado). A interface de fila definida por uma classe abstrata Queue. Para suportar a iterao polimrfica nas diferentes implementaes de fila, o iterador de fila implementado em termos da interface da classe abstrata Queue. Essa variao tem a vantagem de no necessitar de um mtodo-fbrica para consultar as implementaes de fila a respeito do seu iterador apropriado. Contudo, requer que a interface da classe abstrata Queue seja poderosa o bastante para implementar o iterador eficientemente. Os Iterators no necessitam ser definidos to explicitamente em Smalltalk. As classes-padro de colees da linguagem (Bag, Set, Dictionary, OrderedCollection, String, etc.) definem um mtodo iterador interno do:, o qual aceita um bloco (isto , um fecho) como um argumento. Cada elemento na coleo vinculado varivel local no bloco; ento, o bloco executado. Smalltalk tambm inclui um conjunto de classes Stream que suporta uma interface semelhante a de um iterador. ReadStream essencialmente um Iterator, e pode funcionar como um iterador externo para todas as colees seqenciais. No h padres de iteradores externos para colees noseqenciais, tais como Set e Dictionary. Os Iteradores polimrficos e o Proxy de limpeza descritos anteriormente so fornecidos pelas classes container de ET++ [WGM88]. As classes do framework para edio grfica Unidraw utilizam iteradores baseados em cursores [VL90].

PADRES DE PROJETO

257

ObjectWindows 2.0 [Bor94] oferece uma hierarquia de classes de iteradores para containers. Voc pode iterar sobre diferentes tipos de continer da mesma maneira. A sintaxe da iterao do ObjectWindow depende de sobrecarregar o operador de psincremento ++ para avanar a iterao.

Padres relacionados
Composite (160): os Iterators so freqentemente aplicados a estruturas recursivas, tais como Composites. Factory Method (112): os Iteradores polimrficos dependem de mtodos fbrica para instanciar a subclasse apropriada de Iterator. Memento (266) freqentemente usado em conjunto com o padro Iterator. Um iterador pode usar um Memento para capturar o estado de uma iterao. O iterador armazena internamente o memento.

MEDIATOR
Inteno

comportamental de objetos

Definir um objeto que encapsula a forma como um conjunto de objetos interage. O Mediator promove o acoplamento fraco ao evitar que os objetos se refiram uns aos outros explicitamente e permite variar suas interaes independentemente.

Motivao
O projeto orientado a objetos encoraja a distribuio de comportamento entre vrios objetos. Tal distribuio pode resultar em uma estrutura de objetos com muitas conexes entre eles; na pior das situaes, cada objeto acaba tendo conhecimento sobre todos os outros objetos. Embora o particionamento de um sistema em muitos objetos geralmente melhore a reusabilidade, a proliferao de interconexes tende a reduzi-la novamente. Muitas interconexes tornam menos provvel que um objeto possa funcionar sem o apoio de outros o sistema funciona como se fosse monoltico. Alm do mais, pode ser difcil de mudar o comportamento do sistema de maneira significativa, uma vez que o comportamento est distribudo entre muitos objetos. Como resultado, voc pode se ver forado a definir muitas subclasses para customizar e adaptar o comportamento do sistema. Como exemplo, considere a implementao de caixas de dilogo em uma interface grfica de usurio. Uma caixa de dilogo usa uma janela para apresentar uma coleo de widgets, tais como botes, menus e campos de entrada como mostrado a seguir:

258

CAPTULO 5 PADRES

COMPORTAMENTAIS

debibold oblique

Freqentemente, existem dependncias entre os widgets no dilogo. Por exemplo, um boto torna-se desabilitado quando um certo campo de entrada est vazio. A seleo de uma entrada em uma lista de escolhas pode mudar o contedo de um campo de entrada. Reciprocamente, digitar texto no campo de entrada pode selecionar automaticamente uma ou mais entradas correspondentes na list box. Uma vez que aparece texto no campo de entrada, outros botes podem se tornar habilitados, permitindo ao usurio fazer alguma coisa com o texto, tal como mudar ou deletar a coisa qual o mesmo se refere. Diferentes caixas de dilogo tero diferentes dependncias entre widgets. Assim, muito embora dilogos exibam os mesmos tipos de widgets, no podem simplesmente reutilizar as classes widget disponveis no ambiente de desenvolvimento; elas devem ser customizadas para refletir as dependncias especficas do dilogo. A customizao individual das mesmas atravs de subclasses seria tediosa, uma vez que muitas classes esto envolvidas. Voc pode evitar esses problemas encapsulando o comportamento coletivo num objeto mediator separado. Um mediator, ou mediador, responsvel pelo controle e coordenao das interaes de um grupo de objetos. O mediador funciona como um intermedirio que evita que os objetos do grupo referenciem uns aos outros explicitamente. Os objetos somente conhecem o mediador, desta forma reduzindo o nmero de interconexes. Por exemplo, FontDialogDirector pode ser um mediador entre os widgets numa caixa de dilogo. Um objeto FontDialogDirector conhece os widgets de um dilogo e coordena sua interao. Ele funciona como um centro concentrador (hub) de comunicaes para os widgets:

PADRES DE PROJETO

259

O seguinte diagrama de interao ilustra como os objetos cooperam para tratar uma mudana numa seleo num list box:

Aqui est a sucesso de eventos pelos quais uma list box passa, no caso de manipulao de um campo de entrada: 1. 2. 3. 4. A list box notifica seu diretor (director) que ela foi mudada. O diretor obtm a seleo da list box. O diretor passa a seleo para o campo de entrada. Agora que o campo de entrada contm algum texto, o diretor habilita botes para iniciar uma ao (por exemplo, seminegrito, oblquo).

Observe como o diretor faz a mediao entre a list box e o campo de entrada. Os widgets se comunicam uns com os outros apenas indiretamente, atravs do diretor. Eles no precisam saber mais nada sobre cada um dos demais; tudo o que conhecem o diretor. Alm do mais, porque o comportamento est localizado em uma classe, pode ser mudado ou substitudo pela extenso ou substituio dessa classe. Eis como a abstrao FontDialogDirector pode ser integrada em uma biblioteca de classes:

260

CAPTULO 5 PADRES

COMPORTAMENTAIS

DialogDirector uma classe abstrata que define o comportamento geral de um dilogo. Os clientes invocam a operao ShowDialog para exibir o dilogo na tela. CreateWidgets uma operao abstrata para a criao dos widgets de um dilogo. WidgetChanged uma outra operao abstrata; os widgets a chamam para informar ao seu diretor de que eles mudaram. As subclasses de DialogDirector substituem CreateWidgets para criar os widgets apropriados e elas substituem WidgetChanged para tratar as mudanas.

Aplicabilidade
Utilize o padro Mediator quando: um conjunto de objetos se comunica de maneiras bem-definidas, porm complexas. As interdependncias resultantes so desestruturadas e difceis de entender. a reutilizao de um objeto difcil porque ele referencia e se comunica com muitos outros objetos. um comportamento que est distribudo entre vrias classes deveria ser customizvel, ou adaptvel, sem excessiva especializao em subclasses.

Estrutura
mediator

Uma estrutura de objeto tpica pode se assemelhar seguinte:

PADRES DE PROJETO

261

Participantes
Mediator(DialogDirector) define uma interface para comunicao com objetos de classe Colleague. ConcreteMediator(FontDialogDirector) implementa comportamento cooperativo atravs da coordenao de objetos de classe Colleague. conhece e mantm seus colegas. Colleague classes(ListBox, EntryField) cada classe Collegue conhece seu objeto Mediator de outra forma. cada colega se comunica com o seu mediador sempre que, de outra forma, teria que se comunicar com outro colega.

Colaboraes
Colegas enviam e recebem solicitaes de um objeto Mediator. O mediador implementa o comportamento cooperativo pelo direcionamento das solicitaes para os colegas apropriados.

Conseqncias
O padro Mediator tem os seguintes benefcios e problemas: 1. Ele limita o uso de subclasses. Um mediador localiza o comportamento que, de outra forma, estaria distribudo entre vrios objetos. Mudar este comportamento exige a introduo de subclasses somente para o Mediador; classes Colleague podem ser reutilizadas como esto. 2. Ele desacopla colegas. Um mediador promove um acoplamento fraco entre colegas. Voc pode variar e reutilizar as classes Colleague e Mediator independentemente. 3. Ele simplifica o protocolo dos objetos. Um mediador substitui interaes muitospara-muitos por interaes um-para-muitos entre o mediador e seus colegas. Relacionamentos um-para-muitos so mais fceis de compreender, manter e estender.

262

CAPTULO 5 PADRES

COMPORTAMENTAIS

4. Ele abstrai a maneira como os objetos cooperam. Tornando a mediao um conceito independente e encapsulando-a em um objeto, permite-lhe focalizar na maneira como os objetos interagem independente do seu comportamento individual. Isso pode ajudar a esclarecer como os objetos interagem em um sistema. 5. Ele centraliza o controle. O padro Mediator troca a complexidade de interao pela complexidade no mediador. Porque um mediador encapsula protocolos, pode se tornar mais complexo do que qualquer dos colegas individuais. Isso pode tornar o mediador um monolito difcil de manter.

Implementao
Os seguintes aspectos de implementao so relevantes para o padro Mediator: 1. Omisso da classe abstrata Mediator. No h necessidade de definir uma classe abstrata Mediator quando os colegas trabalham apenas com um mediador. O acoplamento abstrato que a classe Mediator fornece permite aos colegas trabalharem com diferentes subclasses de Mediator, e vice-versa. 2. Comunicao Colleague-Mediator. Colegas tm que se comunicar com o seu mediador quando ocorre um evento de interesse. Uma abordagem possvel implementar o Mediator como um Observer usando o padro Observer (274). Classes Colleague funcionam como Subjects, mandando notificaes para o mediador sempre que elas mudam de estado. O mediador responde propagando os efeitos da mudana para outros colegas. Uma outra abordagem define uma interface de notificao especializada em Mediator, que permite aos colegas serem mais diretos em suas comunicaes. Smalltalk/V para Windows utiliza uma forma de delegao: quando se comunicando com um mediador, um colega passa a si prprio como um argumento, permitindo a um mediador identificar o remetente. O Exemplo de Cdigo utiliza essa abordagem, e a implementao em Smalltalk/V discutida em mais detalhes na seo Usos Conhecidos.

Exemplo de cdigo
Usaremos um DialogDirector para implementar a caixa de dilogo de fontes tipogrficas mostradas na seo de Motivao. A classe abstrata DialogDirector define a interface para diretores. Widget a classe base abstrata para widgets. Um widget conhece o seu diretor.

Changed chama o diretor da operao WidgetChanged. Os widgets chamam

PADRES DE PROJETO

263

WidgetChanged no seu diretor para inform-lo de um evento significante.

As subclasses de DialogDirector redefinem WidgetChanged para afetar os widgets apropriados. O widget passa uma referncia de si prprio como argumento para WidgetChanged para permitir que o diretor identifique o widget que mudou. As subclasses de DialogDirector redefinem as virtuais puras CreateWidgets para construir os widgets do dilogo. ListBox, EntryField e Button so subclasses de Widget para elementos especializados da interface do usurio. ListBox fornece uma operao GetSelection para obter a seleo corrente e a operao SetText de EntryField coloca texto novo no campo.

Button um widget simples que chama Changed sempre que for pressionado. Isso feito na sua implementao de HandleMouse:

264

CAPTULO 5 PADRES

COMPORTAMENTAIS

A classe FontDialogDirector faz a mediao entre widgets na caixa de dilogo. FontDialogDirector uma subclasse de DialogDirector:

FontDialogDirector mantm o controle dos widgets que exibe. Ela redefine CreateWidgets para criar os widgets e iniciar suas referncias para eles:

WidgetChanged garante que os widgets trabalhem adequadamente em conjunto:

PADRES DE PROJETO

265

A complexidade de WidgetChanged aumenta proporcionalmente ao aumento da complexidade do dilogo. Naturalmente, por outras razes, grandes dilogos so indesejveis, mas a complexidade do mediador pode reduzir os benefcios do padro em outras aplicaes.

Usos conhecidos
Tanto ET++[WGM88] como a biblioteca de classes THINK C [Sym93b] usam objetos semelhantes a diretores em dilogos como mediadores entre widgets. A arquitetura de aplicaes da Smalltalk/V para Windows baseada numa estrutura de mediador [LaL94]. Nesse ambiente, uma aplicao consiste em uma janela (window) contendo um conjunto de painis (panes). A biblioteca contm diversos objetos Pane pr-definidos; exemplos incluem TextPane, ListBox, Button e assim por diante. Esses painis podem ser usados sem recorrer ao uso de subclasses. O desenvolvedor de uma aplicao somente cria subclasses de ViewManager, uma classe responsvel pela execuo da coordenao entre painis. ViewManager o Mediator, e cada painel somente conhece o seu view manager, o qual considerado proprietrio do painel. Painis no se referem uns aos outros diretamente. O diagrama de objeto a seguir mostra um instantneo de uma aplicao em tempo de execuo:

Smalltalk/V utiliza um mecanismo de eventos para comunicao Pane-ViewManager. Um painel gera um evento quando ele deseja obter informao do mediador ou quando deseja informar o mediador que algo de significante ocorreu. Um evento define um smbolo (por exemplo, # select) que identifica o evento. Para tratar do evento, o administrador de listas registra um seletor de mtodos com o painel. Esse seletor o tratador do evento e ser invocado sempre que o evento ocorrer. O fragmento de cdigo a seguir mostra como um objeto ListPane criado dentro de uma subclasse de ViewManager e como ViewManager registra um tratador de evento para o evento # select:

266

CAPTULO 5 PADRES

COMPORTAMENTAIS

Outra aplicao do padro Mediator a coordenao de atualizaes complexas. Um exemplo a classe ChangeManager mencionada em Observer (274). O ChangeManager faz a mediao entre subjects e observers para evitar atualizaes redundantes. Quando um objeto muda, ele notifica o ChangeManager, o qual, por sua vez, coordena a atualizao notificando os dependentes do objeto. Uma aplicao similar aparece no framework de desenho Unidraw [VL90] e utiliza uma classe chamada CSolver para garantir restries de conectividade entre conectores. Objetos em editores grficos podem parecer como que calando uns aos outros de vrias maneiras. Os conectores so teis em aplicaes que mantm automaticamente a conectividade, tais como editores de diagramas e sistemas de projeto de circuitos. CSolver um mediador entre conectores. Ele resolve as restries de conectividade e atualiza as posies dos conectores de modo a refleti-las.

Padres relacionados
O Faade (179) difere de Mediator no aspecto em que ele abstrai um subsistema de objetos para fornecer uma interface mais conveniente. Seu protocolo unidirecional; isto , objetos Faade das solicitaes fazem as classes dos subsistemas, mas no viceversa. Em comparao, o Mediator habilita o comportamento cooperativo que objetos-colegas no fornecem ou no podem fornecer, e o protocolo multidirecional. Os colegas podem se comunicar com o mediador usando o padro Observer (274).

MEMENTO
Inteno

comportamental de objetos

Sem violar o encapsulamento, capturar e externalizar um estado interno de um objeto, de maneira que o objeto possa ser restaurado para esse estado mais tarde.

Tambm conhecido como


Token

Motivao
Algumas vezes necessrio registrar o estado interno de um objeto. Isso necessrio na implementao de mecanismos de checkpoints e de desfazer (operaes) que permitem aos usurios retroceder de operaes-tentativas ou recuperar-se de erros. Voc deve salvar informao de estado em algum lugar, de modo que possa restaurar os objetos aos seus estados prvios. Porm, objetos normalmente encapsulam parte ou todos os seus estados, tornando-os inacessveis a outros objetos e impossveis de serem salvos externamente. Expor este estado violaria o encapsulamento, o que pode comprometer a confiabilidade e a extensibilidade da aplicao. Considere, por exemplo, um editor grfico que suporta conectividade entre objetos. Um usurio pode conectar dois retngulos com uma linha, e os retngulos permanecem conectados quando o usurio mover qualquer um deles. O editor assegura que a linha estique para manter a conexo.

PADRES DE PROJETO

267

Uma forma bem conhecida de manter relaes de conectividade entre objetos por meio de um sistema de soluo de restries. Podemos encapsular essa funcionalidade em um objeto ConstraintSolver. O ConstraintSolver registra as conexes medida que elas so efetuadas e gera equaes matemticas que as descrevem. Ele resolve essas equaes sempre que o usurio estabelece uma conexo ou modifica o diagrama de outra forma. O ConstraintSolver utiliza os resultados dos seus clculos para rearranjar os elementos grficos, de modo que mantenham as conexes apropriadas. Suportar a operao de desfazer nessa aplicao no to fcil quanto pode parecer. Uma maneira bvia de desfazer uma operao Mover armazenar a distncia original movida e mover o objeto de volta uma distncia equivalente. Contudo, isso no garante que todos os objetos aparecero onde estavam antes. Suponha que exista alguma folga na conexo. Nesse caso, simplesmente mover o retngulo de volta para a sua localizao original no resultar necessariamente no efeito desejado.

Em geral, a interface pblica do ConstraintSolver pode ser insuficiente para permitir a reverso precisa dos seus efeitos sobre outros objetos. O mecanismo de desfazer deve trabalhar mais intimamente com o ConstraintSolver para reestabelecer o estado prvio, mas tambm deveramos evitar a exposio dos detalhes de ConstraintSolver ao mecanismo de desfazer. Podemos resolver esse problema com o padro Memento. Um memento (recordao) um objeto que armazena um instantneo do estado interno de outro objeto o originador do memento. O mecanismo de desfazer solicitar um memento do originador, quando ele necessita fazer um checkpoint do estado do originador. O originador inicia o memento com informaes que caracterizam o seu estado corrente. Somente o originador pode armazenar e recuperar informao do memento o memento opaco para outros objetos. No exemplo do editor grfico que acabamos de discutir, o ConstraintSolver pode funcionar como um originador. A seguinte seqncia de eventos caracteriza o processo de desfazer: 1. O editor solicita um memento para o ConstraintSolver como um efeito colateral da operao mover.

268

CAPTULO 5 PADRES

COMPORTAMENTAIS

2. O ConstraintSolver cria e devolve um memento, neste caso, uma instncia de uma classe SolverState. Um memento de SolverState contm estruturas de dados que descrevem o estado corrente das equaes e variveis internas do ConstraintSolver. 3. Mais tarde, quando o usurio desfaz a operao mover, o editor devolve SolverState para o ConstraintSolver. 4. Baseado na informao do SolverState, o ConstraintSolver muda suas estruturas internas para retornar suas equaes e variveis ao seu estado prvio exato. Este arranjo permite que o Constraint Solver confie a outros objetos a informao de que necessita para reverter a um estado prvio, sem expor suas estruturas e representaes internas.

Aplicabilidade
Use o padro Memento quando: um instantneo de (alguma poro do) estado de um objeto deve ser salvo de maneira que possa ser restaurado para esse estado mais tarde; uma interface direta para obteno do estado exporia detalhes de implementao e romperia o encapsulamento do objeto.

Estrutura

Participantes
Memento (SolverState) armazena o estado interno do objeto Originator. O memento pode armazenar pouco ou muito do estado interno do originator, conforme necessrio e segundo critrios do seu originador. protege contra acesso por objetos que no o originador. Mementos tm efetivamente duas interfaces. O Caretaker v uma interface mnima do memento ele somente pode passar o memento para outros objetos. O originador, diferentemente, v uma interface ampla, que lhe permite acessar todos os dados necessrios para se restaurar ao seu estado prvio. Idealmente, somente o originador que produziu o memento teria o acesso permitido ao seu estado interno. Originator (ConstraintSolver) cria um memento contendo um instantneo do seu estado interno corrente. usa o memento para restaurar o seu estado interno.

PADRES DE PROJETO

269

Caretaker (undo mechanism) responsvel pela custdia do memento. nunca opera ou examina os contedos de um memento.

Colaboraes
um caretaker solicita um memento de um originador, mantm o mesmo por um certo tempo e o devolve para o originador, como ilustra o seguinte diagrama de interao:

Algumas vezes, o caretaker no devolver o memento para o originador porque o originador pode nunca necessitar voltar a um estado anterior. Mementos so passivos. Somente o originador que criou um memento atribuir ou recuperar o seu estado.

Conseqncias
O padro Memento tem vrias conseqncias: 1. Preservao das fronteiras de encapsulamento. Memento evita a exposio de informao que somente um originador deveria administrar, mas que, contudo, deve ser armazenada fora do originador. O padro protege outros objetos de aspectos internos potencialmente complexos do Originador, desta maneira preservando as fronteiras de encapsulamento. 2. Ele simplifica o Originador. Em outros projetos de preservao do encapsulamento, o Originador mantm as verses do estado interno solicitadas pelos clientes. Isso coloca toda a carga de administrao do armazenamento sobre o Originador. Deixar os clientes administrarem o estado que solicitam simplifica o Originador e evita que eles tenham que notificar os originadores quando terminarem a utilizao. 3. O uso de mementos pode ser computacionalmente caro. Mementos podem produzir custos adicionais considerveis se o Originador tiver de copiar grandes quantidades de informao para armazenar no memento, ou se os clientes criam e devolvem mementos para o originador com muita freqncia. A menos que seja barato encapsular e restaurar o estado do Originador, o padro pode no ser apropriado. Veja a discusso sobre incrementabilidade na seo Implementao. 4. Definio de interfaces mnimas e amplas. Pode ser difcil em algumas linguagens garantir que somente o originador possa acessar o estado do memento.

270

CAPTULO 5 PADRES

COMPORTAMENTAIS

5. Custos ocultos na custdia de mementos. Um caretaker responsvel por deletar o memento do qual ele tem a custdia. Contudo, o caretaker no tem idia do volume ocupado pelo estado do memento. Da um caretaker leve poder incorrer em grandes custos de armazenamento quando armazena mementos.

Implementao
Aqui temos dois aspectos a serem considerados ao implementar o padro Memento: 1. Suporte de linguagem. Mementos tm duas interfaces: uma ampla, para os originadores, e uma mnima, para outros objetos. Idealmente, a linguagem de implementao suportaria dois nveis de proteo esttica. C++ lhe permite fazer isso tornando o Originador uma friend de Memento e tornando privada a interface ampla de Memento. Somente a interface estreita seria declarada pblica. Por exemplo:

estrutura de dados internas

membros privados acessveis apenas ao Originator

2. Armazenando mudanas incrementais. Quando mementos so criados e passados de volta para o seu originador, em uma seqncia previsvel, um Memento pode salvar somente a mudana incremental do estado interno do originador. Por exemplo, comandos que podem ser desfeitos em uma lista histrica, podem usar mementos para assegurar que sejam restaurados para o seu exato estado quando so desfeitos (ver Command, 222). A lista histrica define uma ordem especfica pela qual comandos podem ser desfeitos e refeitos. Isso significa que mementos podem armazenar apenas a mudana incremental que resulta dos comandos, em vez do estado completo de todos objetos que afetam. No exemplo

PADRES DE PROJETO

271

anterior na seo Motivao, o solucionador de restries precisa armazenar somente aquelas estruturas internas que mudam para manter a linha que o conecta aos retngulos, em contraste ao armazenamento das posies absolutas desses objetos.

Exemplos de cdigo
O cdigo C++ dado aqui ilustra o exemplo do ConstraintSolver discutido anteriormente. Podemos usar objetos MoveCommand (ver Command, 222) para (des)fazer a translao de um objeto grfico de uma posio para outra. O editor grfico chama a operao Execute do comando para mover um objeto grfico e Unexecute para desfazer a movimentao. O comando armazena seu alvo, a distncia movida e uma instncia de ConstraintSolverMemento, um memento contendo um estado do solucionador de restries.

classe-base para objetos grficos no editor grfico

As restries de conexes so estabelecidas pela classe ConstraintSolver. Sua funo-membro-chave Solve, a qual resolve as restries registradas com a operao AddConstraint. Para suportar desfazer, o estado de ConstraintSolver pode ser externalizado com CreateMemento numa instncia de ConstraintSolverMemento. O solucionador de restries pode ser retornado a um estado prvio chamando SetMemento. ConstraintSolver um Singleton (130).

272

CAPTULO 5 PADRES

COMPORTAMENTAIS

Dadas estas interfaces, ns podemos implementar os membros Execute e


Unexecute, de MoveCommand como segue:

O Execute adquire um memento de ConstraintSolverMemento antes de mover o objeto grfico. Unexecute move o objeto grfico de volta, volta o estado do solucionador de restries para o momento prvio e finalmente pede ao solucionador de restries para solucionar as mesmas.

Usos conhecidos
O exemplo de cdigo procedente baseado no suporte para conectividade fornecido pela classe CSolver do Unidraw [VL90]. As colees na Dylan [App92] fornecem uma interface de iterao que reflete o padro Memento. As colees da Dylan tm a noo de um objeto estado, o qual um memento que representa o estado da iterao. Cada coleo pode representar o estado corrente da iterao de qualquer maneira que ela escolher; a representao completamente ocultada dos clientes. A abordagem da iterao da Dylan pode ser traduzida para C++ como segue:

PADRES DE PROJETO

273

CreateInitialState retorna um objeto iniciado IterationState para a coleo. Next avana o estado do objeto para a posio seguinte na iterao; ele efetivamente incrementa o ndice da iterao. IsDone retorna true se Next avanou alm do ltimo elemento da coleo. CurrentItem desreferencia o objeto-estado e retorna o elemento da coleo ao qual se refere. Copy retorna uma cpia do objeto-estado dado. Isto til para marcar um ponto numa iterao. Dada uma classe ItemType, ns podemos iterar sobre uma coleo de instncias suas, como segue 7:

A interface baseada em mementos para a iterao tem dois benefcios interessantes: 1. Mais de um estado pode trabalhar sobre a mesma coleo (o mesmo verdade para o padro Iterator (244). 2. No necessrio romper o encapsulamento de um conjunto para suportar iterao. Um memento somente interpretado pelo prprio conjunto; ningum mais tem acesso a ele. Outras solues para iterao exigem romper o encapsulamento, tornando as classes de iteradores friends das suas classes de coleo (ver Iterator, 244). A situao revertida na implementao baseada em mementos: Collection uma friend de IteratorState. O toolkit de soluo de restries QOCA armazena informaes incrementais em mementos [HHMV92]. Os clientes podem obter um memento que caracteriza a soluo

274

CAPTULO 5 PADRES

COMPORTAMENTAIS

corrente de um sistema de restries. O memento contm somente aquelas variveis de restrio que mudaram desde a ltima soluo. Normalmente, apenas um pequeno subconjunto das variveis do solucionador muda para cada nova soluo. Esse subconjunto suficiente para retornar o solucionador para a soluo precedente; a reverso para solues anteriores exige a restaurao de mementos das solues intermedirias. Por isso, voc no pode definir mementos em qualquer ordem; QOCA depende de um mecanismo de histria para reverter a solues anteriores.

Padres relacionados
Command (222): Comands podem usar mementos para manter estados para operaes que normalmente no poderiam ser desfeitas. Iterator (244): Mementos podem ser usados para iterao, conforme j descrito.

OBSERVER
Inteno

comportamental de objetos

Definir uma dependncia um-para-muitos entre objetos, de maneira que quando um objeto muda de estado todos os seus dependentes so notificados e atualizados automaticamente.

Tambm conhecido como


Dependents, Publish-Subscribe

Motivao
Um efeito colateral comum resultante do particionamento de um sistema em uma coleo de classes cooperantes a necessidade de manter a consistncia entre objetos relacionados. Voc no deseja obter consistncia tornando as classes fortemente acopladas, porque isso reduz a sua reusabilidade. Por exemplo, muitos toolkits para construo de interfaces grficas de usurio separam os aspectos de apresentao da interface do usurio dos dados da aplicao subjacente [KP88,LVC89,P+88, WGM88]. As classes que definem dados da aplicao e da apresentao podem ser reutilizadas independentemente. Elas tambm podem trabalhar em conjunto. Tanto um objeto planilha como um objeto grfico de barras podem ilustrar informaes do mesmo objeto de aplicao usando diferentes apresentaes. A planilha e o grfico de barras no tm conhecimento um do outro, desta forma permitindo reutilizar somente o objeto de que voc necessita. Porm, elas se comportam como se conhecessem. Quando o usurio muda a informao na planilha, o grfico de barras reflete as mudanas imediatamente, e vice-versa.

PADRES DE PROJETO

275

notificao de mudana solicitaes, modificaes

Esse comportamento implica que a planilha e o grfico de barras so dependentes do objeto de dados e, portanto, deveriam ser notificados sobre qualquer mudana no seu estado. E no h razo para limitar o nmero de dependentes a dois objetos; pode haver um nmero qualquer de diferentes interfaces do usurio para os mesmos dados. O padro Observer descreve como estabelecer esses relacionamentos. Os objetoschave nesse padro so subject (assunto) e observer (observador). Um subject pode ter um nmero qualquer de observadores dependentes. Todos os observadores so notificados quando o subject sofre uma mudana de estado. Em resposta, cada observador inquirir o subject para sincronizar o seu estado com o estado do subject. Esse tipo de interao tambm conhecido como publish-subscribe. O subject o publicador de notificaes. Ele envia essas notificaes sem ter que saber quem so os seus observadores. Um nmero qualquer de observadores pode inscrever-se para receber notificaes.

Aplicabilidade
Use o padro Observer em qualquer uma das seguintes situaes: quando uma abstrao tem dois aspectos, um dependente do outro. Encapsulando esses aspectos em objetos separados, permite-se vari-los e reutiliz-los independentemente; quando uma mudana em um objeto exige mudanas em outros, e voc no sabe quantos objetos necessitam ser mudados; quando um objeto deveria ser capaz de notificar outros objetos sem fazer hipteses, ou usar informaes, sobre quem so esses objetos. Em outras palavras, voc no quer que esses objetos sejam fortemente acoplados.

Estrutura
Subject
Attach(Observer) Detach(Observer) Notify() observers

Observer Update()

for all o in observers { o>Update() } ConcreteObserver

ConcreteSubject GetState() SetState() subjectState return subjectState

subject

Update() observerState

observerState= subject>GetState()

276

CAPTULO 5 PADRES

COMPORTAMENTAIS

Participantes
Subject conhece os seus observadores. Um nmero qualquer de objetos Observer pode observar um subject. fornece uma interface para acrescentar e remover objetos, permitindo associar e desassociar objetos observer. Observer define uma interface de atualizao para objetos que deveriam ser notificados sobre mudanas em um Subject. ConcreteSubject armazena estados de interesse para objetos ConcreteObserver. envia uma notificao para os seus observadores quando seu estado muda. ConcreteObserver mantm uma referncia para um objeto ConcreteSubject. armazena estados que deveriam permanecer consistentes com os do Subject. implementa a interface de atualizao de Observer, para manter seu estado consistente com o do subject.

Colaboraes
O ConcreteSubject notifica seus observadores sempre que ocorrer uma mudana que poderia tornar inconsistente o estado deles com o seu prprio. Aps ter sido informado de uma mudana no subject concreto, um objeto ConcreteObserver poder consultar o subject para obter informaes. O ConcreteObserver usa essa informao para reconciliar o seu estado com o do subject. O seguinte diagrama de interao ilustra as colaboraes entre um subject e dois observadores:

Note como o objeto Observer que inicia a solicitao de mudana posterga sua atualizao at que ele consiga uma notificao do subject. Notify no sempre chamada pelo subject. Pode ser chamada por um observador ou por um outro tipo de objeto. A seo Implementao discute algumas variaes comuns.

Conseqncias
O padro Observer permite variar subjects e observadores de forma independente. Voc pode reutilizar subjects sem reutilizar seus observadores e vice-versa. Ele permite acrescentar observadores sem modificar o subject ou outros observadores. Benefcios adicionais e deficincias do padro Observer incluem o seguinte:

PADRES DE PROJETO

277

1. Acoplamento abstrato entre Subject e Observer. Tudo que o subject sabe que ele tem uma lista de observadores, cada um seguindo a interface simples da classe abstrata Observer. O subject no conhece a classe concreta de nenhum observador. Assim, o acoplamento entre o subject e os observadores abstrato e mnimo. Por no serem fortemente acoplados, Subjet e Observer podem pertencer a diferentes camadas de abstrao em um sistema. Um subject de nvel mais baixo pode comunicar-se com um observador de nvel mais alto, desta maneira mantendo intacta as camadas do sistema. Se Subject e Observer forem agrupados, ento o objeto resultante deve cobrir duas camadas (e violar a estrutura de camadas), ou ser forado a residir em uma das camadas (o que pode comprometer a abstrao da estrutura de camadas). 2. Suporte para comunicaes do tipo broadcast. Diferentemente de uma solicitao ordinria, a notificao que um subject envia no precisa especificar seu receptor. A notificao transmitida automaticamente para todos os objetos interessados que a subscreveram. O subject no se preocupa com quantos objetos interessados existem; sua nica responsabilidade notificar seus observadores. Isso d a liberdade de acrescentar e remover observadores a qualquer momento. responsabilidade do observador tratar ou ignorar uma notificao. 3. Atualizaes inesperadas. Como um observador no tem conhecimento da presena dos outros, eles podem ser cegos para o custo global de mudana do subject. Uma operao aparentemente incua no subject pode causar uma cascata de atualizaes nos observadores e seus objetos dependentes. Alm do mais, critrios de dependncia que no esto bem-definidos ou mantidos normalmente conduzem a atualizaes esprias que podem ser difceis de detectar. Este problema agravado pelo fato de que o protocolo simples de atualizao no fornece detalhes sobre o que mudou no subject. Sem protocolos adicionais para ajudar os observadores a descobrir o que mudou, eles podem ser forados a trabalhar duro para deduzir as mudanas.

Implementao
Nesta seo so discutidos vrios aspectos relacionados com a implementao do mecanismo de dependncia. 1. Mapeando subjects para os seus observadores. A maneira mais simples para um subject manter o controle e o acompanhamento dos observadores que ele deve notificar armazenar referncias para eles explicitamente no subject. Contudo, tal armazenagem pode ser muito dispendiosa quando existem muitos subjects e poucos observadores. Uma soluo trocar espao por tempo usando um mecanismo de pesquisa associativo (por exemplo, uma hash table tabela de acesso randmico) para manter o mapeamento subjectpara-observador. Assim, um subject sem observadores no tem um custo de memria para esse problema. Por outro lado, esta soluo aumenta o custo do acesso aos observadores. 2. Observando mais do que um subject. Em algumas situaes pode fazer sentido para um observador depender de mais do que um subject. Por exemplo, uma planilha pode depender de uma fonte de dados. necessrio estender a

278

CAPTULO 5 PADRES

COMPORTAMENTAIS

interface de Update em tais casos para permitir ao observador saber qual subject est enviando a notificao. O subject pode, simplesmente, passar a si prprio como um parmetro para a operao Update, dessa forma permitindo ao observador saber qual subject examinar. 3. Quem dispara a atualizao? O subject e seus observadores dependem do mecanismo de notificao para permanecerem consistentes. Mas qual objeto na realidade chama Notify para disparar a atualizao? Aqui esto duas opes: (a) ter operaes de estabelecimento de estados no Subject que chame Notify aps elas mudarem o estado do subject. A vantagem dessa soluo que os clientes no tm que lembrar de chamar Notify no subject. A desvantagem que diversas operaes consecutivas causaro diversas atualizaes consecutivas, o que pode ser ineficiente. (b) tornar os clientes responsveis por chamar Notify no momento correto. Aqui, a vantagem que o cliente pode esperar para disparar a atualizao at que seja concluda uma srie de mudanas de estado, desta forma evitando atualizaes intermedirias desnecessrias. A desvantagem que os clientes tm uma responsabilidade adicional, de disparar a atualizao. Isto torna a ocorrncia de erros mais provvel, uma vez que os clientes podem esquecer de chamar Notify. 4. Referncias ao vazio (dangling references) para subjects deletados. A remoo de um subject no deve produzir referncias ao vazio nos seus observadores. Uma forma de evitar referncias ao vazio fazer com que o subject notifique os seus observadores quando deletado, de modo que possam restabelecer suas referncias. Em geral, simplesmente deletar os observadores no uma opo, porque outros objetos podem referenci-los ou porque eles tambm podem estar observando outros subjects. 5. Garantindo que o estado do Subject autoconsistente antes da emisso da notificao. importante se assegurar de que o estado do Subject autoconsistente antes de invocar Notify, porque os observadores consultam o subject sobre o seu estado corrente no curso da atualizao de seus prprios estados. Esta regra de autoconsistncia fcil de violar no-intencionalmente quando operaes nas subclasses de Subject chamam operaes herdadas. Por exemplo, a notificao na seqncia de cdigo seguinte disparada quando o subject est em um estado inconsistente:

Podemos evitar essa armadilha enviando notificaes por mtodos-template (Template Method, 301) em classes Subject abstratas. Defina uma operao primitiva para ser substituda pelas subclasses e torne Notify a ltima operao do mtodo-template, o que garantir que o objeto autoconsistente quando as subclasses substituem operaes de Subject. A propsito, sempre uma boa idia documentar quais operaes de Subject disparam notificaes.

PADRES DE PROJETO

279

6. Evitando protocolos de atualizao especficos dos observadores: os modelos push e pull. A implementao do padro Observer freqentemente tem o subject emitindo (broadscast) informaes adicionais sobre a modificao. O subject passa essa informao como argumento para Update. A quantidade de informao pode variar bastante. Num extremo, que chamaremos de push model (modelo de empurrar informao), o subject manda aos observadores informaes detalhadas sobre a mudana, quer eles queiram ou no. No outro extremo, est o pull model (modelo de puxar informao); o subject no envia nada alm da menor notificao possvel, e posteriormente os observadores solicitam detalhes. O pull model enfatiza o desconhecimento dos subjects sobre seus observadores, enquanto que o push model assume que os subjects sabem algo das necessidades de seus observadores. O push model pode tornar os observadores menos reutilizveis porque as classes Subject fazem hipteses sobre as classes Observer, que podem no ser sempre verdadeiras. Por outro lado, o pull model pode ser ineficiente porque as classes Observer devem verificar o que mudou sem a ajuda do Subject. 7. Especificando explicitamente as modificaes de interesse. Voc pode melhorar a eficincia do processo de atualizao estendendo a interface de inscrio no subject para permitir aos observadores se registrarem somente para eventos especficos de seu interesse. Quando tal evento ocorre, o subject informa somente os observadores que se registraram. Uma maneira de suportar isso usa a noo de aspectos para objetos Subject. Para registrar o seu interesse em determinados eventos, os observadores so associados aos seus subjects usando

onde interest especifica o evento de interesse. Em tempo de notificao, o subject fornece o aspecto mudado para os seus observadores, como um parmetro para a operao Update. Por exemplo:

8. Encapsulando a semntica de atualizaes complexas. Quando o relacionamento de dependncia entre subjects e observadores particularmente complexo pode ser necessrio um objeto que mantenha esses relacionamentos. Chamamos tal objeto de ChangeManager (Administrador de Mudanas). Sua finalidade minimizar o trabalho necessrio para fazer com que os observadores reflitam uma mudana no seu subject. Por exemplo, se uma operao envolve mudanas em vrios subjects interdependentes, voc pode ter que assegurar que seus observadores sejam notificados somente aps todos subjects terem sido modificados para evitar de notificar os observadores mais de uma vez.

280

CAPTULO 5 PADRES

COMPORTAMENTAIS

O ChangeManager tem trs responsabilidades: (a) ele mapeia um subject aos seus observadores e fornece uma interface para manter esse mapeamento. Isto elimina a necessidade dos subjects manterem referncias para os seus observadores, e vice-versa; (b) ele define uma estratgia de atualizao especfica; (c) ele atualiza todos os observadores dependentes, por solicitao de um subject. O diagrama a seguir ilustra uma implementao simples, baseada no uso de um ChangeManager, do padro Observer. Existem dois ChangeManagers especializados. O SimpleChangeManager ingnuo no sentido de que sempre atualiza todos os observadores de cada subject. Por outro lado, o DAGChangeManager trata grafos acclicos direcionados de dependncias entre subjects e seus observadores. Um DAGChangeManager prefervel a um SimpleChangeManager quando um observador observa mais do que um subject. Nesse caso, uma mudana em dois ou mais subjects pode causar atualizaes redundantes. O DAGChangeManager assegura que o observador receba somente uma atualizao. Quando mltiplas atualizaes no so um problema, SimpleChangeManager funciona bem.

O ChangeManager uma instncia do padro Mediator (257). Em geral, existe somente um ChangeManager e ele conhecido globalmente. Aqui, o padro Singleton (130) seria til. 9. Combinando as classes Subject e Observer. As bibliotecas de classes escritas em linguagens que no tm herana mltipla (como Smalltalk) geralmente no definem classes Subject e Observer separadas, mas combinam suas interfaces em uma nica classe. Isso permite definir um objeto que funciona tanto como um subject como um observe, sem usar herana mltipla. Por exemplo, em Smalltalk, as interfaces de Subject e de Observer esto definidas na classe raiz Object, tornando-as disponveis para todas as classes.

Exemplo de cdigo
Uma classe abstrata define a interface de Observer:

PADRES DE PROJETO

281

Esta implementao suporta mltiplos subjects para cada observador. O subject passado para a operao Update permite ao observador determinar qual subject mudou quando ele observa mais de um.

De maneira semelhante, uma classe abstrata define a interface de Subject:

ClockTimer um subject concreto para armazenar e manter a hora do dia. Ele notifica seus observadores a cada segundo. ClockTimer fornece a interface para a recuperao de unidades de tempo individuais, como hora, minuto e segundo.

282

CAPTULO 5 PADRES

COMPORTAMENTAIS

A operao Tick chamada por um relgio interno, a intervalos regulares, para fornecer uma base de tempo precisa. Tick atualiza o estado interno de ClockTimer e chama Notify para informar os observadores sobre a mudana:
atualiza o estado interno de manuteno do tempo

Agora queremos definir uma classe DigitalClock que exibe a hora. Ela herda a sua funcionalidade grfica de uma classe Widget fornecida por um toolkit para interfaces de usurio. A interface de Observer misturada com a interface de DigitalClock por herana de Observer.

redefine a operao de Observer

redefine a operao de Widget define como desenhar o relgio digital

Antes que a operao Update mude o mostrador do relgio, ela faz a verificao para se assegurar de que o subject notificador o subject do relgio:

PADRES DE PROJETO

283

obtm os novos valores do subject

desenha o relgio digital

Da mesma maneira pode ser definida uma classe AnalogClock.

O cdigo a seguir cria uma AnalogClock e uma DigitalClock que sempre mostra o mesmo tempo:

Sempre que o timer pulsar e notificar, os dois relgios sero atualizados e reexibiro a si prprios adequadamente.

Usos conhecidos
O primeiro e talvez mais conhecido exemplo do padro Observer aparece no Model/ View/Controller (MVC), da Smalltalk, o framework para a interface do usurio no ambiente Smalltalk [KP88]. A classe Model, do MVC, exerce o papel do Subject, enquanto View a classe base para observadores. Smalltalk, ET++ [WGM88] e a biblioteca de classes THINK [Sym93b] fornecem um mecanismo geral de dependncia colocando interfaces para Subject e Observer na classe-me para todas as outras classes do sistema. Outros toolkits para interfaces de usurio que empregam esse padro so: InterViews [LVC89], Andrew Toolkit [P+88] e Unidraw [VL90]. O InterViews define classes Observer e Observable (para subjects) explicitamente. Andrew as chama view e data object, respectivamente. O Unidraw separa objetos do editor grfico em View (para observadores) e Subject.

Padres relacionados
Mediator (257): encapsulando a semntica de atualizaes complexas, o ChangeManager atua como um mediador entre subjects e observadores.

284

CAPTULO 5 PADRES

COMPORTAMENTAIS

Singleton (130): O ChangeManager pode usar o padro Singleton para torn-lo nico e globalmente acessvel.

STATE
Inteno

comportamental de objetos

Permite a um objeto alterar seu comportamento quando o seu estado interno muda. O objeto parecer ter mudado sua classe.

Tambm conhecido como


Objects for States

Motivao
Considere a classe TCPConnection que representa uma conexo numa rede de comunicaes. Um objeto TCPConnection pode estar em diversos estados diferentes: Established (Estabelecida), Listening (Escutando), Closed (Fechada). Quando um objetoTCPConnection recebe solicitaes de outros objetos, ele responde de maneira diferente dependendo do seu estado corrente. Por exemplo, o efeito de uma solicitao de Open (Abrir), depende de se a conexo est no seu estado Closed ou no seu estado Established. O padro State descreve como TCPConnection pode exibir um comportamento diferente em cada estado. A idia chave deste padro introduzir uma classe abstrata chamada TCPState para representar os estados da conexo na rede. A classe TCPState declara uma interface comum para todas as classes que representam diferentes estados operacionais. As subclasses de TCPState implementam comportamentos especficos ao estado. Por exemplo, as classes TCPEstablished e TCPClosed implementam comportamento especfico aos estados Established e Closed de TCPConnection.

A classe TCPConnection mantm um objeto de estado (uma instncia da subclasse de TCPstate) que representa o estado corrente na conexo TCP. Connection delega todas as solicitaes especficas de estados para este objeto de estado. TCPConnection usa sua instncia da subclasse de TCPState para executar operaes especficas ao estado da conexo.

PADRES DE PROJETO

285

Sempre que a conexo muda de estado, o objeto TCPConnection muda o objeto de estado que ele utiliza. Por exemplo, quando a conexo passa do estado Established para o estado Closed, TCPConnection substituir sua instncia de TCPEstablished por uma instncia de TCPClosed.

Aplicabilidade
Use o padro State em um dos dois casos seguintes: o comportamento de um objeto depende do seu estado e ele pode mudar seu comportamento em tempo de execuo, dependendo desse estado; operaes tm comandos condicionais grandes, de vrias alternativas, que dependem do estado do objeto. Esse estado normalmente representado por uma ou mais constantes enumeradas. Freqentemente, vrias operaes contero essa mesma estrutura condicional. O padro State coloca cada ramo do comando adicional em uma classe separada. Isto lhe permite tratar o estado do objeto como um objeto propriamente dito, que pode variar independentemente de outros objetos.

Estrutura

Participantes
Context (TCPConnection) define a interface de interesse para os clientes. mantm uma instncia de uma subclasse ConcreteState que define o estado corrente. State (TCPState) define uma interface para encapsulamento associado com um determinado estado do Context. ConcreteState subclasses (TCPEstablished, TCPListen, TCPClosed) cada subclasse implementa um comportamento associado com um estado do Context.

Colaboraes
O Context delega solicitaes especficas de estados para o objeto corrente ConcreteState. Um contexto pode passar a si prprio como um argumento para o objeto State que trata a solicitao. Isso permite ao objeto State acessar o contexto, se necessrio.

286

CAPTULO 5 PADRES

COMPORTAMENTAIS

Context a interface primria para os clientes. Os clientes podem configurar um contexto com objetos State. Uma vez que o contexto est configurado, seus clientes no tm que lidar com os objetos State diretamente. Tanto Context quanto as subclasses de ConcreteState podem decidir qual estado sucede outro, e sob quais circunstncias.

Conseqncias
O padro State tem as seguintes conseqncias: 1. Ele confina comportamento especfico de estados e particiona o comportamento para estados diferentes. O padro State coloca todo o comportamento associado com um estado particular em um objeto. Como todo o cdigo especfico a um estado reside numa subclasse de State, novos estados e transies de estado podem ser facilmente adicionados pela definio de novas subclasses. Uma alternativa usar valores de dados para definir estados internos, tendo operaes de Context que verificam explicitamente os dados. Mas, em conseqncia, teramos instrues parecidas, condicionais ou de seleo (Case) espalhadas por toda a implementao de Context. O acrscimo de um novo estado poderia exigir a mudana de vrias operaes, o que complicaria a manuteno. O padro State evita esse problema, mas introduz um outro, porque o padro distribui o comportamento para diversos estados entre vrias subclasses de State. Isso aumenta o nmero de classes e menos compacto do que ter uma nica classe. Porm, tal distribuio , na realidade, boa se existirem muitos estados, pois de outro modo necessitaramos de grandes comandos condicionais. Do mesmo modo que procedures longas so indesejveis, tambm o so comandos condicionais grandes. Eles so monolticos e tendem a tornar o cdigo menos explcito, o que, por sua vez torna-os difceis de modificar e estender. O padro State oferece uma maneira melhor para estruturar o cdigo especfico de estados. A lgica que determina as transies de estado no se localiza em comandos monolticos if ou switch, mas particionada entre as subclasses de State. O encapsulamento de cada transio de estado e ao associada em uma classe eleva a idia de um estado de execuo ao status de um objeto propriamente dito. Isso impe uma estrutura ao cdigo e torna a sua inteno mais clara. 2. Ele torna explcitas as transies de estado. Quando um objeto define o seu estado corrente unicamente em termos de valores de dados internos, suas transies de estado no tm representao explcita; elas apenas aparecem como atribuies de valores a algumas variveis. A introduo de objetos separados, para estados diferentes, torna as transies mais explcitas. Os objetos State tambm podem proteger o Context de estados internos inconsistentes porque, da perspectiva do Context, as transies de estado so atmicas elas acontecem pela revinculao (rebinding) de uma varivel (a varivel que contm o objeto State de Context), e no de vrias [dCLF93]. 3. Objetos State podem ser compartilhados. Se os objetos State no possuem variveis de instncia ou seja, o estado que elas representam est codificado inteiramente no seu tipo ento contextos podem compartilhar um objeto State. Quando estados so compartilhados dessa maneira, eles so essenci-

PADRES DE PROJETO

287

almente flyweights (ver Flyweight, 187) sem estado intrnseco, somente comportamento.

Implementao
O padro State d origem a diversas questes de implementao: 1. Quem define as transies de estado? O padro State no especifica qual participante define os critrios para transies de estado. Se os critrios so fixos, ento podem ser implementados inteiramente no Context. Contudo, geralmente mais flexvel e adequado permitir que as subclasses de State especifiquem elas mesmas o seu estado sucessor e quando efetuar a transio. Isto exige o acrscimo de uma interface para Context que permite aos objetos State definirem explicitamente o estado corrente de Context. A forma de descentralizar a lgica de transio torna mais fcil modificar ou estender a lgica pela definio de novas subclasses State. Uma desvantagem da descentralizao que uma subclasse State ter conhecimento de pelo menos uma outra, o que introduz dependncias de implementao entre subclasses. 2. Uma alternativa baseada no uso de tabelas. Em C++ Programming Style [Car92], Cargill descreve uma outra maneira de impor estrutura a um cdigo orientado por estados: ele usa tabelas para mapear entradas para transies de estado. Para cada estado, uma tabela mapeia cada entrada possvel para um estado sucessor. Com efeito, essa soluo converte o cdigo condicional (e, no caso de padro State, funes virtuais) em uma pesquisa de tabela. A principal vantagem do uso de tabelas sua regularidade: voc pode mudar os critrios de transio atravs da modificao de dados em vez da modificao do cdigo de programas. Contudo, aqui apresentamos algumas desvantagens: Uma pesquisa de tabela freqentemente menos eficiente do que a chamada (virtual) de uma funo. A colocao da lgica de transio num formato uniforme, tabular, torna os critrios de transio menos explcitos e, portanto, mais difceis de compreender. normalmente difcil acrescentar aes para acompanhar as transies de estado. A soluo baseada em tabelas captura os estados e suas transies, porm, ela deve ser aumentada para executar computaes arbitrrias em cada transio. A diferena-chave entre mquinas de estado guiadas por tabelas e o padro State pode ser resumida desta maneira: o padro State modela o comportamento especfico de estados, enquanto que a abordagem voltada para tabelas se concentra na definio das transies de estado. 3. Criando e destruindo objetos State. Duas alternativas comuns de implementao que valem a pena considerar so: (1) criar objetos State apenas quando so necessrios e destru-los logo que se tornem desnecessrios e (2) cri-los antecipadamente e nunca destru-los. A primeira escolha prefervel quando os estados que sero atingidos no so conhecidos em tempo de execuo e os contextos mudam de estado com pouca freqncia. Esta soluo evita a criao de objetos que no sero usados, o que

288

CAPTULO 5 PADRES

COMPORTAMENTAIS

importante se os objetos State armazenarem grande quantidade de informao. A segunda soluo melhor quando as mudanas de estado ocorrem rapidamente, sendo que nesse caso voc deseja evitar a destruio de estados porque sero necessrios novamente em breve. Os custos de instanciao acontecem somente uma vez antes do processo, e no existe nenhum custo de destruio. Entretanto, esta soluo pode ser inconveniente porque o Context deve manter referncias para todos os estados que possam ser alcanados. 4. Usando herana dinmica. A mudana de comportamento para uma determinada solicitao poderia ser obtida pela mudana da classe do objeto em tempo de execuo, porm, isso no possvel na maioria das linguagens de programao orientadas a objetos. As excees incluem Self [US87] e outras linguagens baseadas em delegao que oferecem tal mecanismo e, portanto, suportam diretamente o padro State. Objetos em Self podem delegar operaes para outros objetos para obter uma forma de herana dinmica. A mudana do alvo da delegao, em tempo de execuo, efetivamente muda a estrutura de herana. Esse mecanismo permite aos objetos mudarem o seu comportamento e corresponde a mudar as suas classes.

Exemplo de cdigo
O exemplo a seguir fornece o cdigo C++ para o exemplo de conexes TCP descrito na seo Motivao. Este exemplo uma verso simplificada do protocolo TCP; ele no descreve o protocolo completo ou todos os estados das conexes TCP8. Primeiramente, definiremos a classe TCPConnection, a qual fornece uma interface para transmisso de dados e trata solicitaes para mudanas de estado.

TCPConnection mantm uma instncia da classe TCPState na varivel membro _state. A classe TCPState duplica a interface de mudana de estados de TCPConnection. Cada operao TCPState recebe uma instncia de TCPConnection como um parmetro, permitindo a TCPState acessar dados de TCPConnection e mudar o estado

da conexo.

PADRES DE PROJETO

289

TCPConnection delega todas as solicitaes especficas de estado para sua instncia _state de TCPState. O TCPConnection tambm fornece uma operao para mudar essa varivel para um novo TCPState. O constructor de TCPConnection inicia o objeto no estado TCPClosed (definido mais tarde).

TCPState implementa o comportamento-padro para todas as solicitaes delegadas a ela. Ela pode tambm mudar o estado de uma TCPConnection com a operao ChangeState. TCPState declarado como um friend de TCPConnection para ter acesso privilegiado a essa operao.

290

CAPTULO 5 PADRES

COMPORTAMENTAIS

As subclasses de TCPState implementam o comportamento especfico de estado. Uma conexo TCP pode estar em muitos estados: Established, Listening, Closed, etc., e existe uma subclasse de TCPState para cada estado. Discutiremos trs subclasses em detalhe: TCPEstablished, TCPListen e TCPClosed.

As subclasses de TCPState no mantm estados locais, de modo que possam ser compartilhadas e apenas uma instncia de cada seja requerida. A nica instncia de cada subclasse TCPState obtida pela operao esttica Instance9. Cada subclasse TCPState implementa um comportamento especfico do estado para cada solicitao vlida no estado:

PADRES DE PROJETO

291

envia SYN, recebe SYN, ACK, etc.

envia FIN, recebe ACK de FIN

envia SYN, recebe SYN, ACK, etc.

Aps executar o trabalho especfico do estado, essas operaes chamam a operao ChangeState para mudar o estado de TCPConnection. O TCPConnection em si no sabe nada sobre o protocolo de conexo TCP; so as subclasses TCPState que definem cada transio de estado e ao em TCP.

Usos conhecidos
Johnson e Zweig [JZ91] caracterizam o padro State e sua aplicao aos protocolos de conexo TCP. A maioria dos programas populares para fazer desenho fornece ferramentas para executar operaes por manipulao direta. Por exemplo, uma ferramenta de desenho de linhas permite ao usurio clicar e arrastar para criar uma nova linha. Uma ferramenta de seleo permite ao usurio selecionar formas. Existe usualmente uma pallete de tais ferramentas para serem escolhidas. O usurio v essa atividade como escolher uma ferramenta e utiliz-la, mas na realidade o comportamento do editor muda com a ferramenta corrente: quando uma ferramenta de desenho est ativa, criamos formas; quando a ferramenta de seleo est ativa, ns selecionamos formas; e assim por diante. Podemos usar o padro State para mudar o comportamento do editor dependendo da ferramenta corrente. Podemos definir uma classe abstrata Tool, a partir da qual definiremos subclasses que implementaro comportamentos especficos de ferramentas. O editor de desenho mantm um objeto Tool corrente e delega solicitaes para ele. Ele substitui esse objeto quando o usurio escolhe uma nova ferramenta, fazendo com que o comportamento do editor de desenhos mude de acordo. Essa tcnica usada nos frameworks para editores de desenho HotDraw [Joh92] e Unidraw [VL90]. Ela permite aos clientes definir facilmente novos tipos de ferramentas. No HotDraw, a classe DrawingController

292

CAPTULO 5 PADRES

COMPORTAMENTAIS

repassa as solicitaes para o objeto Tool corrente. O seguinte diagrama de classe esboa as interfaces de Tool e DrawingController:

O termo Envelope-Letter (tcnica, estilo) apresentado por Coplien [Cop92] est relacionado com State. Envelope-Letter uma tcnica para mudar a classe de um objeto em tempo de execuo. O padro State mais especfico, focalizando sobre como lidar com um objeto cujo comportamento depende do seu estado.

Padres relacionados
O padro Flyweight (187) explica quando e como objetos State podem ser compartilhados. Objetos State so freqentemente Singletons (130).

STRATEGY
Inteno

comportamental de objetos

Definir uma famlia de algoritmos, encapsular cada uma delas e torn-las intercambiveis. Strategy permite que o algoritmo varie independentemente dos clientes que o utilizam.

Tambm conhecido como


Policy

Motivao
Existem muitos algoritmos para quebrar um stream de texto em linhas. Codificar de maneira fixa e rgida tais algoritmos nas classes que os utilizam no desejvel, por vrias razes: clientes que necessitam de quebras de linhas se tornam mais complexos se incluirem o cdigo de quebra de linhas. Isso os torna maiores e mais difceis de manter, especialmente se suportam mltiplos algoritmos de quebra de linhas;

PADRES DE PROJETO

293

diferentes algoritmos sero apropriados em diferentes situaes. No queremos suportar mltiplos algoritmos de quebra de linhas se no usarmos todos eles; difcil adicionar novos algoritmos e variar os existentes quando a quebra de linha parte integrante de um cliente. Podemos evitar esses problemas definindo classes que encapsulam diferentes algoritmos de quebra de linhas. Um algoritmo encapsulado dessa maneira chamado strategy (estratgia).

Suponha que uma classe Composition seja responsvel pela manuteno e atualizao das quebras de linhas de texto exibidas num visualizador de texto. As estratgias de quebra de linhas no so implementadas pela classe Composition. Em vez disso, so implementadas separadamente por subclasses da classe abstrata Compositor. Subclasses de Compositor implementam diferentes estratgias: SimpleCompositor: Implementa uma estratgia simples que determina quebras de linha, uma por vez. TeXCompositor: Implementa o algoritmo TEX para encontrar quebras de linhas. Esta estratgia tenta otimizar globalmente as quebras de linhas, ou seja, um pargrafo por vez. ArrayCompositor: Implementa uma estratgia que seleciona quebras de maneira que cada linha tenha um nmero fixo de itens. Por exemplo, til para quebrar uma coleo de cones em linhas. Uma Composition mantm uma referncia para um objeto Compositor. Sempre que uma Composition reformata seu texto, repassa essa responsabilidade para o seu objeto Compositor. O cliente de Composition especifica qual Compositor deveria ser usado pela instalao do Compositor que ele deseja em Composition.

Aplicabilidade
Use o padro Strategy quando: muitas classes relacionadas diferem somente no seu comportamento. As estratgias fornecem uma maneira de configurar uma classe com um dentre muitos comportamentos; voc necessita de variantes de um algoritmo. Por exemplo, pode definir algoritmos que refletem diferentes solues de compromisso entre espao/ tempo. As estratgias podem ser usadas quando essas variantes so implementadas como uma hierarquia de classes de algoritmos [HO87];

294

CAPTULO 5 PADRES

COMPORTAMENTAIS

um algoritmo usa dados dos quais os clientes no deveriam ter conhecimento. Use o padro Strategy para evitar a exposio das estruturas de dados complexas, especficas do algoritmo; uma classe define muitos comportamentos, e estes aparecem em suas operaes como mltiplos comandos condicionais da linguagem. Em vez de usar muitos comandos condicionais, mova os ramos condicionais relacionados para a sua prpria classe Strategy.

Estrutura

Participantes
Strategy (Compositor) define uma interface comum para todos os algoritmos suportados. Context usa esta interface para chamar o algoritmo definido por uma ConcreteStrategy. ConcreteStrategy (SimpleCompositor, TeXCompositor, ArrayCompositor) implementa o algoritmo usando a interface de Strategy. Context (Composition) configurado com um objeto ConcreteStrategy; mantm uma referncia para um objeto Strategy; pode definir uma interface que permite a Strategy acessar seus dados.

Colaboraes
Strategy e Context interagem para implementar o algoritmo escolhido. Um contexto pode passar todos os dados requeridos pelo algoritmo para a estratgia quando o algoritmo chamado. Alternativamente, o contexto pode passar a si prprio como argumento para operaes de Strategy. Isto permite estratgia chamar de volta o contexto conforme requerido. Um contexto repassa solicitaes dos seus clientes para sua estratgia. Os clientes usualmente criam e passam um objeto ConcreteStrategy para o contexto; aps isso, interagem exclusivamente com o contexto. Freqentemente existe uma famlia de classes ConcreteStrategy para um cliente fazer sua escolha.

Conseqncias
O padro Strategy tem os seguintes benefcios e desvantagens:

PADRES DE PROJETO

295

1. Famlias de algoritmos relacionados. Hierarquias de classes Strategy definem uma famlia de algoritmos e comportamentos para os contextos reutilizarem. A herana pode ajudar a fatorar a funcionalidade comum dos algoritmos. 2. Uma alternativa ao uso de subclasses. A herana oferece uma outra maneira de suportar uma variedade de algoritmos ou comportamentos. Voc pode especializar uma classe Context para lhe dar diferentes comportamentos. Mas isso congela o comportamento em Context, misturando a implementao do algoritmo com a de Context, tornando Context mais difcil de compreender, manter e estender. E no se pode variar de algoritmo dinamicamente. Voc acaba tendo muitas classes relacionadas cuja nica diferena o algoritmo ou comportamento que elas empregam. Encapsular os algoritmos em classes Strategy separadas permite variar o algoritmo independentemente do seu contexto, tornando mais fcil troc-los, compreend-los e estend-los. 3. Estratgias eliminam comandos condicionais da linguagem de programao. O padro Strategy oferece uma alternativa ao uso de comandos condicionais para a seleo de comportamentos desejados. Quando diferentes comportamentos so agrupados em uma classe difcil evitar o uso de comandos condicionais para a seleo do comportamento correto. O encapsulamento do comportamento em classes Strategy separadas elimina estes comandos condicionais. Por exemplo, sem usar estratgias, o cdigo para quebrar o texto em linhas se pareceria com

junta os resultados com a composio existente, se necessrio

O padro Strategy elimina este comando case pela delegao da tarefa de quebra de linhas para um objeto Strategy:

junta os resultados com a composio existente, se necessrio

Um cdigo que contm muitos estados freqentemente indica a necessidade de aplicar o padro Strategy. 4. A possibilidade de escolha de implementaes. As estratgias podem fornecer diferentes implementaes do mesmo comportamento. O cliente pode escolher entre estratgias com diferentes compromissos entre tempo e espao. 5. Os clientes devem conhecer diferentes Strategies. O padro tem uma deficincia potencial no fato de que um cliente deve compreender como Strategies

296

CAPTULO 5 PADRES

COMPORTAMENTAIS

diferem, antes que ele possa selecionar o mais apropriado. Os clientes podem ser expostos a detalhes e aspectos de implementao. Portanto, voc deveria usar o padro Strategy somente quando a variao em comportamento relevante para os clientes. 6. Custo de comunicao entre Strategy e Context. A interface de Strategy compartilhada por todas as classes ConcreteStrategy, quer os algoritmos que elas implementem sejam triviais ou complexos. Da ser provvel que alguns ConcreteStrategy no usem toda a informao passada atravs desta interface; ConcreteStrategies simples podem no usar quaisquer dessas informaes! Isso significa que existiro situaes em que o contexto criar e iniciar parmetros que nunca sero usados. Se esse for um problema, voc necessitar de um acoplamento maior entre Strategy e Context. 7. Aumento do nmero de objetos. Strategies aumentam o nmero de objetos numa aplicao. Algumas vezes, voc pode reduzir esse custo pela implementao de estratgias como objetos sem estados que os contextos possam compartilhar. Qualquer estado residual mantido pelo contexto, que o passa em cada solicitao para o objeto Strategy.

Implementao
Considere os seguintes aspectos de implementao: 1. Definindo as interfaces de Strategy e Context. As interfaces de Strategy e Context podem fornecer a uma ConcreteStrategy um acesso eficiente a quaisquer dados que necessite de um contexto, e vice-versa. Uma soluo fazer com que Context passe dados atravs de parmetros para as operaes de Strategy em outras palavras, levar os dados para a estratgia. Isso mantm Strategy e Context desacoplados. Por outro lado, Context pode passar dados de que Strategy no necessita. Uma outra tcnica fazer um contexto passar a si prprio como um argumento, e ento a estratgia solicitar dados do contexto explicitamente. Alternativamente, a estratgia pode armazenar uma referncia para o seu contexto, eliminando de todo a necessidade de passar qualquer coisa. De ambas as maneiras, a estratgia pode solicitar exatamente o que ela necessita. Porm, agora, Context deve definir uma interface mais elaborada para os seus dados, o que acopla Strategy e Context mais fortemente. As necessidades de um algoritmo especfico e seus requisitos de dados determinaro qual a melhor tcnica. 2. Estratgias como parmetros template. Em C++, templates podem ser usados para configurar uma classe com uma estratgia. Esta tcnica somente aplicvel se: (1) Strategy pode ser selecionada em tempo de compilao e (2) ela no tem que ser mudada em tempo de execuo. Nesse caso, a classe a ser configurada (por exemplo, Context) definida como uma classe template que tem como parmetro uma classe Strategy:

PADRES DE PROJETO

297

A classe ento configurada com uma classe Strategy quando instanciada:

Com templates, no h necessidade de definir uma classe abstrata que defina a interface para Strategy. Usar Strategy como um parmetro de template tambm permite vincular uma Strategy ao seu Context estaticamente, o que pode melhorar a eficincia. 3. Tornando os objetos Strategy opcionais. A classe Context pode ser simplificada se fizer sentido no ter um objeto Strategy. Context verifica se ele tem o objeto Strategy antes de acess-lo. Se existir um, ento Context o utiliza normalmente. Se no houver uma estratgia, ento Context executa o comportamentopadro. O benefcio dessa soluo que os clientes no tm que lidar com nenhum objeto Strategy a menos que eles no queiram o comportamento-padro.

Exemplo de cdigo
Daremos o cdigo de alto nvel para o exemplo da seo Motivao, o qual est baseado na implementao das classes Composition e Compositor em InterViews [LCI+92]. A classe Composition mantm uma coleo de Component, a qual representa texto e elementos grficos num documento. Uma composio arruma os objetos componentes em linhas usando uma instncia da subclasse Compositor, a qual encapsula uma estratgia de quebra de linhas. Cada componente tem associados um tamanho natural, uma extensibilidade e uma compressibilidade. A extensibilidade define quanto o componente pode crescer alm do seu tamanho natural; compressibilidade quanto ele pode ser comprimido. A composio passa esses valores para um compositor, o qual os utiliza para determinar a melhor localizao para quebras de linha.

a lista de componentes o nmero de componentes largura da linha posio das quebras das linhas em componentes nmero de linhas

Quando um novo layout requerido, a composio solicita ao seu compositor determinar onde colocar as quebras de linha. A composio passa para o compositor trs vetores que definem tamanhos naturais, extensibilidades e compressibilidades dos componentes. Ela tambm passa o nmero de componentes, a largura da linha e

298

CAPTULO 5 PADRES

COMPORTAMENTAIS

um vetor que o compositor preenche com a posio de cada quebra de linha. O compositor devolve o nmero de quebras calculadas. A interface de Compositor permite composio passar ao compositor toda a informao de que ele necessita. Isso um exemplo da abordagem do tipo levando os dados para a estratgia:

Note que Compositor uma classe abstrata. As subclasses concretas definem estratgias especficas de quebras de linha. A composio chama a operao Repair do seu compositor. Repair primeiramente inicia vetores com tamanho, extensibilidade e compressibilidade naturais de cada componente (cujos detalhes omitiremos). Ento, ela chama o compositor para obter as quebras de linha e finalmente estabelece o layout dos componentes de acordo com as quebras (tambm omitido):

prepara os arrays com os tamanhos desejados dos componentes

determina onde esto as quebras:

dispe os componentes de acordo com as quebras

Examinemos agora as subclasses de Compositor. A classe SimpleCompositor examina componentes uma linha por vez para determinar onde as quebras deveriam ser colocadas:

PADRES DE PROJETO

299

O TeXCompositor utiliza uma estratgia mais global. Ele examina um pargrafo por vez, levando em conta o tamanho dos componentes e sua extensibilidade. Ele tambm tenta dar um aspecto uniforme ao pargrafo atravs da minimizao dos espaos em branco entre componentes.

ArrayCompositor quebra os componentes em linhas a intervalos regulares.

Essas classes no utilizam toda a informao passada em Compose. O SimpleCompositor ignora a extensibilidade dos componentes, levando em conta somente suas larguras naturais. O TeXCompositor utiliza toda a informao passada para ela, enquanto que ArrayCompositor ignora todas. Para instanciar Composition, voc passa a ela o compositor que deseja usar:

A interface de Compositor cuidadosamente projetada para suportar todos os algoritmos de layout que as subclasses possam implementar. Voc no deseja ter que mudar esta interface a cada nova subclasse introduzida porque isso exigir mudar subclasses existentes. Em geral, as interfaces de Strategy e Context determinam tambm qual padro consegue o seu intento.

Usos conhecidos
Tanto ET++ [WGM88] como InterViews usam estratgias para encapsular diferentes algoritmos de quebras de linhas na forma como descrevemos. No sistema RTL destinado otimizao de cdigo gerado por compiladores [JML92], estratgias definem diferentes esquemas de alocao de registradores (RegisterAllocator) e procedimentos de utilizao (scheduling) do conjunto de instrues (RISCscheduler, CISCscheduler). Isso fornece flexibilidade no direcionamento do otimizador para diferentes arquiteturas de mquina.

300

CAPTULO 5 PADRES

COMPORTAMENTAIS

O framework para calculadoras (calculation engine) do SwapsManager de ET++ computa preos para diferentes instrumentos financeiros [EG92]. Suas abstraeschave so Instrument e YieldCurve (instrumento e curva de rendimentos, respectivamente). Diferentes instrumentos so implementados como subclasses de Instrument. YieldCurve calcula coeficientes de desconto que determinam o valor presente de fluxos de caixa futuros. Ambas as classes delegam algum comportamento para objetos Strategy. O framework fornece uma famlia de classes ConcreteStrategy para gerar fluxos de caixa, avaliar permutas (swaps) e calcular coeficientes de desconto. Voc pode criar novas calculadoras atravs da configurao de Instrument e YieldCurve com diferentes objetos ConcreteStrategy. Esta abordagem suporta a combinao e casamento de implementaes existentes de Strategy, bem como a definio de novas implementaes. Os componentes de Booch usam estratgias como argumentostemplate. As classes de coleo de Booch suportam trs tipos diferentes de estratgias de alocao de memria: administrada (alocao dentro de um pool), controlada (alocaes/ desalocaes so protegidas por travamentos (locks), e no-administradas (o alocador de memria normal). Essas estratgias so passadas como argumentostemplate para uma classe de coleo quando ela instanciada. Por exemplo, uma UnboundedCollection que usa a estratgia no-administrada instanciada como UnboundedCollection<MyItemType*, Unnanaged>. RApp um sistema para o layout de circuitos integrados [GA89,AG90]. RApp deve estabelecer o layout e as rotas dos condutores que conectam subsistemas do circuito. Algoritmos para determinao de rotas no RApp so definidos como subclasses de uma classe abstrata Router. A Router uma classe Strategy. ObjectWindows da Borland [Bor94] utiliza estratgias em caixas de dilogo para assegurar que os usurios forneam dados vlidos. Por exemplo, os nmeros devem estar em um certo intervalo, e um campo de entrada numrica deve aceitar somente dgitos. Validar que uma string est correta pode exigir uma pesquisa numa tabela ObjectWindows utiliza objetos Validator para encapsular estratgias de validao. Validators so exemplos de objetos Strategy. Campos de entrada de dados delegam a estratgia de validao para um objeto Validator opcional. O cliente associa um validator a um campo, se for necessria uma validao (isto , um exemplo de uma estratgia opcional). Quando o dilogo fechado, os campos de entrada solicitam aos seus validators para validarem os dados. A biblioteca de classes fornece validators para casos comuns, tal como um RangeValidator (um validator de intervalo) para nmeros. Novas estratgias de validao, especficas do cliente, podem ser definidas facilmente, criando subclasses da classe Validator.

Padro relacionados
Flyweight (187): objetos Strategy geralmente so bons flyweights.

PADRES DE PROJETO

301

TEMPLATE METHOD
Inteno

comportamental de classes

Definir o esqueleto de um algoritmo em uma operao, postergando alguns passos para as subclasses. Template Method permite que subclasses redefinam certos passos de um algoritmo sem mudar a estrutura do mesmo.

Motivao
Considere um framework para aplicaes que fornece as classes Application e Document. A classe Application responsvel por abrir documentos existentes armazenados num formato externo, tal como um arquivo. Um objeto Document representa a informao num documento, depois que ela foi lida do arquivo. As aplicaes construdas com o framework podem criar subclasses de Application e Document para atender necessidades especficas. Por exemplo, uma aplicao de desenho define as subclasses DrawApplication e DrawDocument; uma aplicao de planilha define as subclasses SpreadsheetApplication e SpreadsheetDocument.

A classe abstrata Application define o algoritmo para abrir e ler um documento na sua operao OpenDocument:

no consegue tratar este documento

302

CAPTULO 5 PADRES

COMPORTAMENTAIS

OpenDocument define cada passo para a abertura de um documento. Ela verifica se o documento pode ser aberto, cria o objeto Document especfico para a aplicao, acrescenta-o ao seu conjunto de documentos e l Document de um arquivo. Chamamos OpenDocument um template method. Um mtodo-template (gabarito, C++ suporta templates) define um algoritmo em termos da operao abstrata que as subclasses redefinem para fornecer um comportamento concreto. As subclasses da aplicao definem os passos do algoritmo que verifica se o documento pode ser aberto (CanOpenDocument) e cria o Document (DoCreateDocument). As classes Document definem a etapa que l o documento (DoRead). O mtodo template tambm define uma operao que permite s subclasses de Application saberem quando o documento est para ser aberto (AboutToOpenDocument), no caso de elas terem essa preocupao. Pela definio de alguns dos passos de um algoritmo usando operaes abstratas, o mtodo template fixa a sua ordem, mas deixa as subclasses de Application e Document variarem aqueles passos necessrios para atender suas necessidades.

Aplicabilidade
O padro Template Method pode ser usado: para implementar as partes invariantes de um algoritmo uma s vez e deixar para as subclasses a implementao do comportamento que pode variar. quando o comportamento comum entre subclasses deve ser fatorado e concentrado numa classe comum para evitar a duplicao de cdigo. Este um bom exemplo de refatorar para generalizar, conforme descrito por Opdyke e Johnson [OJ93]. Primeiramente, voc identifica as diferenas no cdigo existente e ento separa as diferenas em novas operaes. Por fim, voc substitui o cdigo que apresentava as diferenas por um mtodo-template que chama uma dessas novas operaes. para controlar extenses de subclasses. Voc pode definir um mtodo-template que chama operaes gancho (ver Conseqncias) em pontos especficos, desta forma permitindo extenses somente nesses pontos.

Estrutura

Participantes
AbstractClass (Application)

PADRES DE PROJETO

303

define operaes primitivas abstratas que as subclasses concretas definem para implementar passos de um algoritmo. implementa um mtodo-template que define o esqueleto de um algoritmo. O mtodo-template invoca operaes primitivas, bem como operaes definidas em AbstractClass ou ainda outros objetos. ConcreteClass (MyApplication) implementa as operaes primitivas para executarem os passos especficos do algoritmo da subclasse.

Colaboraes
ConcreteClass depende de AbstractClass para implementar os passos invariantes do algoritmo.

Conseqncias
Os mtodos-template so uma tcnica fundamental para a reutilizao de cdigo. So particularmente importantes em bibliotecas de classe porque so os meios para a fatorao dos comportamentos comuns. Os mtodos-template conduzem a uma estrutura de inverso de controle, algumas vezes chamada de o princpio de Hollywood, ou seja: no nos chame, ns chamaremos voc [Swe85]. Isso se refere a como uma classe-me chama as operaes de uma subclasse, e no o contrrio. Os mtodos-template chamam os seguintes tipos de operaes: operaes concretas (ou em ConcreteClass ou em classes-clientes); operaes concretas de AbstractClass (isto , operaes que so teis para subclasses em geral); operaes primitivas (isto , operaes abstratas); mtodos fbrica (ver Factory Method, 112); e operaes-gancho (hook operations) que fornecem comportamento-padro que subclasses podem estender se necessrio. Uma operao-gancho freqentemente no executa nada por padro. importante para os mtodos-template especificar quais operaes so ganchos (podem ser redefinidas) e quais so operaes abstratas (devem ser redefinidas). Para reutilizar uma classe abstrata efetivamente, os codificadores de subclasses devem compreender quais as operaes so projetadas para redefinio. Uma subclasse pode estender o comportamento de uma operao de uma classeme pela redefinio da operao e chamando a operao-me explicitamente:

Infelizmente, fcil esquecer de chamar a operao herdada. Podemos transformar uma tal operao num mtodo-template para dar (classe) me controle sobre a maneira como as subclasses a estendem. A idia chamar uma operao-gancho a partir de um mtodo-template na classe-me. Ento, as subclasses podem substituir essa operao-gancho:

304

CAPTULO 5 PADRES

COMPORTAMENTAIS

Um HookOperation no faz nada em ParentClass:

As subclasses substituem HookOperation para estender o seu comportamento:

Implementao
Trs aspectos da implementao so dignos de nota: 1. Usando o controle de acesso de C++. Em C++, as operaes primitivas que um mtodo template chama podem ser declaradas membros protegidos. Isso assegura que elas so chamadas somente pelo mtodo-template. As operaes primitivas que devem ser substitudas so declaradas como virtuais puras. O mtodo-template em si no deveria ser substitudo; portanto, voc pode tornar o mtodo-template uma funo-membro no-virtual. 2. Minimizando operaes primitivas. Um objetivo importante ao projetar mtodos templates minimizar o nmero de operaes primitivas que uma subclasse deve substituir para materializar o algoritmo. Quanto mais operaes necessitam ser substitudas, mais tediosas se tornam as coisas para os clientes. 3. Convenes de nomenclatura. Voc pode identificar as operaes que deveriam ser substitudas pela adio de um prefixo aos seus nomes. Por exemplo, o framework MacApp para aplicaes Macintosh [App89] prefixa os nomes de mtodos template com Do : DoCreateDocument, DoRead, e assim por diante.

Exemplo de cdigo
O seguinte exemplo em C++ mostra como uma classe-me pode impor e garantir um invariante para suas subclasses. O exemplo tirado do AppKit do NeXT [Add94]. Considere uma classe View que suporta a ao de desenhar na tela. View garante a invariante que as suas subclasses podem desenhar em uma viso somente aps ela se tornar o foco, o que requer que um certo estado de desenho seja estabelecido de forma apropriada (por exemplo, cores e fontes tipogrficas). Podemos usar um mtodo-template Display para estabelecer este estado. View define duas operaes concretas, SetFocus e ResetFocus, que estabelecem e fazem a limpeza do estado de desenho, respectivamente. A operao-gancho DoDisplay de View executa o desenho propriamente dito. Display chama SetFocus antes de DoDisplay para iniciar o estado de desenho; Display chama ResetFocus posteriormente para sair do estado de desenho.

PADRES DE PROJETO

305

Para manter o invariante, os clientes de View sempre chamam Display e subclasses de View redefinem DoDisplay. DoDisplay no faz nada em View:

As subclasses a redefinem para acrescentar o seu comportamento de desenho especfico:


finaliza os contedos da viso

Usos conhecidos
Os mtodos-template so to fundamentais que eles podem ser encontrados em quase todas as classes abstratas. Wirfs-Brock e outros [WBWW90,WBJ90] fornecem uma boa viso geral e uma boa discusso de mtodos-template.

Padres relacionados
Os Factory Methods (112) so freqentemente chamados por mtodos-template. No exemplo da seo Motivao, o mtodo-fbrica DoCreateDocument chamado pelo mtodo-template OpenDocument. Strategy (292): Mtodos-template usam a herana para variar parte de um algoritmo. Estratgias usam a delegao para variar o algoritmo inteiro.

VISITOR
Inteno

comportamental de objetos

Representar uma operao a ser executada nos elementos de uma estrutura de objetos. Visitor permite definir uma nova operao sem mudar as classes dos elementos sobre os quais opera.

Motivao
Considere um compilador que representa programas como rvores sintticas abstratas. Ele necessitar executar operaes nas rvores sintticas abstratas para anlises semnticas estticas, como verificar se todas as variveis esto definidas. Tambm necessitar gerar cdigo executvel. Assim, poder definir operaes para verifica-

306

CAPTULO 5 PADRES

COMPORTAMENTAIS

o de tipos, otimizao de cdigo, anlise de fluxo, verificao se as variveis receberam valores antes de serem usadas, e assim por diante. Alm do mais, poderamos usar as rvores sintticas abstratas para pretty-printing, reestruturao de programas, instrumentao de cdigo e computao de vrias mtricas de um programa. A maioria dessas operaes necessitar tratar ns que representam comandos de atribuio de forma diferente de ns que representam variveis ou expresses aritmticas. Portanto, existir uma classe para comandos de atribuio, outra para acessos a variveis, uma outra para expresses aritmticas, e assim por diante. Naturalmente, o conjunto das classes-n depende da linguagem que est sendo compilada. Porm, ele no muda muito para uma determinada linguagem.

Este diagrama mostra parte da hierarquia da classe Node (N). O problema aqui que distribuir todas essas operaes pelas vrias classes-n conduz a um sistema difcil de compreender, manter e mudar. Ser confuso ter o cdigo para verificao de tipos misturado com o cdigo para pretty-printing ou para anlise de fluxo. Alm do mais, acrescentar uma nova operao geralmente exige a recompilao de todas essas classes. Seria melhor se cada nova operao pudesse ser acrescentada separadamente, e as classes de ns fossem independentes das operaes que se aplicam a elas. Podemos ter as duas coisas empacotando as operaes relacionadas de cada classe num objeto separado, chamado um visitor (visitante), e passando para ele elementos da rvore sinttica abstrata medida que a mesma percorrida. Quando um elemento aceita o visitante, envia uma solicitao para o visitante que codifica a classe do elemento. Ela tambm inclui o elemento como argumento. O visitante ento executar a operao para aquele elemento a operao que costumava estar na classe do elemento. Por exemplo, um compilador que no usou visitantes pode fazer a verificao de tipos de um procedimento (procedure) chamando a operao TypeCheck na sua rvore sinttica abstrata. Cada um dos ns implementaria TypeCheck chamando TypeCheck nos seus componentes (ver o diagrama de classe precedente). Se o compilador fizesse a verificao de tipos de um procedimento utilizando visitantes, ento ele criaria um objeto TypeCheckingVisitor e chamaria a operao Accept na rvore sinttica abstrata com esse objeto como um argumento. Cada um dos ns implementaria Accept chamando de volta o visitante: um n de atribuio chamaria a operao VisitAssignment no visitante, enquanto que uma referncia a uma varivel chamaria VisitVariableReferen-

PADRES DE PROJETO

307

ce. O que era a operao TypeCheck na classe AssignmentNode se torna agora a operao VisitAssignment em TypeCheckingVisitor. Para fazer com que visitantes faam mais do que somente verificao de tipo, necessitamos de uma classe-me abstrata NodeVisitor para todos os visitantes de uma rvore sinttica abstrata. NodeVisitor deve declarar uma operao para cada classe de n. Uma aplicao que necessita computar mtricas de programas definir novas subclasses de NodeVisitor e no mais necessitar adicionar cdigo especfico aplicao s classes de ns. O padro Visitor encapsula as operaes para cada fase da compilao em uma classe Visitor associada com aquela fase.

Com o padro Visitor, voc define duas hierarquias de classes: uma para os elementos sobre os quais esto sendo aplicadas as operaes (a hierarquia Node) e uma para os visitantes que definem as operaes sobre os elementos (a hierarquia NodeVisitor). Voc cria uma nova operao acrescentando uma nova subclasse hierarquia da classe visitante. Na medida em que a gramtica que o compilador reconhece no muda (isto , ns no temos que adicionar novas subclasses de Node), podemos adicionar novas funcionalidades simplesmente definindo novas subclasses de NodeVisitor.

Aplicabilidade
Use o padro Visitor quando: uma estrutura de objetos contm muitas classes de objetos com interfaces que diferem e voc deseja executar operaes sobre esses objetos que dependem das suas classes concretas; muitas operaes distintas e no-relacionadas necessitam ser executadas sobre objetos de uma estrutura de objetos, e voc deseja evitar a poluio das suas

308

CAPTULO 5 PADRES

COMPORTAMENTAIS

classes com essas operaes. Visitor lhe permite manter juntas operaes relacionadas, definindo-as em uma nica classe. Quando a estrutura do objeto for compartilhada por muitas aplicaes, use Visitor para por operaes somente naquelas aplicaes que as necessitam; as classes que definem a estrutura do objeto raramente mudam, porm, voc freqentemente deseja definir novas operaes sobre a estrutura. A mudana das classes da estrutura do objeto requer a redefinio da interface para todos os visitantes, o que potencialmente oneroso. Se as classes da estrutura do objeto mudam com freqncia, provavelmente melhor definir as operaes nessas classes.

Estrutura
Client

Participantes
Visitor (NodeVisitor) declara uma operao Visit para cada classe ConcreteElement na estrutura do objeto. O nome e a assinatura da operao identifica a classe que envia a solicitao Visit ao visitante. Isso permite ao visitante determinar a classe concreta do elemento que est sendo visitado. Ento, o visitante pode acessar o elemento diretamente atravs da sua interface especfica. ConcreteVisitor (TypeCheckingVisitor) implementa cada operao declarada por Visitor. Cada operao implementa um fragmento do algoritmo definido para a correspondente classe de objeto na estrutura. ConcreteVisitor fornece o contexto para o algoritmo e armazena

PADRES DE PROJETO

309

o seu estado local. Esse estado freqentemente acumula resultados durante o percurso da estrutura. Element (Node) define uma operao Accept que aceita um visitante como um argumento. ConcreteElement (AssignmentNode, VariableRefNode) implementa uma operao Accept que aceita um visitante como um argumento. ObjectStructure (Program) pode enumerar seus elementos; pode fornecer uma interface de alto nvel para permitir ao visitante visitar seus elementos; pode ser ou uma composio (ver Composite, 160), ou uma coleo, tal como uma lista ou um conjunto.

Colaboraes
Um cliente que usa o padro Visitor deve criar um objeto ConcreteVisitor e ento percorrer a estrutura do objeto, visitando cada elemento com esse Visitor. Quando um elemento visitado, chama a operao de Visitor que corresponde sua classe. O elemento fornece a si mesmo como argumento para essa operao, para permitir ao visitante acessar seu estado, se necessrio. O seguinte diagrama de interao ilustra as colaboraes entre uma estrutura de objeto, um visitante, e dois elementos:

Conseqncias
A seguir, apresentamos alguns dos benefcios e deficincias do padro Visitor: 1. Visitor torna fcil a adio de novas operaes. Os Visitors tornam fcil acrescentar operaes que dependem dos componentes de objetos complexos. Voc pode definir uma nova operao sobre uma estrutura de objetos simplesmente acrescentando um novo visitante. Em contraste com isso, se voc espalha funcionalidade sobre muitas classes, ento tem que mudar cada classe para definir uma nova operao. 2. Um visitante rene operaes relacionadas e separa as operaes no-relacionadas. O comportamento relacionado no espalhado pelas classes que definem a estrutura do objeto; ele est localizado em um visitante. Conjuntos de comportamentos no-relacionados so particionados em suas prprias

310

CAPTULO 5 PADRES

COMPORTAMENTAIS

subclasses visitantes. Isso simplifica tanto as classes que definem os elementos como os algoritmos definidos nos visitantes. Qualquer estrutura de dados especfica de um algoritmo pode ser ocultada no visitante. 3. difcil acrescentar novas classes ConcreteElement. O padro Visitor torna difcil acrescentar novas subclasses de Element. Cada novo ConcreteElement d origem a uma nova operao abstrata em Visitor e uma correspondente implementao em cada classe ConcreteVisitor. Algumas vezes, uma implementao-padro pode ser fornecida em Visitor, a qual pode ser herdada pela maioria dos ConcreteVisitors, mas isso exceo, no a regra. Assim, a considerao-chave na aplicao do padro Visitor : qual a mudana mais provvel: a do algoritmo aplicado sobre uma estrutura de objetos ou a das classes de objetos que compem a estrutura? A hierarquia de classe de Visitor pode ser difcil de ser mantida quando novas classes ConcreteElement so acrescentadas com freqncia. Em tais casos, provavelmente ser mais fcil simplesmente definir operaes nas classes que compem a estrutura. Se a estrutura de classe de Element for estvel, mas voc estiver continuamente adicionando operaes ou mudando algoritmos, o padro Visitor vai ajudar a controlar as mudanas. 4. Visitando hierarquias de classes. Um iterador (ver Iterator, 244) pode visitar os objetos numa estrutura medida que os percorre pela chamada das suas operaes. Mas um iterador no pode agir sobre estruturas de objetos com distintos tipos de elementos. Por exemplo, a interface de Iterator definida na pgina 249 pode acessar somente objetos do tipo Item: Isso implica em que todos os elementos que o iterador pode visitar tenham uma classe-me Item em comum.

O Visitor no tem essa restrio. Ele pode visitar objetos que no compartilham uma mesma classe-me. Voc pode acrescentar qualquer tipo de objeto interface de um Visitor. Por exemplo, em MyType e YourType no precisam ser relacionados de forma alguma atravs da herana.

5. Acumulando estados. Os Visitors podem acumular estados medida que visitam cada elemento na estrutura do objeto. Sem um visitante, estes estados seriam passados como argumentos extras para as operaes que executam o percurso, ou eles poderiam aparecer como variveis globais.

PADRES DE PROJETO

311

6. Rompendo o encapsulamento. A abordagem do padro Visitor assume que a interface de ConcreteElement poderosa o suficiente para permitir aos visitantes executarem o seu trabalho. Como resultado, o padro freqentemente fora a fornecer operaes pblicas que acessam o estado interno de um elemento, o que pode comprometer seu encapsulamento.

Implementao
Cada estrutura de objeto ter uma classe Visitor associada. Essa classe abstrata visitante declara uma operao VisitConcreteElement para cada classe de ConcreteElement que define a estrutura do objeto. Cada operao Visit no Visitor declara seu argumento como um particular ConcreteElement, permitindo ao Visitor acessar diretamente a interface do ConcreteElement. Classes ConcreteVisitor substituem cada operao Visit para implementar um comportamento especfico do visitante para a correspondente classe ConcreteElement. A classe Visitor seria declarada dessa maneira em C++:

e assim por diante para outros elementos concretos

Cada classe de ConcreteElement implementa uma operao Accept que chama a correspondente operao Visit... no visitante para aquele ConcreteElement. Assim, a operao que acaba sendo chamada depende tanto da classe do elemento como da classe do visitante10. Os elementos concretos so declarados como

Uma classe CompositeElement pode implementar Accept desta forma:

312

CAPTULO 5 PADRES

COMPORTAMENTAIS

A seguir, esto dois outros aspectos de implementao que surgem quando voc aplica o padro Visitor: 1. Double dispatch (despacho duplo). Efetivamente, o padro Visitor permite acrescentar operaes a classes sem modific-las. Visitor consegue isto usando uma tcnica chamada double-dispatch. uma tcnica bem conhecida. De fato, algumas linguagens a suportam diretamente (por exemplo, CLOS). Linguagens como C++ e Smalltalk suportam single-dispatch (despacho simples). Em linguagens com single-dispatch, dois critrios determinam qual operao atender uma solicitao: o nome da solicitao e o tipo do receptor. Por exemplo, a operao que uma solicitao GenerateCode chamar depende do tipo de objeto-n que voc pede. Em C++, chamar GenerateCode numa instncia de VariableRefNode chamar VariableRefNode:: GenerateCode (a qual gerar cdigo executvel para uma referncia a uma varivel). Chamar GenerateCode em um AssignmentNode chamar AssignmentNode:: GenerateCode (a qual gerar cdigo executvel para uma atribuio). A operao que executada depende tanto do tipo da solicitao como do tipo do receptor. Double-dispatch simplesmente significa que a operao que executada depende do tipo de solicitao e do tipo de dois receptores. Accept uma operao de double-dispatch. O seu significado depende de dois tipos: o do Visitor e o do Element. O despacho duplo permite aos visitantes solicitarem operaes diferentes em cada classe de elemento11. Esta a chave do padro Visitor: a operao que executada depende tanto do tipo de Visitor como do tipo de Element que ele visita. Em vez de vincular as operaes estaticamente interface de Element, voc pode consolidar as operaes em um Visitor e usar Accept para fazer a vinculao em tempo de execuo. Estender a interface de Element equivale a definir uma nova subclasse de Visitor, em vez de muitas subclasses novas de Element. 2. Quem responsvel por percorrer a estrutura do objeto? Um visitante deve visitar cada elemento da estrutura do objeto. A questo : como ele chega l? Podemos colocar a responsabilidade do percurso em qualquer um de trs lugares: na estrutura do objeto, no visitante ou em um objeto iterador separado (ver Iterator, 244).

PADRES DE PROJETO

313

Freqentemente, a estrutura do objeto responsvel pela iterao. Uma coleo simplesmente iterar sobre os seus elementos, chamando a operao Accept em cada um. Uma composio (composite) comumente percorrer a si mesma, fazendo com que cada operao Accept percorra os filhos do elemento e chamando Accept em cada um deles recursivamente. Outra soluo usar um iterador para visitar os elementos. Em C++, voc poderia usar um iterador interno ou um externo, dependendo do que est disponvel e do que mais eficiente. Em Smalltalk, normalmente se usa um iterador interno usando do: e um bloco. Uma vez que iteradores internos so implementados pela estrutura do objeto, us-los bastante parecido com fazer com que a estrutura do objeto seja responsvel pela iterao. A principal diferena que um iterador interno no causar despacho duplo chamar uma operao no visitante usando um elemento como argumento, ao contrrio de chamar uma operao no elemento com o visitante como argumento. Mas fcil usar o padro Visitor com um iterador interno se a operao no visitante simplesmente chamar a operao no elemento, sem recurso. Voc poderia at mesmo colocar o algoritmo para percurso no visitante, embora termine duplicando o cdigo de percurso em cada ConcreteVisitor para cada ConcreteElement agregado. A razo principal para pr a estratgia de percurso no visitante implementar um percurso particularmente complexo, o qual depende dos resultados das operaes sobre a estrutura do objeto. Daremos um exemplo de um caso desses na seo a seguir.

Exemplo de cdigo
Porque visitantes esto normalmente associados com (objetos) compostos (composites), usaremos as classes Equipment definidas no Exemplo de cdigo do padro Composite (160) para ilustrar o padro Visitor. Usaremos Visitor para definir operaes para computar o estoque de materiais e o custo total de um determinado equipamento. As classes Equipment so to simples que o uso do Visitor no realmente necessrio, porm, elas tornam fcil de ver o que est envolvido na implementao do padro. Novamente temos aqui a classe Equipment, de Composite (160). Ns a aumentamos com uma operao Accept para permitir que ela funcione com um visitante.

314

CAPTULO 5 PADRES

COMPORTAMENTAIS

As operaes de Equipment retornam os atributos de um equipamento determinado, tais como o seu consumo de energia se seu custo. As subclasses substituem estas operaes de maneira apropriada para tipos especficos de equipamentos (por exemplo, chassis, transmisses). A classe abstrata para todos os visitantes de equipamentos tem uma funo virtual para cada subclasse de equipamento, como mostrado a seguir. Nenhuma das funes virtuais define operaes-padro.

e assim por diante para outras subclasses concretas de Equipment

As subclasses de Equipment definem Accept basicamente da mesma maneira: ela chama a operao EquipmentVisitor correspondente classe que recebeu a solicitao Accept, como a seguir:

Equipamento que contm outros equipamentos (em particular, subclasses de


CompositeEquipment no padro Composite) implementa Accept pela iterao sobre seus filhos e chamando Accept em cada um deles. Ento, como usual, chama a operao Visit. Por exemplo, Chassis:: Accept percorreria todas as partes do chassis como

segue:

As subclasses de EquipmentVisitor definem algoritmos particulares sobre a estrutura de equipamento. O PricingVisitor computa o custo da estrutura do equipamento. Ele computa o preo lquido de todos os equipamentos simples (por exemplo, dispositivo de disco flexvel) e o preo com desconto de todos os equipamentos compostos (por exemplo, chassis e nibus).

PADRES DE PROJETO

315

PricingVisitor computar o custo total de todos os ns na estrutura de equipamento. Note que PricingVisitor escolhe a poltica ou o procedimento de preos adequados para uma classe de equipamento atravs do despacho da funo-membro correspondente. E, ainda mais, podemos mudar a poltica de preos de uma estrutura de equipamento simplesmente mudando a classe PricingVisitor. Podemos definir um visitante para a computao do estoque desta maneira:

O InventoryVisitor acumula os totais para cada tipo de equipamento na estrutura do objeto. InventoryVisitor utiliza uma classe Inventory que define uma interface para acrescentar equipamentos (com cuja definio no nos preocuparemos aqui).

316

CAPTULO 5 PADRES

COMPORTAMENTAIS

Aqui, a seguir, est como podemos usar um InventoryVisitor numa estrutura de equipamentos:

Agora mostraremos como implementar o exemplo em Smalltalk do padro Interpreter (ver pgina 231) com o padro Visitor. Como no exemplo prvio, este to pequeno que Visitor provavelmente no nos ajudar em muita coisa, mas ele fornece uma boa ilustrao de como usar o padro. Alm disso, ilustra uma situao na qual a iterao responsabilidade do visitante. A estrutura do objeto (expresses regulares) compe-se de quatro classes, e todas tm um mtodo accept: que recebe o visitante como argumento. Na classe SequenceExpression, o mtodo accept:

Na classe RepeatExpression, o mtodo accept: envia a mensagem visitRepeat:. Na classe AlternationExpression, envia a mensagem visitAlternation:. Na classe LiteralExpression, ele envia a mensagem visitLiteral:. As quatro classes tambm devem ter funes de acesso que o visitante possa usar. Para SequenceExpression estas so expression1 e expression2; para AlternationExpression estas so alternative1 e alternative2; para RepeatExpression ela repetition; e para LiteralExpression estas so components. A classe ConcreteVisitor REMatchingVisitor. Ela responsvel pelo percurso porque o algoritmo de percurso irregular. A maior irregularidade que uma RepeatExpression percorrer repetidamente seu componente. A classe REMatchingVisitor tem uma varivel de instncia inputState. Seus mtodos so essencialmente os mesmos que os mtodos match: das classes expresso no exemplo do padro Interpreter, exceto que elas substituem o argumento denominado inputState com a expresso do n que est sendo casado. Contudo, elas ainda retornam o conjunto de streams que expresso teria que casar (match) para identificar o estado corrente.

PADRES DE PROJETO

317

Usos conhecidos
O compilador Smalltalk-80 tem uma classe Visitor chamada ProgramNodeEnumerator. Ela usada primariamente para algoritmos que analisam cdigo-fonte. No usada para a gerao de cdigo executvel ou pretty-printing, embora pudesse ser. IRIS Inventor [Str93] um toolkit para o desenvolvimento de aplicaes grficas 3D. Inventor representa uma cena tridimensional como uma hierarquia de ns, cada um representando um objeto geomtrico ou um atributo de um objeto geomtrico. As operaes como exprimir uma cena ou mapear um evento de entrada requerem percorrer essa hierarquia de diferentes maneiras. Inventor faz isso usando visitantes chamados actions (aes). H diferentes visitantes para rendering, tratamento de eventos, buscas, preenchimentos e determinao de caixas delimitadoras. Para tornar a adio de novos ns mais fcil, Inventor implementa um esquema de despacho duplo para C++. O esquema depende de informao sobre tipos em tempo de execuo e de uma tabela bidimensional, nas quais as linhas representam visitantes e as colunas representam classes ns. As clulas armazenam um apontador para a funo ligada ao visitante e a classe de n. Mark Linton criou o termo Visitor na especificao do Fresco Application Toolkit do X Consortium [LP93].

318

CAPTULO 5 PADRES

COMPORTAMENTAIS

Padres relacionados
Composite (160): os Visitors podem ser usados para aplicar uma operao sobre uma estrutura de objetos definida pelo padro Composite. Interpreter (231): o Visitor pode ser aplicado para efetuar a interpretao.

Discusso sobre padres comportamentais


Encapsulando variaes
O encapsulamento de variaes um tema de muitos padres comportamentais. Quando um aspecto de um programa muda freqentemente, esses padres definem um objeto que encapsula aquele aspecto. Ento, outras partes do programa podem colaborar com o objeto sempre que elas dependam daquele aspecto. Os padres normalmente definem uma classe abstrata que descreve o objeto encapsulador, e o padro deriva seu nome desse objeto12. Por exemplo: um objeto Strategy encapsula um algoritmo (Strategy, 292); um objeto State encapsula um comportamento dependente de estados (State, 284); um objeto Mediator encapsula o protocolo entre objetos (Mediator, 257); e um objeto Iterator encapsula a maneira como voc acessa e percorre os componentes de um objeto agregado (Iterator, 244). Esses padres descrevem aspectos mutveis de programas. A maioria dos padres tem dois tipos de objetos: o(s) novo(s) objeto(s) que encapsula(m) o aspecto e o(s) objeto(s) existente(s) que utilizam os novos objetos. Geralmente, a funcionalidade de objetos novos seria uma parte integrante dos objetos existentes, no fosse pelo uso do padro. Por exemplo, o cdigo para um Strategy seria provavelmente incorporado ao Context da estratgia, e o cdigo para um State seria implementado diretamente no Context do estado. Porm, nem todos os padres comportamentais de objeto particionam a funcionalidade dessa maneira. Por exemplo, Chain of Responsibility (212) lida com um nmero arbitrrio de objetos (ou seja, uma cadeia), todos os quais podem j existir no sistema. A Chain of Responsibility ilustra uma outra diferena entre os patterns comportamentais: nem todos definem relacionamentos estticos de comunicao entre classes. A Chain of Responsibility prov a comunicao entre um nmero indefinido de objetos. Outros padres envolvem objetos que so passados como argumentos.

Objetos como argumentos


Vrios padres introduzem um objeto que sempre usado como um argumento. Um desses o Visitor (305). Um objeto Visitor o argumento para uma operao polimrfica Accept nos objetos que ele visita. O visitante nunca considerado uma parte desses objetos, ainda que a alternativa convencional ao uso do padro seja a distribuio do cdigo de Visitor entre as classes de estruturas de objetos. Outros padres definem objetos que funcionam como tokens mgicos que so passados por onde necessrio e invocados em um momento posterior. Tanto Command (222) quanto Memento (266) caem nessa categoria. No Command, o token representa uma solicitao; no Memento, ele representa o estado interno de um objeto

PADRES DE PROJETO

319

num determinado instante. Em ambos os casos, o token pode ter uma representao interna complexa, mas o cliente nunca est ciente da mesma. Porm, mesmo aqui h diferenas. O polimorfismo importante no padro Command porque executar o objeto Command uma operao polimrfica. Em contraste com isso, a interface de Memento to estreita que um memento somente pode ser passado como um valor. Assim, provvel que no apresente nenhuma operao polimrfica para os seus clientes.

A comunicao deveria ser encapsulada ou distribuda?


Mediator (257) e o Observer (274) so padres que competem entre si. A diferena entre eles que o Observer distribui a comunicao atravs da introduo de objetos Observer e Subject, enquanto que um objeto Mediator encapsula a comunicao entre outros objetos. No padro Observer no existe um objeto nico que encapsula uma restrio. Em vez disso, Observer e Subject devem cooperar para manter a restrio. Padres de comunicao so determinados pela maneira como Observers e Subjects so interconectados: um nico subject usualmente tem muitos observadores (observers), e algumas vezes o observador de um subject um subject e um outro observador. O padro Mediator centraliza em vez de distribuir. Ele coloca a responsabilidade pela manuteno de uma restrio basicamente no mediador. Descobrimos que mais fcil tornar reutilizveis Observers e Subjects do que Mediators. O padro Observer promove o particionamento e o acoplamento fraco entre o Observer e o Subject, e isso leva obteno de classes de granularidade mais fina, mais aptas a serem reutilizadas. Por outro lado, mais fcil de compreender o fluxo de comunicao no Mediator do que no Observer. Observadores e subjects so usualmente conectados logo aps terem sido criados e difcil ver como esto conectados mais tarde no programa. Se voc conhece o padro Observer, ento compreende que a maneira como observadores e subjects esto conectados importante, e tambm sabe quais conexes procurar. Contudo, o referenciamento indireto que Observer introduz torna um sistema ainda mais difcil de compreender. Observers em Smalltalk podem ser parametrizados com mensagens para acessar o estado do Subject e, assim, eles so ainda mais reutilizveis do que o so em C++. Isto torna, em Smalltalk, o Observer mais atraente que o Mediator. Assim, um programador Smalltalk freqentemente usar o Observer, enquanto que um programador C++ usar o Mediator.

Desacoplando emissores e receptores


Quando objetos colaborantes referenciam-se uns aos outros diretamente, eles se tornam dependentes uns dos outros, e isso pode ter um impacto negativo na estrutura de camadas e na reutilizabilidade de um sistema. O Command, o Observer, Mediator e a Chain of Responsibility tratam de como voc pode desacoplar emissores e receptores, porm com diferentes custos e benefcios. O padro Command suporta o desacoplamento usando um objeto Command para definir a vinculao entre um emissor e um receptor:

320

CAPTULO 5 PADRES

COMPORTAMENTAIS

O objeto Command prov uma interface simples para a emisso da solicitao (ou seja, a operao Execute). Definir a conexo emissor-receptor em um objeto separado permite ao emissor funcionar com diferentes receptores. Isso mantm o emissor desacoplado dos receptores, tornando os emissores fceis de serem reutilizados. Alm disso, voc pode reutilizar um objeto Command para parametrizar um receptor com diferentes emissores. O padro Command requer nominalmente uma subclasse para cada conexo emissor-receptor, embora o padro descreva tcnicas de implementao que evitam o uso de subclasses. O padro Observer desacopla emissores de receptores pela definio de uma interface para sinalizar mudanas em subjects. O Observer define uma vinculao emissor-receptor mais fraca que aquela definida pelo Command, uma vez que um subject pode ter mltiplos observadores e o seu nmero pode variar em tempo de execuo.

No padro Observer, as interfaces do Subject e do Observer so projetadas para a comunicao de mudanas. Portanto, o padro Observer melhor para o desacoplamento de objetos quando existem dependncias de dados entre eles. O padro Mediator desacopla objetos fazendo com que eles referenciem-se indiretamente, atravs de um objeto Mediator.

PADRES DE PROJETO

321

Um objeto Mediator direciona as solicitaes entre objetos-Colega e centraliza sua comunicao. Conseqentemente, colegas podem falar uns com os outros somente atravs da interface de Mediator. Como essa interface fixa, o Mediator pode necessitar implementar o seu prprio esquema de despacho para maior flexibilidade. Solicitaes podem ser codificadas e argumentos empacotados de uma maneira em que os colegas possam solicitar um conjunto aberto de operaes. O padro Mediator pode reduzir o uso de subclasses em um sistema porque ele centraliza o comportamento de comunicao em uma classe, ao invs de distribu-lo entre subclasses. Contudo, esquemas de despacho ad hoc diminuem a segurana relativa consistncia de tipos. Por fim, o padro Chain of Responsibility desacopla o emissor do receptor pela passagem da solicitao por uma cadeia de receptores potenciais:

Uma vez que a interface entre emissores e receptores fixa, o Chain of Responsibility tambm pode requerer um esquema de despacho customizado. Da ter os mesmos inconvenientes relativos segurana, referente consistncia de tipos, que o Mediator. Um Chain of Responsibility uma boa maneira de desacoplar o emissor e o receptor se a cadeia j faz parte da estrutura do sistema, e um dentre vrios objetos pode estar capacitado a tratar a solicitao. Alm disso, o padro oferece a flexibilidade adicional de poder mudar ou estender facilmente a cadeia.

Resumo
Com poucas excees, os padres comportamentais complementam e reforam uns aos outros. Por exemplo, uma classe numa cadeia de responsabilidades provavelmente incluir pelo menos uma aplicao do Template Method (301). O mtodo-template pode usar operaes primitivas para determinar se o objeto deveria tratar a solicitao e para escolher o objeto para o qual repass-la. A cadeia pode usar o padro Command para representar solicitaes como objetos. Interpreter (231) pode usar o padro State para definir contextos para anlise sinttica. Um iterador pode percorrer um agregado, e um visitante pode aplicar uma operao para cada elemento no agregado. Os padres comportamentais tambm funcionam bem com outros padres. Por exemplo, um sistema que usa um padro Composite (160) poderia usar um visitante para executar operaes nos componentes da composio. Ele poderia usar a Chain of Responsibility para permitir aos componentes acessarem propriedades globais atravs dos seus pais. Tambm poderia usar Decorator (170) para substituir essas propriedades nas partes da composio. Poderia usar o Observer para amarrar uma estrutura de objeto a uma outra, e o padro State para permitir que o componente

322

CAPTULO 5 PADRES

COMPORTAMENTAIS

mudasse o seu comportamento quando o seu estado mudasse. A composio, propriamente dita, poderia ser criada usando a soluo mostrada em Builder (104), e ela poderia ser tratada como um Prototype (121) por alguma outra parte do sistema. Sistemas orientados a objeto bem-projetados so exatamente assim eles tm mltiplos padres embutidos mas no necessariamente porque seus projetistas pensaram nestes termos. A composio em termos de padres, em vez de em classes ou objetos, permite-nos atingir a mesma sinergia com maior facilidade.

Notas
1

8 9 10

11

12

Para simplificar, ignoraremos a precedncia de operadores, assumindo que ela responsabilidade de qualquer que seja o objeto que constri a rvore sinttica. Booch se refere a iteradores externos e internos como iteradores ativos e passivos, respectivamente [Boo94]. Os termos ativo e passivo descrevem o papel do cliente, no o nvel de atividade no iterador. Cursores so exemplos simples do padro Memento (266) e compartilham muitos dos seus aspectos de implementao. Ns podemos tornar essa interface ainda menor fundindo Next, IsDone e CurrentItem numa nica operao que avana para o prximo objeto e o retorna. Se o percurso acabou, ento essa operao retorna um valor especial (por exemplo, 0) que marca o fim da iterao. Voc pode garantir isto em tempo de compilao simplesmente declarando como privados os operadores new e delete. No necessrio uma implementao que a acompanhe. A operao Traverse nesses exemplos um Template Method (301) com operaes primitivas TestItem e ProcessItem. Note que o nosso exemplo deleta o objeto de estado no fim da iterao. Porm, delete no seria chamada se ProcessItem emitisse uma exceo desta forma criando uma rea perdida de memria (garbage). Este um problema em C++, mas no em Dylan, que possui garbage collection. Ns discutimos uma soluo para este problema na pgina 252. Este exemplo est baseado no protocolo de conexo TCP descrito por Lynch and Rose [LR93]. Isso torna cada subclasse TCPState um Singleton (ver Singleton, 130). Poderamos usar a sobrecarga de funo para dar a essas operaes o mesmo nome simples, como Visit, uma vez que as operaes esto diferenciadas pelos parmetros que lhes so passados. Existem prs e contras a respeito de tal sobrecarga. Por um lado, ela refora o fato de que cada operao envolve a mesma anlise, embora sobre um argumento diferente. Por outro lado, isso pode tornar menos bvio o que est acontecendo no local da chamada para algum que esteja lendo o cdigo. Na realidade, isso se resume sua opinio sobre sobrecarga de funo ser uma coisa boa ou no. Se podemos ter despacho duplo, ento por que no triplo ou quadrplo, ou qualquer outro nmero? Na realidade, o despacho duplo apenas um caso especial de despacho mltiplo (multiple dispatch), no qual a operao escolhida com base num nmero qualquer de tipos. (CLOS efetivamente suporta o despacho mltiplo.) Linguagens que suportam despacho duplo ou mltiplo diminuem a necessidade de uso do padro Visitor. Este tema ocorre tambm em outros tipos de padres. O AbstractFactory (95), o Builder (104) e Prototype (121) encapsulam conhecimento sobre como objetos so criados. Decorator (170) encapsula responsabilidades que podem ser acrescentadas a um objeto. O Bridge (151) separa uma abstrao da sua implementao, permitindo que elas variem independentemente.

6
Concluso
possvel argumentar que este livro no conseguiu muita coisa. Afinal de contas, no apresenta nenhum algoritmo ou tcnica de programao que j no tenha sido usada. No fornece um mtodo rigoroso para projetar sistemas, nem desenvolve uma nova teoria de projeto de sistemas ele simplesmente documenta projetos existentes. Voc poderia concluir que um texto que no oferece muito para um projetista experiente em orientao a objetos. Mas, esperamos que voc pense de maneira diferente. A catalogao de padres de projeto importante. Ela nos d nomes padronizados e definies para as tcnicas que usamos. Se no estudarmos padres de projeto em software, no seremos capazes de melhor-los, e ser mais difcil criar novos padres. Este livro s um comeo. Ele contm alguns dos padres de projeto mais comuns usados por projetistas experientes de software orientado a objetos, e mesmo assim as pessoas em geral somente ouviram falar de padres por conversas ou por estudo de sistemas existentes. Os primeiros rascunhos deste livro estimularam outras pessoas a escrever os padres de projeto que usavam, e, na sua forma atual, deve estimular ainda outros profissionais a fazerem o mesmo. Esperamos que isto marque o comeo de um movimento com o objetivo de documentar a experincia dos profissionais de software. Este captulo discute o impacto que ns pensamos que os padres de projeto tero, como esto relacionados a outros trabalhos no projeto de sistemas e como voc pode envolver-se na pesquisa e catalogao de padres.

6.1 O que esperar do uso de padres de projeto


Apresentaremos vrias maneiras pelas quais os padres de projeto neste livro podem afetar o modo como voc projeta software orientado a objetos, com base na nossa experincia diria com eles.

324

CAPTULO 6 CONCLUSO

Um vocabulrio comum de projeto


Estudos de programadores especialistas em linguagens convencionais mostraram que o conhecimento e a experincia no so organizados simplesmente em torno da sintaxe, mas sim em torno de estruturas conceituais maiores, tais como algoritmos, estruturas de dados e termos [AS85,Cop92,Cur89,SS86], e planos para atingir um objetivo especfico [SE84]. Os projetistas provavelmente no pensam sobre a notao que usam para registrar decises de projeto, enquanto esto tentando resolver a situao atual do projeto com planos, algoritmos, estrutura de dados e termos que aprenderam no passado. Os cientistas da computao nomeiam e catalogam algoritmos e estruturas de dados, porm, raramente nomeamos outros tipos de padres. Os padres de projeto fornecem um vocabulrio comum para comunicar, documentar e explorar alternativas de projeto. Os padres de projeto tornam um sistema menos complexo ao permitir falar sobre ele em um nvel de abstrao mais alto do que aquele de uma notao de projeto ou uma linguagem de programao. Os padres de projeto elevam o nvel no qual voc projeta e discute o projeto com seus colegas. Uma vez absorvidos os padres de projeto deste livro, provavelmente o seu vocabulrio de projeto mudar. Voc falar diretamente nos nomes dos padres. Voc surpreender a si prprio dizendo: vamos usar um Observer aqui ou vamos tornar estas classes um Strategy.

Um auxlio para documentao e o aprendizado


O conhecimento dos padres de projeto deste livro torna mais fcil compreender sistemas existentes. A maioria dos grandes sistemas orientados a objetos usa estes padres. As pessoas que esto aprendendo programao orientada a objeto freqentemente se queixam de que os sistemas com os quais esto trabalhando usam a herana de maneira confusa e inconsistente e de que difcil seguir o fluxo de controle. Em grande parte, isso deve-se ao fato de elas no compreenderem os padres de projeto no sistema. O aprendizado dos padres ir ajudar a compreender os sistemas orientados a objetos existentes. Esses padres de projeto tambm podem torn-lo um projetista melhor. Eles fornecem solues para problemas comuns. Se voc trabalha com sistemas orientados a objetos j h algum tempo, provavelmente aprendeu esses padres de projeto pelo seu prprio esforo. Porm, a leitura do livro vai ajud-lo a aprender muito mais rapidamente. O aprendizado desses padres ajudar um novato a trabalhar de maneira semelhante a um profissional experiente na rea. Alm do mais, descrever um sistema em termos dos padres de projeto que usa facilitar sua compreenso. De outra forma, as pessoas tero que fazer a engenharia reversa do projeto para desenterrar os padres que ele usa. Ter um vocabulrio comum significa que voc no necessita descrever todo o padro de projeto; simplesmente o nomeia e espera que o seu leitor o conhea. Um leitor que no conhece os padres ter primeiramente que examin-los, porm isto mais fcil do que fazer uma engenharia reversa. Usamos esses padres em nossos prprios projetos e descobrimos que so de muita valia. Ns os utilizamos para escolher nomes para classes, para pensar e ensinar a projetar em termos da seqncia dos padres de projeto que aplicamos nos mesmos [BJ94]. fcil imaginar maneiras mais sofisticadas de utilizar padres, tais

PADRES DE PROJETO

325

como ferramentas CASE baseadas em padres ou documentos em hipertexto. Porm, os padres so de grande ajuda, mesmo sem o uso de ferramentas sofisticadas.

Um acrscimo aos mtodos existentes


Os mtodos de projeto orientados a objetos so promotores de bons projetos. Ensinam novos projetistas a padronizar o desenvolvimento dos projetos. Um mtodo de projeto define um conjunto de notaes (normalmente grficas) para modelagem de vrios aspectos de um projeto, juntamente com um conjunto de regras que determinam como e onde usar cada notao. Os mtodos de projeto geralmente descrevem problemas mais comuns, como resolv-los e como avaliar a qualidade do projeto. Porm, no tm sido capazes de capturar a experincia de projetistas especialistas. Acreditamos que os nossos padres de projeto sejam uma pea importante que tem faltado nos mtodos de projeto orientado a objetos. Os padres mostram como usar tcnicas bsicas, tais como objetos, herana e polimorfismo. Mostram como parametrizar um sistema com um algoritmo, um comportamento, um estado, ou os tipos de objetos que se supem que ele deva criar. Os padres de projeto fornecem uma maneira de descrever em mais detalhes o porqu de um projeto e no simplesmente registrar os resultados de suas decises. As sees Aplicabilidade, Conseqncias e Implementao dos padres de projeto ajudam nas decises a serem tomadas. Os padres de projeto so especialmente teis na converso de um modelo de anlise para um modelo de implementao. Apesar de muitas afirmaes prometendo a transio suave da anlise para o projeto orientado objetos, na prtica, ela no tem sido nada suave. Um projeto flexvel e reutilizvel conter objetos que no esto no modelo de anlise. A linguagem de programao e as bibliotecas de classe usadas afetam o projeto. Freqentemente, os modelos de anlise tm que ser reprojetados para tornarem-se reutilizveis. Muitos dos padres de projeto do catlogo tratam desses tpicos, e essa a razo pela qual o chamamos de padres de projeto. Um mtodo de projeto plenamente desenvolvido e maduro requer mais tipos de padres do que apenas padres para projeto. Tambm podem existir para anlise, projetos de padres de interfaces de usurio ou para ajuste de desempenho. Porm, padres de projetos so uma parte essencial de todo o processo, uma parte que estava faltando at agora.

Um alvo para refatorao


Um dos problemas no desenvolvimento de software reutilizvel que, freqentemente, o software tem que ser reorganizado ou refatorado [OJ90]. Os padres de projeto ajudam a determinar como reorganizar um projeto e podem reduzir o volume de refatorao que voc ter que fazer mais tarde. O ciclo de vida do software orientado a objetos tem vrias fases. Brian Foote identifica-as como prototipao, expanso e consolidao [Foo92]. A fase de prototipao uma atividade agitada medida que o software trazido vida atravs da prototipao rpida em mudanas incrementais, at que satisfaa um conjunto inicial de requisitos e atinja a adolescncia. Neste ponto, o software normalmente consiste de hierarquias de classes que refletem aproximadamente as entidades

326

CAPTULO 6 CONCLUSO

no domnio do problema inicial. O principal tipo de reutilizao a de caixa branca, usando-se a herana. Uma vez que o software atingiu a adolescncia e colocado em servio, sua evoluo governada por duas necessidades conflitantes: (1) o software deve satisfazer mais requisitos, e (2) o software deve ser mais reutilizvel. Comumente, novos requisitos acrescentam novas classes e operaes e, talvez, novas hierarquias completas de classes. O software passa por uma fase de expanso para atender novos requisitos. Contudo, isto no pode continuar por muito tempo. Eventualmente, o software ir tornar-se muito inflexvel e endurecido para permitir mais mudanas. As hierarquias de classes no mais correspondero a qualquer domnio de problema. Pelo contrrio, refletiro muitos domnios de problema, e as classes definiro muitas operaes e variveis de instncia no-relacionadas. Para continuar a evoluir, o software deve ser reorganizado por um processo conhecido como refatorao. Esta a fase na qual freqentemente se detectam possveis frameworks. A refatorao envolve separar as classes em componentes especiais e componentes de finalidade genrica, movendo as operaes para cima ou para baixo ao longo da hierarquia de classes e racionalizando as interfaces das mesmas. Esta fase de consolidao produz muitos tipos novos de objetos, freqentemente pela decomposio de objetos existentes e pela utilizao da composio de objetos, em vez da herana. Por isso a reutilizao de caixa preta substitui a reutilizao de caixa branca. A necessidade contnua de satisfazer mais requisitos, juntamente com a necessidade de maior reutilizao, faz o software orientado a objetos passar por repetidas fases de expanso e consolidao de expanso medida que novos requisitos so atendidos, e de consolidao medida que o software se torna mais genrico.

expanso

mais requisitos

mais reutilizao

prototipao

consolidao

Esse ciclo inevitvel. Porm, bons projetistas esto cientes das mudanas que podem forar refatoraes. Os bons projetistas tambm conhecem estruturas de classes e objetos que ajudam a evitar refatoraes seus projetos so robustos face s mudanas de requisitos. Uma anlise abrangente de requisitos destacar aqueles que so suscetveis mudana durante a vida do software, e um bom projeto ser elaborado de forma a ser robusto face a essas mudanas. Nossos padres de projeto capturam muitas das estruturas resultantes da refatorao. O uso desses padres, no incio da vida de um projeto, evita refatoraes mais tarde. Mas mesmo que voc no veja como aplicar um padro seno aps ter construdo seu sistema, ainda assim ele pode mostrar como mud-lo.

PADRES DE PROJETO

327

6.2 Uma breve histria


O catlogo comeou como parte da tese de doutorado de Erich Gamma [Gam91,Gam92]. Aproximadamente metade dos padres apresentados j faziam parte da sua tese. Por ocasio da OOPSLA91, ele j era um catlogo independente, e Richard juntou-se a Erich para trabalhar com ele. John juntou-se aos dois logo aps. Por ocasio da OOPSLA92, Ralph passou a fazer parte do grupo. Ns trabalhamos duro para deixar o catlogo pronto para publicao na ECOOP93, mas logo percebemos que um artigo de 90 pginas no seria aceito. Assim, resumimos o catlogo e apresentamos o resumo, que foi aceito. Logo aps, decidimos fazer do catlogo um livro. Os nomes que demos para os padres mudaram um pouco ao longo desse tempo. Wrapper mudou para Decorator, Glue tornou-se Faade, Solitaire passou a ser Singleton, e Walker tornou-se Visitor. Alguns padres foram eliminados do catlogo por no parecerem importantes o bastante. Mas, desde o final de 1992, o conjunto de padres no catlogo mudou pouco. Porm, os padres propriamente ditos evoluram tremendamente. De fato, perceber que algo um padro a parte fcil do processo. Ns quatro temos trabalhado ativamente no desenvolvimento de sistemas orientados a objetos e temos percebido que fcil descobrir padres quando voc examina um nmero significativo de sistemas. Porm, encontrar padres muito mais fcil do que descrev-los. Se voc constri sistemas e reflete sobre aquilo que constri, ento encontrar padres naquilo que voc faz. Mas difcil descrever padres de maneira que as pessoas que no os conheam sejam capazes de compreend-los e perceber porque eles so importantes. Os profissionais experientes reconheceram imediatamente o valor do catlogo ainda nos seus estgios iniciais. Mas os nicos que conseguiram entender os padres eram aqueles que j os tinham usado. Uma vez que uma das finalidades principais do livro era ensinar o projeto orientado a objetos para novos projetistas, percebemos que tnhamos que melhorar o catlogo. Assim, expandimos o tamanho mdio da descrio de um padro, de menos de duas pginas para mais de dez pginas, atravs da incluso de um exemplo motivador detalhado e de amostras ou exemplos de cdigo. Tambm comeamos a examinar as vantagens e desvantagens e as vrias maneiras de implementar o padro. Isto tornou-os mais fceis de serem aprendidos. Uma outra mudana importante que ocorreu em 1994 foi colocar maior nfase no problema que um padro soluciona. mais fcil ver um padro como uma soluo, como uma tcnica que pode ser adaptada e reutilizada. mais difcil ver quando ele apropriado caracterizando os problemas que resolve e o contexto no qual representa a melhor soluo. Em geral, mais fcil ver o qu algum est fazendo do que saber o porqu, e o porqu de um padro o problema que ele resolve. Tambm importante conhecer a sua finalidade porque isso nos auxilia a escolher os padres a serem aplicados. Ela tambm nos auxilia a compreender o projeto de sistemas existentes. O autor de um padro deve determinar e caracterizar o problema que soluciona, mesmo que o tenha que fazer aps ter descoberto a soluo.

328

CAPTULO 6 CONCLUSO

6.3 A comunidade envolvida com padres


No somos os nicos interessados em escrever livros que catalogam os padres que profissionais experientes utilizam. Fazemos parte de uma comunidade interessada em padres, em geral, e em padres relacionados com software, em especial. Christopher Alexander foi o primeiro arquiteto a estudar padres em edificaes e comunidades, e o primeiro a desenvolver uma linguagem de padres para ger-los. Seu trabalho inspirou-nos repetidas vezes. Assim, apropriado, e vale a pena, comparar o nosso trabalho com o seu. Aps isso examinaremos o trabalho de outros profissionais com padres relacionados a software.

As linguagens de padres de Alexander


H muitas semelhanas entre o nosso trabalho e o de Alexander. Ambos so baseados na observao de sistemas existentes e na busca de padres nesses sistemas. Ambos propem gabaritos para descrio de padres (embora nossos gabaritos sejam bem diferentes dos seus). Seu trabalho e o nosso dependem de linguagem natural e de muitos exemplos para a descrio dos padres, em detrimento do uso de linguagens formais, e ambos os trabalhos fornecem motivao (rationales) a cada padro. Mas, nos seguintes pontos, nossos trabalhos so diferentes: 1. Os homens tm construdo edificaes h milhares de anos, e nessa rea existem muitos exemplos clssicos em que se basear. Em contrapartida, estamos construindo sistemas de software h pouco tempo e poucos sistemas so considerados clssicos. 2. Alexander d uma ordenao segundo a qual seus padres devem ser usados; ns, no. 3. Os padres de Alexander enfatizam os problemas de que tratam, enquanto nossos padres de projeto descrevem as solues com mais detalhes. 4. Alexander afirma que o uso dos seus padres gera edificaes completas. No afirmamos que os nossos padres geram programas completos. Quando Alexander afirma que voc pode projetar uma casa simplesmente pela aplicao de seus padres, um aps outro, ele tem objetivos semelhantes queles dos projetistas de software orientado a objetos que do regras passo-a-passo para o projeto. Alexander no nega a necessidade de criatividade; alguns dos seus padres requerem a compreenso dos hbitos de vida das pessoas que utilizaro a edificao e sua crena na poesia do projeto implica um nvel de percia alm da linguagem de padres proposta1. Porm, sua descrio de como padres geram projetos significa que uma linguagem de padres pode tornar o processo de projeto determinstico e capaz de ser repetido. O ponto de vista de Alexander fez com que nos detivssemos sobre custos e benefcios envolvidos no projeto as diferentes foras que ajudam a dar forma a um projeto. Sua influncia fez com que trabalhssemos arduamente para compreender a aplicabilidade e as conseqncias dos nossos padres e tambm nos ajudou a evitar a preocupao com uma definio formal de padres. Embora para eles tal representao pudesse tornar a automatizao possvel, nesse estgio em que estamos mais importante explorar o espao-problema dos padres de projeto em vez de formaliz-los.

PADRES DE PROJETO

329

Do ponto de vista de Alexander, os padres neste livro no formam uma linguagem de padres. Devido diversidade dos sistemas de software que as pessoas constroem, difcil ver como poderamos prover um conjunto de padres completo, conjunto esse que ofereceria instrues passo-a-passo para projetar uma aplicao. Poderamos fazer isso para certas classes de aplicaes, tal como a impresso de relatrios ou um sistema de entrada de formulrios. Porm, nosso catlogo apenas uma coleo de padres relacionados; no podemos sequer fingir que uma linguagem de padres. De fato, pensamos que improvvel que algum dia venha a existir uma linguagem de padres completa para software. Mas certamente possvel obter alguma mais completa. As adies teriam que incluir frameworks e mostrar como us-los [Joh92], padres para o projeto de interfaces para usurios [BJ94], padres para a anlise [Coa92], e para todos os outros aspectos do desenvolvimento de software. Os padres de projetos so apenas uma parte de uma linguagem maior de padres para software.

Padres em software
Nossa primeira experincia coletiva com o estudo da arquitetura de software aconteceu em um workshop na OOPSLA91 conduzido por Bruce Anderson. O workshop foi dedicado ao desenvolvimento de um manual (handbook) para arquitetos de software. (A julgar por este livro, ns suspeitamos que enciclopdia de arquitetura de software seria um nome mais apropriado do que manual de arquitetura). Aquele primeiro workshop estimulou uma srie de encontros, o mais recente dos quais foi a primeira conferncia sobre Patterns Languages of Programs, ocorrida em agosto de 1994. Essa conferncia criou uma comunidade de pessoas interessadas na documentao de experincia (expertise) em software. Naturalmente, outros tambm tiveram esse objetivo. The Art of Computer Programming, de Donald Knuth [Knu73], foi uma das primeiras tentativas de catalogar o conhecimento existente sobre software, embora tenha se concentrado na descrio de algoritmos. Ainda assim, a tarefa mostrou-se demasiadamente grande para poder ser terminada. A srie Graphics Gems [Gla90, Arv91, Kir92] outro catlogo de conhecimentos sobre projeto, embora tambm tenda a se concentrar em algoritmos. O programa Domain Specific Software Architeture (Arquiteturas de Software Especficas de Domnios), patrocinado pelo Departamento da Defesa dos Estados Unidos [GM92], concentra-se na coleta de informaes sobre arquiteturas. A comunidade de engenharia de software, com formao na rea de representao do conhecimento, tenta representar o conhecimento geral relacionado com software. Existem muitos outros grupos com objetivos, pelo menos um pouco, parecidos com os nossos. O livro Advanced C++: Programming Styles and Idioms, de James Coplien [Cop92], tambm nos influenciou. Os padres nesse livro tendem a ser mais especficos para C++ do que os nossos padres de projeto, e seu livro contm tambm uma grande quantidade de padres de nvel mais baixo. Porm, existe alguma sobreposio do seu trabalho com o nosso, como salientamos na descrio de nossos padres. Jim tem estado ativo na comunidade envolvida com padres. Ele atualmente est trabalhando em padres que descrevem os papis das pessoas em organizaes de desenvolvimento de software. Existe uma grande quantidade de outros lugares nos quais pode-se encontrar descries de padres. Kent Beck foi um dos primeiros na comunidade de software a divulgar e apoiar o trabalho de Christopher Alexander. Em 1993, ele comeou a escrever uma coluna no The Smalltalk Report sobre os padres em Smalltalk. Peter Coad tambm tem coletado padres j algum tempo. Seu artigo sobre os padres parece conter

330

CAPTULO 6 CONCLUSO

principalmente padres de anlise [Coa92]; ns no vimos os seus ltimos padres, embora saibamos que ele ainda est trabalhando neles. Ouvimos falar de vrios livros sobre padres que esto em preparao, porm, no vimos nenhum deles ainda. Tudo que podemos fazer informar que esto por ser lanados. Um destes livros ser proveniente da conferncia Patterns Languages of Programs.

6.4 Um convite
O que voc pode fazer se estiver interessado em padres? Em primeiro lugar, use-os e procure outros padres que se ajustem sua maneira de projetar. Uma poro de livros e artigos sobre padres aparecer nos prximos anos, de maneira que haver uma grande quantidade de fontes de novos padres. Desenvolva seu vocabulrio de padres e utilize-o. Utilize-o quando conversar com outras pessoas sobre os seus projetos. Utilize-o quando voc pensar e escrever sobre eles. Em segundo lugar, seja um consumidor crtico. O catlogo de padres de projeto o resultado de um trabalho duro, no apenas nosso, mas tambm de dezenas de revisores que nos apresentaram crticas e comentrios. Se perceber um problema, ou acreditar que so necessrias maiores explicaes, entre em contato conosco. O mesmo se aplica para qualquer outro catlogo de padres: d retorno aos autores! Uma das melhores coisas dos padres que eles removem as decises de projeto do domnio das intuies vagas. Eles permitem que os autores sejam explcitos sobre as solues de compromisso que adotam. Isso torna mais fcil ver o que est errado com seus padres e argumentar com seus autores. Tire partido disso. Em terceiro lugar, procure os padres que voc usa e descreva-os. Torne-os parte da sua documentao. Mostre-os para outras pessoas. Voc no precisa trabalhar em um laboratrio de pesquisa para encontrar padres. De fato, encontrar padres relevantes quase impossvel se voc no tem uma experincia prtica. Sinta-se vontade para escrever o seu prprio catlogo de padres... mas assegure-se de que algum mais ir auxiliar a coloc-los no ponto!

6.5 Um pensamento final


Os melhores projetos usaro muitos padres que se encaixam e se entrelaam para produzir um todo maior. Como diz Christopher Alexander: possvel projetar edificaes juntando padres de uma maneira bastante casual. Uma edificao projetada dessa forma uma montagem de padres. Ela no densa. Ela no profunda. Mas tambm possvel juntar padres de tal maneira que muitos se sobreponham no mesmo espao fsico: a edificao fica muito densa; tem muitos significados capturados em um pequeno espao; e, atravs dessa densidade, se torna profunda. A Pattern Language [AIS+77, Pgina xli]

Notas
1

Ver The poetry of the language [AIS+77].

Apndice

Glossrio

acoplamento (coupling): Medida do grau de dependncia, uns dos outros, dos componentes de um software. acoplamento abstrato (abstract coupling): Dada uma classe A que mantm uma referncia para uma classe abstrata B, diz-se que a classe A est acoplada abstratamente a B. Chamamos isso de acoplamento abstrato, porque A se refere a um tipo de objeto e no a um objeto concreto. assinatura (signature): A assinatura de uma operao define o seu nome, seus parmetros e seu valor de retorno. classe (class): Uma classe define a interface e a implementao de um objeto. Ela especifica a representao interna do objeto e define as operaes que o objeto pode executar. classe abstrata (abstract class): Classe cuja finalidade primria definir uma interface. Uma classe abstrata posterga parte ou toda a sua implementao para subclasses. Ela no pode ser instanciada. classe concreta (concrete class): Classe que no tem operaes abstratas. Ela pode ser instanciada. classe-me/ancestral (parent class) Classe da qual uma outra classe herda. Sinnimos: superclasse (superclass, em Smalltalk), classe-base (base class, em C++) e classe-pai ou classe-me. classe mixin (mixin class): Classe projetada para ser combinada com outras classes atravs do uso da herana. As classes mixin so geralmente abstratas. composio de objetos (object composition): Montagem de objetos para obter um objeto composto com um comportamento mais complexo. constructor: Em C++, uma operao que automaticamente invocada para iniciar novas instncias. delegao (delegation): Mecanismo de implementao pelo qual um objeto repassa ou delega uma solicitao para um outro objeto. O delegado executa a solicitao em lugar do objeto original.

332

GLOSSRIO

destructor: Em C++, uma operao que automaticamente invocada para finalizar um objeto que est para ser deletado. diagrama de classe (class diagram): Diagrama que ilustra classes, suas estruturas internas e suas operaes, e os relacionamentos estticos entre elas. diagrama de interao (interaction diagram): Diagrama que mostra o fluxo de solicitaes (mensagens) entre objetos. diagrama de objeto (object diagram): Diagrama que ilustra a estrutura em tempo de execuo de um objeto especfico. encapsulamento (encapsulation): Resultado de ocultar uma representao e uma implementao em um objeto. A representao no visvel e no pode ser acessada diretamente a partir do exterior do objeto. As operaes so a nica forma de acessar e modificar a representao de um objeto. framework: Conjunto de classes que cooperam entre si e compem um projeto reutilizvel para uma categoria especfica de software. Um framework fornece uma melhor orientao arquitetnica do software, atravs do particionamento do projeto em classes abstratas e da definio de suas responsabilidades e colaboraes. Um desenvolvedor customiza o framework, para uma aplicao particular, atravs da especializao e da composio de instncias de suas classes. friend class: Em C++, uma outra classe que tem os mesmos direitos de acesso s operaes e dados de uma determinada classe. herana (inheritance): Relacionamento que define uma entidade em termos de uma outra. A herana de classe define uma nova classe em termos de uma, ou mais, classe(s)me(s). A nova classe chamada de subclasse ou (em C++) de classe derivada. A herana de classes combina a herana de interface e a herana de implementao. A herana de interface define uma nova interface em termos de uma ou mais interfaces existentes. A herana de implementao define uma nova implementao em termos de uma, ou mais, implementao(es) existente(s). herana privada (private inheritance): Em C++, uma classe herdada somente por causa de sua implementao. interface (interface): Conjunto de todas as assinaturas definidas pelas operaes de um objeto. A interface descreve o conjunto de solicitaes as quais um objeto pode responder. ligao dinmica (dynamic binding): Associao, em tempo de execuo, de uma solicitao a um objeto e uma de suas operaes. Em C++, somente funes virtuais so dinamicamente amarradas. metaclasse (metaclass): As classes so objetos em Smalltalk. Uma metaclasse a classe de um objeto-classe. objeto (object): Entidade existente, em tempo de execuo, que empacota tanto os dados como os procedimentos que operam sobre esses dados. objeto agregado (aggregate objetct): Objeto que composto de sub-objetos. Os sub-objetos so chamados partes do agregado, e o agregado responsvel por eles. operao (operation): Os dados de um objeto podem ser manipulados somente por suas operaes. Um objeto executa uma operao quando ele recebe uma solicitao. Em C++, estas operaes so chamadas de member functions. O Smalltalk usa o termo method. operao abstrata (abstract operation): Operao que declara uma assinatura, mas no a implementa. Em C++, uma operao abstrata corresponde a uma funo-membro virtual pura (pure virtual member function).

PADRES DE PROJETO

333

operao de classe (class operation): Uma operao dirigida para uma classe e no para um objeto individual. Em C++, operaes de classe so chamadas static member functions (funes membro estticas). padro de projeto (design pattern): Um padro de projeto sistematicamente nomeia, motiva e explica uma soluo de projeto geral, que trata um problema recorrente de projeto em sistemas orientados a objetos. Ele descreve o problema, a soluo, quando aplicar a soluo e suas conseqncias. Tambm d sugestes e exemplos de implementao. A soluo um arranjo genrico de objetos e classes que resolve o problema. A soluo customizada e implementada para resolver o problema em um contexto particular. polimorfismo (polymorphism): Capacidade de substituir objetos com interfaces coincidentes por um outro objeto em tempo de execuo. protocolo (protocol): Estende o conceito de interface para incluir as seqncias permissveis de solicitaes. receptor (receiver): Objeto alvo, ou destinatrio, de uma solicitao. redefinio (overriding): Redefinio de uma operao (herdada de uma classe ancestral) em uma subclasse. referncia a um objeto (object reference): Valor que identifica um outro objeto. relacionamento de agregao (aggregation relationship): Relacionamento de um objeto agregado com suas partes. Uma classe define esse relacionamento para as suas instncias (ou seja, objetos agregados). relacionamento de conhecimento ou associao (acquaintance relationship): Uma classe que se refere a uma outra tem um conhecimento daquela classe. reutilizao de caixa branca (white-box reuse): Estilo de reutilizao baseado na herana de classe. Uma subclasse reutiliza a interface e a implementao da sua classe me, porm, ao assim faz-lo, pode ter acesso a aspectos que, de outra maneira, seriam privativos da sua classe-me. reutilizao de caixa preta (black-box reuse): Estilo de reutilizao, baseado na composio de objetos. Os objetos compostos no revelam detalhes internos uns para os outros e, assim, so anlogos a caixas pretas. solicitao (request): Um objeto executa uma operao quando ele recebe uma solicitao correspondente de um outro objeto. Um sinnimo comum para a solicitao mensagem. subclasse (subclass): Classe que herda de uma outra classe. Em C++, uma subclasse chamada de derived class (classe derivada). subsistema (subsystem): Grupo independente de classes que colaboram para cumprir um conjunto de responsabilidades. subtipo (subtype): Um tipo um subtipo de outro se sua interface contm a interface do outro tipo. supertipo (supertype) Tipo original do qual um tipo herda. tipo (type): Nome de uma determinada interface. tipo parametrizado (parameterized type): Tipo que deixa no-especificados alguns tipos constituintes. Os tipos no especificados so fornecidos como parmetros no ponto de utilizao. Em C++, tipos parametrizados so chamados de templates. toolkit: Coleo de classes que fornece funcionalidades teis, porm, no define o projeto de uma aplicao. varivel de instncia (instance variable): Componente de dados que define parte da representao de um objeto. C++ usa o termo data member.

Apndice

Guia para notao

Usamos diagramas por todo o livro para ilustrar idias importantes. Alguns so informais, como uma imagem da tela de uma caixa de dilogo ou um esquema mostrando uma rvore de objetos. Porm, os padres de projeto, em particular, usam notaes mais formais para denotar relacionamentos e interaes entre classes e objetos. Este apndice descreve essas notaes em detalhe. Usamos trs notaes diagramticas diferentes: 1. Um diagrama de classe ilustra classes, suas estruturas e os relacionamentos estticos entre elas. 2. Um diagrama de objeto ilustra uma determinada estrutura de objeto em tempo de execuo. 3. Um diagrama de interao mostra o fluxo de solicitaes entre objetos. Cada padro inclui pelo menos um diagrama de classe. As outras notaes so usadas quando necessrias para suplementar a discusso. Os diagramas de classe e de objeto esto baseados na OMT (Object Modeling Techinique) [RBP+91, Rum94]1. Os diagramas de interao so tirados do Objectory [JCJO92] e do mtodo Booch [Boo94]. Estas notaes esto sumarizadas na capa interna posterior do livro.

B.1 Diagrama de classe


A figura B.1a mostra a notao OMT para classes abstratas e concretas. Uma classe denotada por uma caixa (retngulo) com seu nome no topo. As operaes-chave da classe aparecem abaixo do nome. Quaisquer variveis de instncia aparecem abaixo das operaes. A informao de tipo opcional; usamos a conveno C++, que coloca o nome do tipo antes do nome da operao (significando o tipo do retorno), varivel de

336

GUIA PARA NOTAO

instncia ou parmetro efetivo. O tipo itlico indica que a classe, ou operao, abstrata. Em alguns padres til ver onde as classes de clientes referenciam classes participantes. Quando um padro inclui uma classe Client como um dos seus participantes (significando que o cliente tem uma responsabilidade no padro), o Client aparece como uma classe ordinria. Por exemplo, isto acontece no Flyweight (187). Quando o padro no inclui um participante Client (ou seja, clientes no tm responsabilidades no padro), porm a sua incluso de qualquer maneira esclarece quais participantes interagem com clientes, ento a classe Client mostrada em cinza-claro, como mostrado na figura B.1b. Um exemplo disso Proxy (198). Um Client em cinzaclaro tambm chama a ateno para a possibilidade de ele ter sido omitido na discusso sobre os participantes. A figura B.1c mostra vrios relacionamentos entre classes. A notao OMT para a herana de classes um tringulo conectando uma subclasse (na figura, LineShape) sua classe me (Shape). Uma referncia a um objeto representando um relacionamento do tipo parte-de, ou agregao, indicada por uma flecha com um losango na sua origem ou base. A flecha aponta para a classe que agregada (por exemplo, Shape). Uma flecha sem o losango denota associao (acquaintance); por exemplo, um LineShape mantm uma referncia para um objeto Color que outros shapes podem compartilhar. Um nome para a referncia pode aparecer prximo base ou origem para distingui-la de outras referncias 2. Uma outra coisa til de ser mostrada quais classes instanciam outras (que tambm devem ser mostradas). Usamos uma flecha tracejada para indicar isto, uma vez que a OMT no tem suporte para essa informao. Chamamos isto de relacionamento cria. A flecha aponta para a classe que instanciada. Na figura B.1c, CreationTool cria objetos LineShape. A OMT tambm define um crculo cheio para significar mais que um. Quando o crculo aparece na ponta de uma referncia, significa que mltiplos objetos esto sendo referenciados ou agregados. A figura B.1c mostra que Drawing agrega mltiplos objetos de tipo Shape. Finalmente, estendemos a OMT com anotaes de pseudocdigo para nos permitir esboar as implementaes de operaes. A figura B.1d mostra a anotao de pseudocdigo para a operao Draw na classe Drawing.

B.2 Diagrama de objeto


Um diagrama de objeto mostra exclusivamente instncias. Ele fornece um instantneo dos objetos em um padro. Os objetos so nomeados da forma asomething, em que something a classe do objeto.

PADRES DE PROJETO

337

AbstractClassName AbstractOperation 1 () Type AbstractOperation2 ()

ConcreteClassName
Operation 1 () Type Operation 2 () instance Variable 1 Type instance Variable 2

(a) Classes abstratas e concretas

Client

Client

(b) Classe Client Participante ( esquerda) e classe Client implcita ( direita)

Drawing

shapes

Shape

Creation Tool

LineShape

Color

(c) Relacionamentos de classes

Drawing Draw() for each shape { shape >Draw() }

(d) Anotao em pseudocdigo

Figura B.1 Notao de diagrama de classe.

Figura B.2 Notao de diagrama de objeto.

338

GUIA PARA NOTAO

Figura B.3 Notao de diagrama de interao.

Nosso smbolo para um objeto (modificado ligeiramente da OMT padro) uma caixa com os cantos arredondados, com uma linha separando o nome do objeto de quaisquer referncias a objetos. Flechas indicam o objeto referenciado. A figura B.2 mostra um exemplo.

B.3 Diagrama de interao


Um diagrama de interao mostra a ordem na qual as solicitaes entre objetos so executadas. A figura B.3 um diagrama de interao que mostra como um shape adicionado a um drawing (desenho). O tempo flui do topo para a base do diagrama em um diagrama de interao. Uma linha vertical slida indica o tempo de vida de um determinado objeto. A conveno de nomenclatura para objetos a mesma que a adotada para diagramas de objeto o nome da classe prefixado pelo artigo indefinido (por exemplo, aShape). Se o objeto no instanciado at depois do incio do tempo, tal como registrado no diagrama, ento sua linha vertical aparece tracejada desde a origem do tempo at o instante de sua criao. Um retngulo vertical mostra que um objeto est ativo, ou seja, est tratando uma solicitao. A operao pode enviar solicitaes para outros objetos; estas so indicadas por uma flecha horizontal apontando para o objeto receptor. O nome da solicitao mostrado logo acima da flecha. Uma solicitao para criar um objeto mostrada por uma flecha pontilhada. Uma solicitao para o prprio objeto emissor (que emitiu a solicitao) aponta de volta para o mesmo. A figura B.3 mostra que a primeira solicitao de um objeto aCreationTool para criar aLineShape. Mais tarde, aLineShape acrescentado a aDrawing, o qual solicita a aDrawing enviar uma solicitao Refresh para si mesmo. Note que aDrawing envia uma solicitao Draw para aLineShape, como parte da operao Refresh.

PADRES DE PROJETO

339

Notas
1

OMT usa o termo diagrama de objeto para referir-se a diagramas de classe. Ns usamos diagrama de objeto para referirmo-nos exclusivamente a diagramas de estruturas de objetos. OMT tambm define associaes entre classes que aparecem como linhas simples entre caixas de classes. Associaes so bidirecionais. Embora as associaes sejam adequadas durante a anlise, pensamos que so de nvel muito alto para expressar os relacionamentos nos padres, simplesmente porque associaes devem ser mapeadas para referncias, ou apontadores, para objetos durante o projeto. As referncias para objetos so intrinsecamente direcionadas e, portanto, so mais adequadas para os relacionamentos de nosso interesse. Por exemplo, Drawing sabe algo sobre Shapes, mas um objeto Shape no sabe em que Drawing ele est. Voc no pode expressar esse relacionamento somente com associaes.

Apndice

Classes fundamentais (foundation classes)

Este apndice documenta as classes fundamentais que usamos no exemplo de cdigo, em C++, de diversos padres. Mantivemos as classes intencionalmente simples e mnimas. Descrevemos as seguintes classes: List, uma lista ordenada de objetos; Iterator, a interface para acessar os objetos de um agregado em uma sequncia; ListIterator, um iterador para percorrer uma List; Point, um ponto num espao de duas dimenses; Rect, um retngulo alinhado com os eixos. Alguns novos tipos-padro da C++ podem no estar disponveis em todos os compiladores. Particularmente, se o seu compilador no define bool, ento definao manualmente como:

C.1 List (Lista)


O template da classe List fornece um container bsico para armazenar uma lista ordenada de objetos. List armazena elementos por valor, o que significa que ele funciona para tipos primitivos, bem como para instncias de classes. Por exemplo, List<int> declara uma lista de ints. Mas, a maioria dos padres usa List para armazenar apontadores para objetos, como em List<Glyph*>. Dessa forma, List pode ser usada para listas heterogneas.

342

CLASSES FUNDAMENTAIS

Por convenincia, List tambm fornece sinnimos para operaes de pilhas, o que torna o cdigo que usa List para pilhas mais explcito, sem ter que definir outra classe.

As seguintes sees descrevem essas operaes em maior detalhe.

Construo, destruio, inicializao e atribuio


List (long size)

Inicia a lista. O parmetro size uma sugesto para o nmero inicial de elementos.
List (List&)

Redefine o constructor por omisso de cpia de maneira que determinados membros sejam iniciados apropriadamente.
~List ()

Libera as estruturas de dados internas da lista, mas no os elementos na lista. A classe no est projetada para ter subclasses; portanto o destructor no virtual.
List& operator = (const List&)

Implementa a operao de atribuio para atribuir dados adequadamente.

Acesso
Essas operaes fornecem o acesso bsico aos elementos da lista.

PADRES DE PROJETO long Count () const

343

Retorna o nmero de objetos da lista.


Item& Get (long index) const

Retorna o objeto situado em um ndice determinado.


Item& First () const

Retorna o primeiro objeto da lista.


Item& Last () const

Retorna o ltimo objeto da lista.

Adio
void Append (const Item&)

Acrescenta o argumento lista, tornando-o seu ltimo elemento.


void Prepend (const Item&)

Acrescenta o argumento lista, tornando-o seu primeiro elemento.

Remoo
void Remove (const Item&)

Remove um determinado elemento da lista. Essa operao requer que o tipo dos elementos na lista suporte o operador == de comparao.
void RemoveFirst ()

Remove o primeiro elemento da lista.


void RemoveLast ()

Remove o ltimo elemento da lista.


void RemoveA11 ()

Remove todos os elementos da lista.

Interface de Stack (Pilha)


Item& Top () const

Retorna o elemento do topo (quando a lista vista como uma pilha).


void Push (const Item&)

Empilha o elemento (no topo da pilha).


Item& Pop ()

Retira o elemento que est no topo da pilha.

344

CLASSES FUNDAMENTAIS

C.2 Iterator (Iterador)


Iterator uma classe abstrata que define uma interface para percurso de agregados.

As operaes fazem o seguinte:


virtual void First ()

Posiciona o iterador no primeiro objeto no agregado.


virtual void Next ()

Posiciona o iterador no prximo objeto na seqncia.


virtual bool isDone() const

Retorna True quando no h mais objetos na seqncia.


virtual Item CurrentItem () const

Retorna o objeto na posio corrente na seqncia.

C.3 ListIterator (IteradordeLista)


ListIterator implementa a interface de Iterator para percorrer objetos List. Seu

constructor aceita como um argumento uma lista a ser percorrida.

PADRES DE PROJETO

345

C.4 Point (Ponto)


Point representa um ponto em coordenadas cartesianas em um espao bidimensional. Um Point suporta um mnimo de aritmtica vetorial. As coordenadas de um Point so

definidas como

As operaes de Point so auto-explicativas.

Um membro esttico Zero representa Point (0,0).

346

CLASSES FUNDAMENTAIS

C.5 Rect (Retngulo)


Rect representa um retngulo alinhado com os eixos. Um Rect definido por um ponto de origem e uma extenso (ou seja, largura e altura). As operaes de Rect so autoexplicativas.

O membro esttico Zero equivalente ao retngulo

Referncias bibliogrficas

[Add94] Addison-Wesley, Reading, MA. NEXTSTEP General Reference: Release 3, Volumes 1 and 2,1994. [AG90] D.B. Anderson and S. Gossain. Hierarchy evolution and the software lifecycle. In TOOLS 90 Conference Proceedings, pages 41-50, Paris, June 1990. Prentice Hall. [AIS+ 77] Christopher Alexander, Sara Ishikawa, Murray Silverstein, Max lacobson, Ingrid Fiksdahl-King, and Shlomo Angel. A Pattern Language. Oxford University Press, New York, 1977. [App89] Apple Computer, Inc., Cupertino, CA. Macintosh Programmers Workshop Pascal 3.0 Reference, 1989. [App92] Apple Computer, Inc., Cupertino, CA. Dylan. An object-oriented dynamic language, 1992. [Arv91] James Arvo. Graphics Gems II. Academic Press, Boston, MA, 1991. [AS85] B. Adelson and E. Soloway. The role of domain experience in software design. IEEE Transactions on Software Engineering, 11 (11):1351-1360, 1985. [BE93] Andreas Birrer and Thomas Eggenschwiler. Frameworks in the financial engineering domain: An experience report. In European Conference on Object- Oriented Programming, pginas 21-35, Kaiserslautern, Germany, July 1993. SpringerVerlag. [BJ94] Kent Beck and Ralph lohnson. Patterns generate architectures. In European Conference on Object-Oriented Programming, pages 139-149, Bologna, Italy, July 1994. Springer-Verlag. [Boo94] Grady Booch. Object-Oriented Analysis and Design with Applications. Benjamin/ Cummings, Redwood City, CA, 1994. Segunda edio. [Bor81] A. Borning. The programming language aspects of ThingLaba constraint-oriented simulation laboratory. ACM Transactions on Programming Languages and Systems, 3(4):343-387, Outubro1981. [Bor94] Borland International, Inc., Scotts Valley, CA. A Technical Comparison of Borland ObjectWindows 2.0 and Microsoft MFC 2.5,1994.

348

REFERNCIAS

BIBLIOGRFICAS

[BV90] Grady Booch and Michael Vilot. The design of the C++ Booch components. In Object-Oriented Programming Systems, Languages, and Applications Conference Proceedings, pginas 1-11, Ottawa, Canada, Outubro 1990. ACM Press. [Cal93] Paul R. Calder. Building User Interfaces with Lightweight Objects. PhD thesis, Stanford University, 1993. [Car89] I. Carolan. Constructing bullet-proof classes. In Proceedings C++ at Work 89. SIGS Publications, 1989. [Car92] Tom Cargill. C++ Programming Style. Addison-Wesley, Reading, MA, 1992. [CIRM93] Roy H. Campbell, Nayeem Islam, David Raila, and Peter Madeany. Designing and implementing Choices: An object-oriented system in C++. Communications of the ACM, 36(9):117-126, Setembro 1993. [CL90] Paul R. Calder and Mark A. Linton. Glyphs: Flyweight objects for user interfaces. In ACM User Interface Software Technologies Conference, pginas 92-101, Snowbird, UT, Outubro 1990. [CL92] Paul R. Calder and Mark A. Linton. The object-oriented implementation of a document editor. In Object-Oriented Programming Systems, Languages, and Applications Conference Proceedings, pginas 154-165, Vancouver, British Columbia, Canada, Outubro 1992. ACM Press. [Coa92] Peter Coad. Object-oriented patterns. Communications of the ACM, 35(9):152-159, Setembro 1992. [Coo92] William R. Cook. Interfaces and specifications for the Smalltalk-80 collection classes. In Object-Oriented Programming Systems, Languages, and Applications Conference Proceedings, pages 1-15, Vancouver, British Columbia, Canada, Outubro 1992. ACM Press. [Cop92] James O. Coplien. Advanced C++ Programming Styles and Idioms. AddisonWesley, Reading, MA, 1992. [Cur89] Bill Curtis. Cognitive issues in reusing software artifacts. In Ted I. Biggerstaff and Alan I. Perlis, editors, Software Reusability, Volume II: Applications and Experience, pginas 269-287. Addison-Wesley, Reading, MA, 1989. [dCLF93] Dennis de Champeaux, Doug Lea, and Penelope Faure. Object-Oriented System Development. Addison-Wesley, Reading, MA, 1993. [Deu89] L. Peter Deutsch. Design reuse and frameworks in the Smalltalk-80 system. In Ted J. Biggerstaff and Alan T. Perlis, editors, Software Reusability, Volume II: Applications and Experience, pginas 57-71. Addison-Wesley, Reading, MA, 1989. [Ede92] D. R. Edelson. Smart pointers: Theyre smart, but theyre not pointers. In Proceedings of the 1992 USENIX C++ Conference, pginas 1-19, Portland, OR, Agosto 1992. USENIX Association. [EG92] Thomas Eggenschwiler and Erich Gamma. The ET+ SwapsManager: Using object technology in the financial engineering domain. In ObjectOriented Programming Systems, Languages, and Applications Conference Proceedings, pginas 166-178, Vancouver, British Columbia, Canada, Outubro 1992. ACM Press. [ES90] Margaret A. Ellis and Bjarne Stroustrup. The Annotated C++ Reference Manual. Addison-Wesley, Reading, MA, 1990. [Foo92] Brian Foote. A fractal model of the lifecycles of reusable objects. OOPSLA 92 Workshop on Reuse, Outubro 1992. Vancouver, British Columbia, Canada. [GA89] S. Gossain and D.B. Anderson. Designing a class hierarchy for domain representation and reusability. In TOOLS 89 Conference Proceedings, pginas 201-210, CNIT ParisLa Defense, Frana, Novembro 1989. Prentice Hall.

REFERNCIAS BIBLIOGRFICAS

349

[Gam91] Erich Gamma. Object-Oriented Software Development based on ET++: Design Patterns, Class Library, Tools (in German). PhD thesis, University of Zurich Institut fr Informatik, 1991. [Gam92] Erich Gamma. Object-Oriented Software Development based on ET++: Design Patterns, Class Library, Tools (in German). Springer-Verlag, Berlin, 1992. [Gla90] Andrew Glassner. Graphics Gems. Academic Press, Boston, MA, 1990. [GM92] M. Graham and E. Mettala. The Domain-Specific Software Architecture Program. In Proceedings of DARPA Software Technology Conference, 1992, pginas 204-210, Abril 1992. Tambm publicado em in CrossTalk, The Journal of Defense Software Engineering, pginas 19-21,32, Outubro 1992. [GR83] Adele J. Goldberg and David Robson. Smalltalk-80: The Language and Its Implementation. Addison-Wesley, Reading, MA, 1983. [HHMV192] Richard Helm, Tien Huynh, Kim Marriott, and John Vlissides. An objectoriented architecture for constraint-based graphical editing. In Proceedings of the Third Eurographics Workshop on Object-Oriented Graphics, pginas 1-22, Champery, Switzerland, October 1992. Tambm disponivel como IBM Research Division Technical Report RC 18524 (79392). [HO87] Daniel C. Halbert and Patrick D. OBrien. Object-oriented development. IEEE Software, 4 (5):71-79, Setembro 1987. [ION94] IONA Technologies, Ltd., Dublin, Ireland. Programmers Guide for Orbix, Version 1.2, 1994. [JCJ092] Ivar Jacobson, Magnus Christerson, Patrik Jonsson, and Gunnar Overgaard. Object-Oriented Software EngineeringA Use Case Driven Approach. Addison-Wesley, Wokingham, England, 1992. [JF88] Ralph E. Johnson and Brian Foote. Designing reusable classes. Journal of Object-Oriented Programming, 1 (2):22-35, Junho/Julho 1988. [JML92] Ralph E. Johnson, Carl McConnell, and I. Michael Lake. The RTL system: A framework for code optimization. In Robert Giegerich and Susan L. Graham, editors, Code Generation Concepts, Tools, Techniques. Proceedings of the International Workshop on Code Generation, pginas 255-274, Dagstuhl, Germany, 1992. Springer-Verlag. [Joh92] Ralph Johnson. Documenting frameworks using patterns. In ObjectOriented Programming Systems, Languages, and Applications Conference Proceedings, pginas 63-76, Vancouver, British Columbia, Canada, Outubro 1992. ACM Press. [JZ91] Ralph E. Johnson and Jonathan Zweig. Delegation in C++. Journal of Object-Oriented Programming, 4 (11):22-35, Novembro 1991. [Kir92] David Kirk. Graphics Gems III. Harcourt, Brace, Jovanovich, Boston, MA, 1992. [Knu73] Donald E. Knuth. The Art of Computer Programming, Volumes 1, 2, and 3. Addison-Wesley, Reading, MA, 1973. [Knu84] Donald E. Knuth. The TEXbook. Addison-Wesley, Reading, MA, 1984. [Kof93] Thomas Kofler. Robust iterators in ET++. Structured Programming, 14:6285, Maro 1993. [KP88] Glenn E. Krasner and Stephen T. Pope. A cookbook for using the modelview controller user interface paradigm in Smalltalk-80. Journal of Object-Oriented Programming, 1(3):26-49, Agosto/Setembro 1988. [LaL94] Wilf LaLonde. Discovering Smalltalk. Benjamin/Cummings, Redwood City, CA, 1994. [LCI+92] Mark Linton, Paul Calder, John Interrante, Steven Tang, and John Vlissides. InterViews Reference Manual. CSL, Stanford University, 3.1 edition, 1992.

350

REFERNCIAS

BIBLIOGRFICAS

[Lea88] Doug Lea. libg++, the GNU C++ library. In Proceedings of the 1988 USENIX C++ Conference, pginas 243-256, Denver, CO, Outubro 1988. USENIX Association. [LG86] Barbara Liskov and John Guttag. Abstraction and Specification in Program Development. McGraw-Hill, New York, 1986. [Lie85] Henry Lieberman. Theres more to menu systems than meets the screen. In SIGGRAPH Computer Graphics, pages 181-189,San Francisco, CA, Julho 1985. [Lie86] Henry Lieberman. Using prototypical objects to implement shared behavior in object-oriented systems. In Object-Oriented Programming Systems, Languages, and Applications Conference Proceedings, pginas 214-223, Portland, OR, Novembro 1986. [Lin92] Mark A. Linton. Encapsulating a C++ library. In Proceedings of the 1992 USENIX C++ Conference, pginas 57-66, Portland, OR, Agosto 1992. ACM Press. [LP93] Mark Linton and Chuck Price. Building distributed user interfaces with Fresco. In Proceedings of the 7th X Technical Conference, pginas 77-87, Boston, MA, Janeiro 1993. [LR93] Daniel C. Lynch and Marshall T. Rose. Internet System Handbook. AddisonWesley, Reading, MA, 1993. [LVC89] Mark A. Linton, lohn M. Vlissides, and Paul R. Calder. Composing user interfaces with InterViews. Computer, 22 (2): 8-22, Fevereiro 1989. [Mar91] Bruce Martin. The separation of interface and implementation in C++. In Proceedings of the 1991 USENIX C++ Conference, pginas 51-63, Washington, D.C., Abril 1991. USENIX Association. [McC87] Paul McCullough. Transparent forwarding: First steps. In Object-Oriented Programming Systems, Languages, and Applications Conference Proceedings, pginas 331-341, Orlando, FL, Outubro 1987. ACM Press. [Mey88] Bertrand Meyer. Object-Oriented Software Construction. Series in Computer Science. Prentice Hall, Englewood Cliffs, NJ, 1988. [Mur93] Robert B. Murray. C++ Strategies and Tactics. Addison-Wesley, Reading, MA, 1993. [OJ90] William F. Opdyke and Ralph E. Johnson. Refactoring: An aid in designing application frameworks and evolving object-oriented systems. In SOOPPA Conference Proceedings, pginas 145-161, Marist College, Poughkeepsie, NY, Setembro 1990. ACM Press. [OJ93] William F. Opdyke and Ralph E. lohnson. Creating abstract superclasses by refactoring. In Proceedings of the 21st Annual Computer Science Conference (ACM CSC 93J, pginas 66-73, Indianapolis, IN, Fevereiro 1993. [P+88] Andrew J. Palay et al. The Andrew Toolkit: An overview. In Proceedings of the 1988 Winter USENIX Technical Conference, pginas 9-21, Dallas, TX, Fevereiro 1988.USENIX Association. [Par90] ParcPlace Systems, Mountain View, CA. ObjectWorks\Smalltalk Release 4 Users Guide, 1990. [Pas86] Geoffrey A. Pascoe. Encapsulators: A new software paradigm in Smalltalk-80. In Object-Oriented Programming Systems, Languages, and Applications Conference Proceedings, pginas 341-346, Portland, OR, Outubro 1986. ACM Press. [Pug90] William Pugh. Skiplists: A probabilistic alternative to balanced trees. Communications of the ACM, 33(6):668-676, Junho 1990. [RBP+91] lames Rumbaugh, Michael Blaha, William Premerlani, Frederick Eddy, and William Lorenson. Object-Oriented Modeling and Design. Prentice Hall, Englewood Cliffs, NJ, 1991.

REFERNCIAS BIBLIOGRFICAS

351

[Rum94] James Rumbaugh. The life of an object model: How the object model changes during development. Journal of Object-Oriented Programming, 7(1):24-32, Maro/ Abril 1994. [SE84] Elliot Soloway and Kate Ehrlich. Empirical studies of programming knowledge. IEEE Transactions on Software Engineering, 10(5):595-609, Setembro 1984. [Sha90] Yen-Ping Shan. MoDE: A UIMS for Smalltalk. In ACM OOPSLA/ECOOP 90 Conference Proceedings, pginas 258-268, Ottawa, Ontario, Canada, Outubro 1990. ACM Press. [Sny86] Alan Snyder. Encapsulation and inheritance in object-oriented languages. In Object-Oriented Programming Systems, Languages, and Applications Conference Proceedings, pginas 38-45, Portland, OR, Novembro 1986. ACM Press. [SS86] James C. Spohrer and Elliot Soloway. Novice mistakes: Are the folk wisdoms correct? Communications of the ACM, 29(7):624-632, Julho 1986. [SS94] Douglas C. Schmidt and Tatsuya Suda. The Service Configurator Framework: An extensible architecture for dynamically configuring concurrent, multi-service network daemons. In Proceeding of the Second International Workshop on Configurable Distributed Systems, pginas 190-201, Pittsburgh, - PA, Maro 1994. IEEE Computer Society. [Str91] Bjarne Stroustrup. The C++ Programming Language. Addison-Wesley, Reading, MA, 1991. Segunda Edio. [Str93] Paul S. Strauss. IRIS Inventor, a 3D graphics toolkit. In Object-Oriented Programming Systems, Languages, and Applications Conference Proceedings, pginas 192-200, Washington, D.C., Setembro 1993. ACM Press. [Str94] Bjarne Stroustrup. The Design and Evolution of C++. Addison-Wesley, Reading, MA, 1994. [Sut63] I.E. Sutherland. Sketchpad: A Man-Machine Graphical Communication System. PhD thesis, MIT, 1963. [Swe85] Richard E. Sweet. The Mesa programming environment. SIGPLAN Notices, 20(7):216-229, Julho 1985. [Sym93a] Symantec Corporation, Cupertino, CA. Bedrock Developers Architecture Kit, 1993. [Sym93b] Symantec Corporation, Cupertino, CA. THINK Class Library Guide, 1993. [Sza92] Duane Szafron. SPECTalk: An object-oriented data specification language. In Technology of Object-Oriented Languages and Systems (TOOLS 8), pginas 123-138, Santa Barbara, CA, Agosto 1992. Prentice Hall. [US87] David Ungar and Randall B. Smith. Self: The power of simplicity. In Object-Oriented Programming Systems, Languages, and Applications Conference Proceedings, pginas 227-242, Orlando, FL, Outubro 1987. ACM Press. [VL88] John M. Vlissides and Mark A. Linton. Applying object-oriented design to structured graphics. In Proceedings of the 1988 USENIX C++ Conference, pginas 81-94, Denver, CO, Outubro 1988. USENIX Association. [VL90] John M. Vlissides and Mark A. Linton. Unidraw: A framework for building domain-specific graphical editors. ACM Transactions on Information Systems, 8(3):237-268, Julho 1990. [WBJ90] Rebecca Wirfs-Brock and Ralph E. Johnson. A survey of current research in object-oriented design. Communications of the ACM, 33(9):104-124, 1990. [WBWW90] Rebecca Wirfs-Brock, Brian Wilkerson, and Lauren Wiener. Designing Object-Oriented Software. Prentice Hall, Englewood Cliffs, NJ, 1990. [WGM88] Andre Weinand, Erich Gamma, and Rudolf Marty. ET++ An object-oriented application framework in C++. In Object-Oriented Programming Systems, Languages, and Applications Conference Proceedings, pginas 46-57, San Diego, CA, Setembro 1988. ACM Press.

ndice

Os nomes de padres de projeto aparecem em letras maisculas; por exemplo: ADAPTER. Nmeros em itlico indicam que existe um diagrama para o termo. Letras aps o nmero da pgina de um diagrama indicam o tipo de diagrama: um c indica um diagrama de classe, um i denota um diagrama de interao e um o denota um diagrama de objeto. Por exemplo, 88co significa que na pgina 88 aparecem um diagrama de classe e um diagrama de objeto. Tradues para termos em ingls aparecem entre parnteses: quando no houver uma traduo, ou o termo usado no original ou a traduo aparece no texto no lugar conveniente.

A ABSTRACT FACTORY 96 extensibilidade da 99 no resumo do catlogo 24 uso pelo Lexi da 63 AbstractExpression participante de INTERPRETER 234c, 234 AbstractFactory participante de ABSTRACT FACTORY 97c, 98 Abstraction participante de BRIDGE 153c, 154 AbstractProduct participante de ABSTRACT FACTORY 97c, 98 Ao (Action), ver COMMAND acoplamento 331 abstrato 182, 262, 277, 331 forte 40 fraco 39, 41, 261, 320, ver tambm desacoplamento reduzindo 39, 181, 182, 212, 215 acoplamento abstrato, ver acoplamento abstrato em OBSERVER 277

acumulando estados 310 Ada 20, 37 adaptado (Adaptee) participante de ADAPTER 142, 142c Adapter participante de ADAPTER 142, 142c ADAPTER (Adaptador) 140 comparado com BRIDGE 160, 208 comparado com DECORATOR 179 comparado com PROXY 207 no resumo do catlogo 24 adapter (adaptador[a])141 classe 142, 142c objeto 142, 142c parametrizado 146 plugvel, ver plugvel (pluggable) two-way 144, 144c adorner (adornador) 174 Aggregate participante de ITERATOR 246, 246c agregao 37, 332 C++, definida em 38 comparada com associao 38

354

NDICE
Booch Components uso de ITERATOR 256 uso de STRATEGY 300 Booch, (mtodo) 333 Booch, Grady ix, 247 BooleanExp 240 Border 57, 58c, 59o BorderDecorator 171o, 171c, 175 bridge (ponte), 152 BRIDGE (PONTE) 151 comparado com ADAPTER 208 configurado por ABSTRACT FACTORY 154 no resumo do catlogo 24 uso da delegao no 37 uso pelo Lexi 70 Btree (rvore balanceada) 194 Builder participante de BUILDER 105, 105c BUILDER (CONSTRUTOR) 104 comparado com ABSTRACT FACTORY 111, 137 comparado com PROTOTYPE 137 no resumo do catlogo 24 uso no exemplo de compilador 183 Bureaucrat 221, ver tambm CHAIN OF RESPONSIBILITY Button (boto) 62c, 212o, 213ci, 218, 263

notao para 38 Smalltalk, definida em 38 Alexander, Christopher ix, 18, 328, 330 algoritmo esqueleto 302 evitando dependncia de 39 famlia de 294 postergando passos do 302 AlternationExpression 232co, 317 implementada em Smalltalk 237 alumnio, liga de 86 Ambassador, ver tambm PROXY C++, idioma (idiom) 199 AnalogClock 283 Anderson, Bruce viii, 329 AndExp 241 Andrew Toolkit uso de OBSERVER 283 Apllication 113, 113c, 212o, 213ci, 220, 301, 301c ApplicationWindow 65, 66c, 67c, 156, 222c, 223c ArrayCompositor 55c, 293, 293c, 299 rvore sinttica abstrata 232, 239, 306 construindo em Smalltalk 238 estrutura de classes para 232c, 306c estrutura de objeto para 232o ASCII7Stream 178, 178c aspecto de um objeto 279 AssignmentNode 307c assinatura (de uma operao) 28, 333 associao (acquaintance) 37, 332 C++, definida em 38 comparada com agregao 38 Smalltalk, definida em 38 associaes, ver tambm associao, agregao em OMT 336 atualizaes (updates) disparando (triggering) 278 encapsulando complexas 279 limitando inesperadas 277 protocolo para em OBSERVER 277

C C 20 Calder, Paul 47, 52 callback de funo 224 Caractere 52, 52c representado como objeto 52, 188 Caretaker participante de MEMENTO 268, 268c, 269i Cargill, Tom 286 CHAIN OF RESPONSIBILITY (CADEIA DE RESPONSABLIDADES) 212 combinada com COMPOSITE 163, 221 comparada com MEDIATOR 321 comparada com OBSERVER 321 definida por referncias aos pais 163, 221 no resumo do catlogo 24 uso da delegao na 37 ChangeManager 265, 279, 280c Cheshire Cat 154 Choices (sistema operacional) uso de FAADE 186 uso de PROXY 200 ciclo de vida do software 325, 326 classe 29, 332 abstrata 30, 31c, 332, 336, 335c adapter, ver adapter, classe biblioteca, ver toolkit comparada com tipo 31 derivada 333

B Beck, Kent viii, 329 Bedrock uso de DECORATOR 174, 175 black box reuse (reutilizao de caixa preta ou fechada), ver reutilizao de caixa preta ou black box bloco (block), Smalltalk 256 BNF, forma 236 exemplos da 231, 1861, 239 boleano (boolean) expresso 239 varivel 242 BombedMazeFactory 101 BombedMazeGame 120 BombedWall 101, 102, 128

NDICE
diagrama de classe 333, 335c, 332 dificuldades para alterar 39 friend, ver friend, classe de hereditariedade, ver hereditariedade instncia 30 mixin 31, 331c, 333 notao para 30, 333 pai/me 30, 333 subclasse 30 template, ver template classe ancestral 333, ver tambm classe-pai/me classe concreta, 30, 332 evitando especificao da 39 isolando clientes da 98 classe-base, ver classe-me Client participante da CHAIN OF RESPONSIBILITY 214ci, 215, 321i participante de ABSTRACT FACTORY 97c, 98 participante de ADAPTER 142, 142c participante de BUILDER, 105, 105c, 106i participante de COMMAND 225, 225c, 226i participante de COMPOSITE 161c, 162 participante de FLYWEIGHT 190c, 191 participante de INTERPRETER 234c, 235 participante de PROTOTYPE 123, 123c cliente 27 isolando das classes concretas 98 ClockTimer 282 clonado (objeto) 121 inicializando 125 clone (operao) 121 implementando 125 usada em PROTOTYPE 121 CLOS 20, 247, 312 Coad, Peter 329 CodeGenerationVisitor 307c Colleague comunicando com Mediator 262, 265 participante de MEDIATOR 260co, 261, 321i comandos condicionais da linguagem de programao ConstraintSolver 266-267, 271 evitando usar STATE 286 evitando usar STRATEGY 294 command C++, idioma (idiom), ver functor copiando antes da execuo 227 histria, ver lista da histria implementado atravs de templates C++ 228, 229 inteligncia dos 227 COMMAND 222 combinado com MEMENTO 228, 270 combinado com PROTOTYPE 227 no resumo do catlogo 24 uso em Lexi 74 Command 72, 72c, 222, 222c, 228 configurado em MenuItem 72 histria dos 73 participante de COMMAND 225, 225c, 226i, 320i

355

comparao de padres; pattern matching 231 compilador exemplo em FAADE 179, 180c, 182 implementado usando VISITOR 306 compilador RTL Smalltalk uso de COMPOSITE 169 uso de STRATEGY 300 Compiler 179, 180c, 185 Component participante de COMPOSITE 161c, 162, 165 participante de DECORATOR 172, 172c composio de objetos 33, 333 comparada com herana 33-36 comparada com tipos parametrizados 37 reutilizao atravs 34 composio recursiva 50, ver tambm COMPOSITE, causas de reprojeto, 39 da estrutura de um documento 50 de elementos grficos 160 de estruturas partes-todo 161 iterao sobre 248 Composite participante de COMPOSITE 161c, 162, 162o, 165 COMPOSITE 160 caching filhos de 166 combinado com INTERPRETER 243 combinado com ITERATOR, 248 combinado com VISITOR 313 comparado com DECORATOR 208-209 comparado com INTERPRETER 236 compartilhando componentes 163 controle de filhos 164 dados sobre a estrutura de dados para 166, 167 interface de 164 no resumo do catlogo 24 referncias aos pais 163 uso em Model/View/Controller 21 uso pelo Lexi 53 CompositeEquipment 168 Composition 54, 55c, 293, 293c, 297 Compositor 54, 55, 293, 293c, 298 interface 54 CompressionStream 178c, 179 comunicao encapsulada ou distribuda 320 entre Strategy e Context 295 entre Visitor e Element 311 comunicao broadcast 277 ConcreteAggregate participante de Iterator 246, 246c ConcreteBuilder participante de BUILDER 105c, 106, 106i ConcreteCommand participante de COMMAND 225, 225c, 227 ConcreteComponent participante de DECORATOR 172, 172c ConcreteDecorator participante de DECORATOR 172c, 173

356

NDICE
D data members 333 DebuggingGlyph 177 DECORATOR (DECORADOR) 171 comparado com ADAPTER 171, 179 comparado com COMPOSITE 57, 170, 208-209 comparado com PROXY 207, 208-209 comparado com STRATEGY 174 lightweight (leve) versus heavyweight (pesado) 174 no resumo do catlogo 24 uso pelo Lexi 58c, 59o, 60 decorator 171 Decorator 171, 240 participante de DECORATOR 172, 172c, 175o deep copy, ver copy, deep delegao 36, 262, 333 comparada com herana 36-37 implementando adaptadores plugveis com 145 padres que dependem da 37 delegado 36, 145 dependncia 275 controlando/administrando complexa 279 dependncias de compilao reduzindo usando FAADE 182 Dependents, ver OBSERVER desacoplamento, ver tambm acoplamento, fraco emissores e receptores 320 interface e implementao 154 desfazer/refazer 71-72, 73-74, 224, 227, 266, 287 evitando a acumulao de erros durante 228 despacho (dispatch) duplo (double) 312 mltiplo (multiple) 313 simples(single) 312 destructor 333 garantindo que o do iterador chamado 252 diagrama de interao 23, 338 em BUILDER 106i em CHAIN OF RESPONSIBILITY 213i, 321i em COMMAND 226i, 320i em MEDIATOR 258i, 321i em MEMENTO 269i em OBSERVER 276i, 320i em VISITOR 84i Visitor do Lexi 84i diagrama de objeto 336, 333 Dialog 212, 220 DialogDirector 258o, 259ci, 262 DialogWindow 65, 66c, 67c DigitalClock 282 Director participante de BUILDER 105c, 106, 106i DirectoryBrowser 145, 145c, 146c Doc 197, ver tambm Lexi document (documento) cor 55, 299 estrutura fsica 49

ConcreteElement participante de VISITOR 308, 308c, 309i ConcreteFactory participante de ABSTRACT FACTORY 98 ConcreteFlyweight participante de FLYWEIGHT 190co, 191 ConcreteHandler participante de CHAIN OF RESPONSIBILITY, 214c, 215 ConcreteImplementor participante de BRIDGE, 153c, 154 ConcreteIterator participante de ITERATOR 246, 246c ConcreteObserver participante de OBSERVER 275c, 276, 276i ConcreteProduct participante de ABSTRACT FACTORY 97, 98 participante de FACTORY METHOD, 113, 113c ConcretePrototype participante de PROTOTYPE 123, 123c ConcreteState participante de STATE 286, 286c ConcreteStrategy participante de STRATEGY 294c, 294 ConcreteSubject participante de OBSERVER 275c, 276, 276i ConcreteVisitor participante 308, 308c, 309i constructor 333 Context 240 participante de INTERPRETER 234c, 235 participante de STATE 285, 285c participante de STRATEGY 294c, 294 contratibilidade 297 convenes de nomenclatura 44, 46 FACTORY METHOD 46, 118 TEMPLATE METHOD 304 VISITOR 311 convite 330 Coplien, James 129, 153, 159, 230, 291, 329 copy (cpia) deep 125 on write 201 shalow 125 CountingMazeBuilder 111 CreateMaze (operao) 94 ABSTRACT FACTORY, variante C++, 100 ABSTRACT FACTORY, variante Smalltalk, 102 BUILDER, variante de 109 FACTORY METHOD, variante de 119 PROTOTYPE, variante Smalltalk, 127, 129 Creator implementao de 116, 118 participante de FACTORY METHOD 113, 113c cursor, ver iterador, cursor Cursor, ver ITERATOR

NDICE
estrutura lgica 53 formatao 53 Document 113, 113c, 222c, 223, 223c, 301, 301c documentando o projeto com padres 42, 324 doesNotUnderstand (mensagem) usada para implementar CHAIN OF RESPONSIBILITY 218 usada para implementar PROXY 203, 206 Domain 186, 186c Door 92c, 93 extenses para PROTOTYPE 127 downcast 99 Dylan 20 uso de MEMENTO 272 dynamic _cast em C++ 99, 165 dynamic binding (amarrao ou vinculao dinmica) 29, 333 estilos de interao 96 suporte no Lexi 49, 60 estrutura do cdigo em tempo de execuo versus em tempo de compilao 38 ET++ uso da CHAIN OF RESPONSIBILITY 221 uso de ABSTRACT FACTORY 103 uso de ADAPTER 129, 149 uso de BRIDGE 159 uso de BUILDER 111 uso de COMMAND 230 uso de COMPOSITE 169 uso de DECORATOR 177, 178 uso de FACADE 186 uso de FACTORY METHOD 120 uso de FLYWEIGHT 198 uso de ITERATOR 247 uso de MEDIATOR 264 uso de OBSERVER 283 uso de PROTOTYPE 124, 129 uso de PROXY 207 uso de STRATEGY 299-300 ET++ SwapsManager uso de STRATEGY 300 ETgdb 129 exploso, ver hierarquia de classe, exploso ExtendedHandler 217 extensibilidade 297 externo (iterador), ver iterador, externo extrnseco (estado), ver estado, extrnseco

357

E Eiffel 32, 37 Element participante de VISITOR 308, 308c e-mail, endereo do contatando os autores viii emissor desacoplamento do receptor 320 encapsulao 27, 333 da anlise e percurso do documento 81 da semntica de atualizaes complexas, 279, ver tambm ChangeManager de algoritmos, ver STRATEGY de como objetos so criados, ver ABSTRACT FACTORY, BUILDER, PROTOTYPE de solicitaes 71, ver tambm COMMAND do comportamento especfico de estados, ver STATE do conceito que varia 44, 66 do percurso, ver ITERATOR do protocolo entre objetos, ver MEDIATOR preservando as fronteiras da 269 rompendo com a herana 34 rompendo com VISITOR 311 EnchantedMazeFactory 101 envelope-letter (idioma) 291 Equipamento 167, 314 EquipmentVisitor 315 Erros (acumulao de) evitando durante desfazer/refazer 228 escopo de um padro de projeto, ver padro de projeto, escopo estados acumulando durante o percurso 310 compartilhando 286, ver tambm FLYWEIGHT evitando inconsistentes 286 extrnseco 188 intrnseco 188 mudanas incrementais 287

F Faade participante de FAADE 179c, 181, 181c FAADE (FACHADA) 179 comparado com MEDIATOR 187, 265 no resumo do catlogo 24 uso em Choices 186 FACTORY METHOD (MTODO FBRICA) 113 no resumo do catlogo 24 parametrizado com o identificador do produto 115 usado para criar um iterador 245 usado para implementar ABSTRACT FACTORY 99, 99, 115 variantes especficas s linguagens 117 fase de consolidao do ciclo de vida 325 fase de expanso do ciclo de vida 325, 326 fase de prototipao do ciclo de vida 325 fecho 247, 253 fecho transparente 56, ver tambm DECORATOR FileStream 178, 178c finalidade de um padro de projeto, ver padro de projeto, finalidade fluxo de caixa futuro 300 fluxo de controle encapsulao, ver MEDIATOR inverso do 42

358

NDICE
Handler participante da CHAIN OF RESPONSIBILITY 214, 214ci, 321i help (ajuda) on line 212 sensvel ao contexto 212 Help-Handler 213, 213c, 216, 218 herana 30, 333 C++, definida em 32 classe versus interface 31 combinada com polimorfismo 33 comparada com a composio de objetos 33, 173 comparada com tipos parametrizados 37 de implementao 32, 333 de interface 32, 333 definida em Eiffel 32 dinmica 287 mixin, ver classe, mixin notao para 30, 336, 335c reutilizao atravs 33 Smalltalk, definida em 32 uso apropriado da 33 herana mltipla, ver tambm classe mixin usada para implementar adapter de classe 142 usada para implementar BRIDGE 155 herana privada 333, ver tambm herana, de implementao hierarquia de classe adicionando operaes 328 conectando paralelas 114, 245 exploso 40, 56, 151, 172 minimizando o tamanho da 118, 124, 172, 261, 294, 321 visitando mltiplas 310 hifenizao 74 Holywood (princpio de) 302 hook (operao) 302, 303 em ABSTRACT FACTORY 114 em FACTORY METHOD 114 em PROXY 203 em TEMPLATE METHOD 302, 303 HotDraw uso de STATE 291 hub de comunicao 258

FLYWEIGHT 188 combinado com COMPOSITE 164, 192 combinado com INTERPRETER 236 combinado com STATE 286 no resumo do catlogo 25 participante de FLYWEIGHT 190, 190co uso pelo Lexi 52 flyweight 188, 188o administrando compartilhados 192 flyweightFactory 196 participante de FLYWEIGHT 190co, 191 FontDialogDirector 258, 258o, 259ci Foote, Brian 325 framework para memria virtual 186 framework 41, 333 comparado com padres de projeto 43 compromissos associados com 42 documentando com padres 42 editor grfico 121 ver Bedrock ver Choices ver ET++ ver HotDraw ver MacApp ver NeXT ver NEXTSTEP ver Rapp ver RTL Smalltalk compilador ver Unidraw Fresco 317 friend (classe) 333 usada para garantir a Iterator um acesso privilegiado uma coleo (collection) 248 usada para suportar Memento 287 functor 230

G gdb 129 generics (ADA, Eiffel; templates, C++) 37 gerenciador de prottipo 125 glifo discricionrio(discretionary glyph) 85 Glue, ver FAADE Glyph 52, 52c, 55c, 56o, 58c, 59o, 62c, 66c, 77c implementado como um flyweight 193-196 interface 52 interface para percurso 76 GlyphArrayIterator 77, 77c GNU gdb 129 gramtica 231 mantendo complexa 236 Graphic 160c, 199c, 204 GraphicTool 121, 122c GUIFactory 61, 62c, 63

I IconWindow 65, 66c, 151-152, 151c, 156 Image 199o, 199c ImageProxy 199o, 199c Implementor participante de BRIDGE 153c, 154 implcito (receptor), ver receptor, implcito Inicializao tardia (lazy initialization) 117 instncia, ver tambm classe, instncia garantindo a unicidade da, ver SINGLETON varivel 30, 333

H Hamlet 17 Handle/Body, ver tambm BRIDGE idioma (idiom) de C++ 154, 159

NDICE
instanciao 30 abstraindo o processo de 61 notao para 30, 336, 335c integrado (circuito) 300 interface 28 benefcios de programar para 33 conversao, ver ADAPTER especificando em C++ 32 estreita versus larga em MEMENTO 268 herana 28, 32 inflar, inchar 244 para iterao 248 simplificando subsistemas , ver FACADE INTERPRETER (INTERPRETADOR) 231 combinado com COMPOSITE 243 combinado com VISITOR 236, 243 no resumo do catlogo 25 InterViews uso de ABSTRACT FACTORY 103 uso de ADAPTER 149 uso de COMMAND 230 uso de COMPOSITE 169 uso de DECORATOR 177 uso de FLYWEIGHT 197 uso de OBSERVER 283 uso de SINGLETON 136 uso de STRATEGY 297, 299-300 intrnseco (estado), ver estado, intrnseco inverso do fluxo de controle 42 Invoker participante de COMMAND 225, 225c, 226i, 320i IRIS Inventor uso de Visitor 317 iterao polimrfica 245 implementando em C++ 247 Iterador 76, 244, 313 acesso para agregado 248 acesso privilegiado para Aggregate 248 alternativa para, em Smalltalk 256 ativo 247 controlando 247 cursor 247 externo 247, 313, 255 garantindo a excluso do 252 interface para 247, 249 interno 247, 253, 313, ver tambm ListTraverser nulo (null) 248, ver tambm NullIterator parametrizado com uma operao 253 passivo 247 polimrfico 245, 247, 251 robusto 247 sobre estruturas recursivas 248 ITERATOR (ITERADOR) 244 combinado com COMPOSITE 333 combinado com VISITOR 313 comparado com VISITOR 310 no resumo do catlogo 25 uso em Lexi 79 Iterator 77, 77c, 245c, 249, 344 participante de ITERATOR 246, 246c K

359

Kit, ver tambm ABSTRACT FACTORY em InterViews 103 Knuth, Donald 329

L Leaf participante de COMPOSITE 161c, 162, 162o, 165 Lempel-Ziv (compresso de) 178 Lexi 47 estrutura do documento 49 interface do usurio 47, 48 mltiplos sistemas de janelas 63 operaes do usurio 70 padres de aparncia e resposta 60 percurso e anlise do documento 74 libg++ uso de BRIDGE 159 linguagem(ns) de padres 328 Linton, Mark 317 List 244, 244c, 245c, 249, 341 list box 258 lista da histria 73-74, 227 copiando comandos para a 227 ListBox 258o, 259ci, 263 ListIterator 77, 244, 244c, 245c, 250, 344 ListTraverser 253 LiteralExpression 232co, 317 implementada em Smalltalk 238

M MacApp uso da CHAIN OF RESPONSIBILITY 221 uso de COMMAND 230 uso de DECORATOR 174, 175 uso de FACTORY METHOD 118, 120 Macbeth 17 MacFactory 62c Macintosh 61, 64 MacroCommand 224, 224c, 230 Manipulator 114, 115c MapSite 92, 92c Marriage of Convenience (casamento de convenincia) 150 Maze 92c, 94 MazeBuilder 108 MazeFactory 100 como singleton 136 MazeGame 94, 119 MazePrototypeFactory 126 Mediator comunicando com Colleague 262, 265 omitindo a classe abstrata de 262 participante de MEDIATOR 260co, 261, 321i

360

NDICE
O Objective C 99, 124, 125, 137, 145 Objectory 333 ObjectWindows uso de ITERATOR 256 uso de STRATEGY 300 ObjectWorks\Smalltalk, ver tambm Smalltalk uso de ADAPTER 149-150 uso de DECORATOR 177 uso de FAADE 185 objeto 27, 333 adapter, ver adapter, objeto agregao 37 aspecto de um 279 associao 37 como argumento para uma solicitao 318 compartilhado, ver FLYWEIGHT composio, ver composio de objetos encontrando 27 especificando a implementao do 29 especificando interface para 28 evitando dependncias de implementao de 39 granularidade do 28, ver tambm FLYWEIGHT guts (aspectos internos) 174 referncia 333 skin (aspectos externos) 174 Objeto Modeling Technique (OMT) 23, 29, 333, 336 Objetos para Estados, ver STATE OBSERVER (OBSERVADOR) 275 combinado com MEDIATOR 262, 265 comparado com CHAIN OF RESPONSABILITY 320, 321 comparado com MEDIATOR 320, 321 em Model/View/Controller 21 limitando atualizaes inesperadas em 277 no resumo do catlogo 24 Observer 280 combinando com Subject 280 participante de OBSERVER 275c, 276, 276i, 280c, 320i operao 27, 333 adicionando a classes 309 concreta 302 despacho, ver dispatch ou despacho evitando dependncia de especficas 39 hook, ver hook (operao) primitiva 302, 304 substituio 31 operao abstrata 30, 332 uso para implementar ADAPTER 145 operao de classe 332 alternativas fornecidas por SINGLETON 131 Orbix uso de FACTORY METHOD 120 Originator participante de MEMENTO 268, 268c, 269i originator 267 overloading (sobrecarregar) usado para implementar PROXY 202 usado para implementar VISITOR 82, 311

MEDIATOR (MEDIADOR) 258 combinado com OBSERVER 279 comparado com CHAIN OF RESPONSIBILITY 321 comparado com FACADE 265 comparado com OBSERVER 320, 321 no resumo do catlogo 25 uso de delegao em 37 mediator 258 member function 332, ver tambm operao Memento combinado com COMMAND 228, 287 participante de MEMENTO 268, 268c, 269i MEMENTO 266 no resumo do catlogo 25 memento 267 custos associados com 269 suporte de linguagem para 287 MemoryStream 178 menu 222 configurando 72, 223 pull-down 70 Menu 62c, 222c MenuItem 71, 72c, 222, 222c metaclasse 136, 333 mtodo (method) 332, ver tambm operao Meyer, Bertrand 150 Microsoft Windows, 64 Model/View/Controller 20, 21 uso de COMPOSITE 21, 169 uso de FACTORY METHOD 120 uso de OBSERVER 21, 283 uso de STRATEGY, 22 modelo de objeto da anlise transformando para o modelo de objeto do projeto 325 MonoGlyph 57, 58c Motif 49, 60, 61, 61, 63, 96 MotifFactory 61, 62c mudanas incrementais 287 MVC, ver Model/View/Controller

N NeXT AppKit Uso de ADAPTER 150 Uso de BRIDGE 159 Uso de CHAIN OF RESPONSIBILITY 221 Uso de TEMPLATE METHOD 304 NEXTSTEP uso de ADAPTER 146 uso de PROXY 199, 203, 207 N (Node) 307c NodeVisitor 307c NonTerminalExpression participante de INTERPRETER 234c, 235 notificao 275 null (iterador), ver iterador, nulo NullIterator 77c, 77-78, 248 NXProxy 199, 203

NDICE
P padro comportamental 26, 211 comparao entre escopo de classe e de objeto 211 discusso do 318 padro de criao 26, 91 discusso do 137 padro de projeto 333 aspectos do projeto variados pelo 45 benefcios 323 classificao 26 como usar 44 comparados com frameworks 43 complemento metodologia de projeto 325 diagrama de relacionamentos 27 documentando projetos com 42, 324 elementos essenciais 19 encontrando 327 escopo 26 finalidade 26 histria dos 327 no resumo do catlogo 24 refatorando com 325 seleo do 43 tabela de 26 template (formato) do catlogo 22 padro estrutural 26, 139 comparao entre escopo de classe e escopo de objeto 139 discusso do 208 pai/me (classe), ver classe, me ou pai/me pais: referncias para os definidas em COMPOSITE 163 parser ( analisador sinttico) 236 partes-todo, ver composio recursiva ver tambm agregao Pascal 20 PassivityWrapper 177 passivo (iterador), ver iterador, passivo path (caminho, trajetria) especificando formas com mltiplos segmentos 69 Pattern Languages of Programs (PLOP) 329 percurso de lista 244 percurso de objetos agregados, ver tambm ITERATOR ao longo de hierarquias de classes 310 atribuindo responsabilidades em VISITOR 313 in-ordem, pr-ordem, ps-ordem 248 persistncia 200 Picture 160c, 161o plataforma de hardware isolando a aplicao da 39 plataforma de software isolando a aplicao da 39 plugvel (adapter) 143 implementao de um 145-146, 145c, 146c, 149, 150c PMFactory 62c PMIconWindow 151, 151c PMWindowImp 152, 152c , 157-158 Point (ponto) 373

361

Policy (polticas, procedimentos, regras), ver STRATEGY polimorfismo 29, 333 usado com herana 33 PreorderIterator 77 member functions 78-79 Presentation Manager 49, 61, 64, 68, 69, 96, 151, 157 PrincingVisitor 315 PrintDialog 222o, 213i Product participante de Builder 105c, 106 participante de FACTORY METHOD 113, 113c produtos (objetos produtos) 61 criando em ABSTRACT FACTORY 99 famlia de 96 mudando em tempo de execuo 123 trocando 98 variando a representao dos 107 programas de aplicao 40 projeto dj-vu 18 densidade 330 documentando com padres 42, 324 para reutilizao 38 poesia do 328 projetando para mudanas 38 projeto de objeto de modelo 325 proteo ver proxy protocolo 333 prottipo 121 PROTOTYPE (PROTTIPO) 121 combinado com COMMAND 227 comparado com ABSTRACT FACTORY 129, 137 comparado com FACTORY METHOD 120, 124 no resumo do catlogo 25 participante de PROTOTYPE 123, 123c usado para implementar ABSTRACT FACTORY 99 Proxy 199 combinado com ITERATOR 248 comparado com DECORATOR 209 no resumo do catlogo 25 participante do PROXY 200co, 200 proxy 199 proteo 199, 201 remoto 199, 201 virtual 199, 201 pseudocdigo 31, 31c, 335c Publish-Subscribe, ver OBSERVER pull model 279 pull-down (menu), ver menu, pull-down push model 279

Q QOCA uso de ADAPTER 144 uso de INTERPRETER 243 uso de MEMENTO 274

362
R

NDICE
seqenciando solicitaes 223 shallow copy, ver copy, shallow Shape 140, 141c, 147 smbolo no-terminal (nonterminal symbol) 235 smbolo terminal 235 shared usando FLYWEIGHT 236 SimpleCompositor 55, 55c, 293, 293c, 298 single static assignment form, SSA 169 single-dispatch (despacho simples), ver dispatch (despacho), single (simples) Singleton participante de SINGLETON 130c, 131 SINGLETON 130 C++, implementao em 132, 134 no resumo do catlogo 25 registro de um 133 subclasses 133 usado para implementar ABSTRACT FACTORY 99 sistemas de janelas (window systems) 49 suporte para, no Lexi 63 Sketchpad 129 SkipList 157c, 251 skiplist 245 Smalltalk/V uso de INTERPRETER 239 uso de MEDIATOR 262, 264 Smalltalk-240, ver tambm ObjectWorks\Smalltalk, Smalltalk/V uso de BUILDER 111 uso de COMPOSITE 169 uso de FACTORY METHOD 120 uso de INTERPRETER 239 uso de ITERATOR 256 uso de OBSERVER 283 uso de SINGLETON 136 uso de VISITOR 317 smart pointers 200 smart references 200 solicitao 27, 333 encapsulao de uma 71, ver tambm COMMAND recepo garantida de uma 215 repasse (forwarding) automtico de 218 representao 216 seqenciao 223 Solitaire, ver SINGLETON SolverState 266-267 SPECtalk uso de INTERPRETER 243 SpellingChecker 81-83 SpellingCheckerVisitor 85 StandardMazeBuilder 110 State participante de STATE 285, 285c STATE (ESTADO) 284 idioma (idiom) C++ para, ver idioma envelopeletter no resumo do catlogo 25 uso da delegao no 37

RApp uso de STRATEGY 300 RealSubject participante de PROXY 200co, 201 Receiver (receptor) participante de COMMAND 225, 225c, 226i, 227 recepo garantida da solicitao 215 receptor 333 desacoplamento do emissor 320 implcito 213 Rect 346 Rectangle 36, 36c refatorao 302, 325 refazer, ver desfazer/refazer referncias (contagem de) 201 RefinedAbstraction participante de BRIDGE 153c, 154 regulares (expresses regulares) 231 representando em Smalltalk 1861 RegularExpression 232c remoto (proxy), ver proxy, remoto repassando (forwarding) solicitaes 218 RepetitionExpression 232co, 317 implementada em Smalltalk 237 resoluo de restries 265, 266 ver tambm ThingLab, QOCA Responder 221 reutilizao black-box (de caixa preta) 34, 326, 332 da implementao 33 de cdigo 41 frameworks 41 interno 40 maximizando 38 por uso de subclasses 34 projetando para o 39-40 toolkits 41 usando composio 34 usando tipos parametrizados 37 white-box (de caixa branca ou aberta) 34, 326, 333 Rich Text Format 104 robusto (iterador), ver iterador, robusto Room 92c, 93 RTF, ver Rich Text Format RTFReader 104, 104c

S Scrollbar 62c ScrollDecorator 171o, 171c Scroller 58, 59o Self 20, 125, 287 SequenceExpression 232, 317 implementada em Smalltalk 237

NDICE
Strategy 175o participante de STRATEGY 294c, 294 tornando opcional 297 STRATEGY 293 comparado com DECORATOR 174 no resumo do catlogo 24 uso da delegao em 37 uso em Choices 187 uso em Model/View/Controller 22 uso pelo Lexi 55 strategy 293 Stream 178, 178c StreamDecorator 178, 178c Stroustrup, Bjarne 159 subclasse, ver classe, subclasse subject 275 mapeando para observers 278 observando mais de um 278 Subject 281 combinando com Observer 280 evitando referncias vazias para 278 participante de OBSERVER 275c, 276, 276i, 280c, 320 i participante de PROXY 200co, 201 subsistema 333 simplificando a interface para, ver FAADE subtipo, ver tipo, subtipo sucessor 213 conectando cadeia 216 implementando uma cadeia de 214 superclasse 333, ver tambm classe-pai/me supertipo, ver tipo, supertipo Surrogate, ver PROXY Sutherland, Ivan 129 swaps (em finanas) 300

363

T Target participante de ADAPTER 142, 142c TCP (protocolo) 287 TCPConnection 284, 284c, 287 TCPState 284, 284c, 288 template 37, ver tambm tipos parametrizados usado para implementar COMMAND 228 usado para implementar FACTORY METHOD 118 usado para implementar STRATEGY 296, 300 TEMPLATE METHOD (MTODO TEMPLATE) 301 chamando Factory Methods 120 convenes de nomenclatura para 304 no resumo do catlogo 25 usado para implementar ITERATOR 256 TerminalExpression participante de INTERPRETER 234c, 235 TEX 55, 104, 294 TeXCompositor 55c, 293, 293c, 299 TextShape 140, 141c, 147, 148

TextView uso em ADAPTER 140, 141c, 147 uso em DECORATOR 171c, 171o ThingLab 129 THINK uso de COMMAND 230 uso de OBSERVER 283 tipo 28 comparado com classe 31 C++, definio em 32 Eiffel, definio em 32 Smalltalk, definio em 32 subtipo 28 supertipo 28 verificao de 307 para identificar solicitaes em tempo de execuo 217 ver tambm dinaymic_cast tipos parametrizados 37, 333, ver tambm template comparados com composio 37 comparados com herana 37 token, mgico 320 Token, ver MEMENTO Tool 121, 122c, 291, 291c toolkit 41, 222, 333 ver Andrew ver Booch Components ver Fresco ver InterViews ver IRIS Inventor ver Object Windows ver QOCA ver THINK transao 225 Transaction, ver COMMAND transies de estados atmicas 286 definindo 286 usando tabelas 286 TreeAcessorDelegate 146, 146c TreeDisplay 143, 145, 145c, 146c two-way adapter, ver adapter (adaptador), twoway TypeCheckingVisitor 307c

U Unidraw uso de ADAPTER 144 uso de CHAIN OF RESPONSIBILITY 221 uso de COMMAND 221, 230 uso de FACTORY METHOD 116 uso de ITERATOR 256 uso de MEDIATOR 265 uso de MEMENTO 272 uso de OBSERVER 283 uso de PROTOTYPE 129 uso de STATE 291

364

NDICE
W Wall 92c, 93 white-box (reutilizao de caixa branca ou aberta), ver reutilizao, white-box Widget 213c, 219, 259c, 262 widget 61, 96 hierarquia de Glyph 62 WidgetFactory 96 Window 36c, 52, 66c, 67c, 152, 155 configurando com WindowImp 69-70 interface 65 WindowImp 67, 67c, 152, 152c, 156 subclasses 67 Windows, ver Microsoft Windows WindowSystemFactory 69 Wrapper, ver ADAPTER, DECORATOR WYSIWYG 47

UnsharedConcreteFlyweight participante de FLYWEIGHT 191 uso de subclasses estendendo a funcionalidade pelo uso de 39

V Validator 300 VariableExp 240 VariableRefNode 307c verificao ortogrfica 74 ViewManager 264, 264o Virtual Constructor (ConstrutorVirtual), ver FACTORY METHOD visitor (visitante) 84, 307 VISITOR 306 combinado com INTERPRETER 236, 243 diagrama de interao para o Lexi 84 no resumo do catlogo 25 uso da delegao em 37 uso em exemplo de compilador 184, 306 uso em Lexi 86 Visitor 85, 311 participante de VISITOR 308, 308c VisualComponent 171, 171c, 175 vocabulrio comum 324

X X Window System 64, 68, 69, 151, 157 XIconWindow 151, 151c XWindow 151, 151c XWindowImp 152, 152c, 157

Padres de Criao
Abstract Factory (95): Fornece uma interface para criao de famlias de objetos relacionados ou dependentes sem especificar suas classes concretas. Builder (104): Separa a construo de um objeto complexo da sua representao, de modo que o mesmo processo de construo possa criar diferentes representaes. Factory Method (112): Define uma interface para criar um objeto, mas deixa as subclasses decidirem qual classe a ser instanciada. O Factory Method permite a uma classe postergar (defer) a instanciao s subclasses. Prototype (121): Especifica os tipos de objetos a serem criados usando uma instncia prototpica e criar novos objetos copiando este prottipo. Singleton (130): Garante que uma classe tenha somente uma instncia e fornecer um ponto global de acesso para ela.

Padres Estruturais
Adapter (140): Converte a interface de uma classe em outra interface esperada pelos clientes. O Adapter permite que certas classes trabalhem em conjunto, pois de outra forma seria impossvel por causa de suas interfaces incompatveis. Bridge (151): Separa uma abstrao da sua implementao, de modo que as duas possam variar independentemente. Composite (160): Compe objetos em estrutura de rvore para representar hierarquias do tipo partes-todo. O Composite permite que os clientes tratem objetos individuais e composies de objetos de maneira uniforme. Decorator (170): Atribui responsabilidades adicionais a um objeto dinamicamente. Os decorators fornecem uma alternativa flexvel a subclasses para extenso da funcionalidade. Faade (179): Fornece uma interface unificada para um conjunto de interfaces em um subsistema. O Faade define uma interface de nvel mais alto que torna o subsistema mais fcil de usar. Flyweight (187): Usa compartilhamento para suportar grandes quantidades de objetos, de granularidade fina, de maneira eficiente. Proxy (198): Fornece um objeto representante (surrogate), ou um marcador de outro objeto, para controlar o acesso ao mesmo.

Padres Comportamentais
Chain of Responsibility (212): Evita o acoplamento do remetente de uma solicitao ao seu destinatrio, dando a mais de um objeto a chance de tratar a solicitao. Encadeia os objetos receptores e passa a solicitao ao longo da cadeia at que um objeto a trate. Command (222): Encapsula uma solicitao como um objeto, desta forma permitindo que voc parametrize clientes com diferentes solicitaes, enfileire ou registre (log) solicitaes e suporte operaes que podem ser desfeitas. Interpreter (231): Dada uma linguagem, define uma representao para sua gramtica juntamente com um interpretador que usa a representao para interpretar sentenas nesta linguagem. Iterator (244): Fornece uma maneira de acessar seqencialmente os elementos de um objeto agregado sem expor sua representao subjacente. Mediator (257): Define um objeto que encapsula como um conjunto de objetos interage. O Mediator promove o acoplamento fraco ao evitar que os objetos se refiram explicitamente uns aos outros, permitindo que voc varie suas interaes independentemente. Memento (266): Sem violar a encapsulao, captura e externaliza um estado interno de um objeto, de modo que o mesmo possa posteriormente ser restaurado para este estado. Observer (274): Define uma dependncia um-para-muitos entre objetos, de modo que, quando um objeto muda de estado, todos os seus dependentes so automaticamente notificados e atualizados. State (284): Permite que um objeto altere seu comportamento quando seu estado interno muda. O objeto parecer ter mudado sua classe. Strategy (292): Define uma famlia de algoritmos, encapsular cada um deles e faz-los intercambiveis. O Strategy permite que o algoritmo varie independentemente dos clientes que o utilizam. Template Method (301): Define o esqueleto de um algoritmo em uma operao, postergando a definio de alguns passos para subclasses. O Template Method permite que as subclasses redefinam certos passos de um algoritmo sem mudar sua estrutura. Visitor (305): Representa uma operao a ser executada sobre os elementos da estrutura de um objeto. O Visitor permite que voc defina uma nova operao sem mudar as classes dos elementos sobre os quais opera.

referncia a objeto

AbstractClass AbstractOperation()

uma muitas

agregao cria

ConcreteClass

ConcreteSubclass1 Operation() instanceVariable

ConcreteSubclass2 pseudocdigo de implementao

Notao de diagrama de classe

anObject objectReference

anotherObject instanceVariable

Notao de diagrama de objeto

anObject

anotherObject

operao ativa em anObject

new Object
(instanciao)

tempo

Operation()

DoOperation()
(Solicita operao a si mesmo)

Notao de diagrama de interao

Memento Builder
salvando o estado da iterao

Proxy Adapter

criando compostos

Iterator

evitando histerese

Bridge

acrescentando responsabilidades a objetos

enumerando filhos

usando composto

Command

Composite Decorator
compartilhando compostos definindo percursos definindo a cadeia

adicionando operaes definindo a gramtica

Flyweight
mudando o exterior versus o interior compartilhando estratgias

Visitor

Interpreter

adicionando operaes

Chain of Responsibility

Strategy

compartilhando smbolos terminais compartilhando estados

Mediator

administrao de dependncias complexas

Observer

definindo os passos do algoritmo

State Template Method


usos freqentes

Prototype
configurar a fbrica dinamicamente

Factory Method
implementa usando

Abstract Factory
instncia nica instncia nica

Facade

Singleton

Relacionamentos entre padres de projeto