View
189
Download
0
Category
Preview:
Citation preview
PÓS-GRADUAÇÃO EM ANÁLISE E GESTÃO DE SISTEMAS DE INFORMAÇÃO
DÁVISSON HÚDSON CHAVES BERNADETE EDUARDO COELHO CARNEIRO FILIPE ARANTES FERNANDES
PADRÕES DE PROJETO (DESIGN PATTERNS)
Trabalho da Disciplina de Reutilização de Software
Campos do Goytacazes, RJ
2013
RESUMO
Padrões de projeto são padrões que visam estruturar soluções para problemas antes encontrados em projetos de software, facilitando assim a criação de novos projetos já que apresentam soluções eficazes para problemas conhecidos. Padrões de projetos não são de fácil aplicação quando não se domina os conceitos que envolvem orientação a objetos, porém tornam-se de grande ajuda para desenvolvedores e projetistas mais experientes, facilitando bastante o trabalho. Padrões de projetos podem ser classificados de forma geral como de criação, estrutural e comportamental, cada tipo atua de forma diferente no tratamento dos objetos e classes. Padrões de projetos tendem a maximizar a qualidade do projeto, tornando-os mais flexíveis e possibilitando um melhor reuso de código. Os padrões de projeto mais conhecidos e utilizados são os 23 padrões do grupo conhecido como GoF (Gang of Four), e com base principal no trabalho deles, o presente trabalho aborda os padrões Abstract Factory, Decorator, Composite, Chain of Responsibility, Observer, Strategy, Template Method e Visitor com exemplos de código e explicações.
LISTA DE FIGURAS
Figura 1. Os 23 Padrões GoF (PEREIRA, 2013)............................................................................. 14
Figura 2. Estrutura do Padrão Abstract Factory (GAMMA et al., 2000) ...................................... 18
Figura 3. Diagrama de classe fábrica de carros (BRIZENO, 2011) ............................................... 20
Figura 4. Estrutura padrão do Composite ................................................................................... 23
Figura 5. Exemplo de implementação do padrão Composite ..................................................... 24
Figura 6. Estrutura do padrão Decorator .................................................................................... 29
Figura 7. Exemplo de implementação do padrão Decorator ...................................................... 30
Figura 8. Estrutura do Chain of Responsability ........................................................................... 34
Figura 9. Diagrama de classes do exemplo do padrão Chain of Responsability ......................... 37
Figura 10. Estrutura do padrão Observer (GAMMA et al, 2000). ............................................... 39
Figura 11. Diagrama de sequência entre um subject e dois observadores (GAMMA et al, 2000).
..................................................................................................................................................... 40
Figura 12. Diagrama de classes da estação meteorológica ......................................................... 41
Figura 13. Estrutura do Padrão Strategy (GAMMA et al., 2000). ................................................ 46
Figura 14. Diagrama de classe do exemplo do padrão Visitor. ................................................... 50
Figura 15. Diagrama de sequência do padrão Visitor ................................................................. 53
Figura 16. Diagrama de classes do exemplo de Visitor. .............................................................. 54
LISTA DE BLOCOS
Bloco 1. Interface FabricaDeCarro .............................................................................................. 20
Bloco 2. Classe FabricaFiat .......................................................................................................... 20
Bloco 3. Interfaces CarroPopular e CarroSedan .......................................................................... 21
Bloco 4. Implementação das interfaces CarroPopular e CarroSedan ......................................... 21
Bloco 5. Classe de teste da Fábrica de Carros ............................................................................. 22
Bloco 6. Implementação da classe MenuComponent ................................................................ 25
Bloco 7. Implementação da classe MenuItem ............................................................................ 26
Bloco 8. Implementação da classe Waitress ............................................................................... 26
Bloco 9. Implementação da classe MenuItem ............................................................................ 27
Bloco 10. Implementação da classe MenuTestDrive .................................................................. 28
Bloco 11. Implementação da classe Beverage ............................................................................ 31
Bloco 12. Implementação das classes dos condimentos ............................................................ 32
Bloco 13. Implementação da classe StarbuzzCoffee ................................................................... 33
Bloco 14. Implementação da enumeração de IDBancos ............................................................ 34
Bloco 15. Implementação da classe BancoChain ........................................................................ 35
Bloco 16. Implementação do banco concreto ............................................................................ 36
Bloco 17. Implemantação do padrão Chain of Responsability ................................................... 36
Bloco 18. Interfaces Subject, Observer e DisplayElement .......................................................... 42
Bloco 19. Classe WeatherData .................................................................................................... 43
Bloco 20. Implemnetação das interfaces Observer e DisplayElement em
CurrentConditionDisplay. ............................................................................................................ 44
Bloco 21. Criação do objeto weatherData e as 3 exibições ........................................................ 44
Bloco 22. Interface CalculaImposto ............................................................................................ 47
Bloco 23. Implementaação do imposto de 20% ou 15%. ............................................................ 48
Bloco 24. Método construtor de Funcionario. ............................................................................ 48
Bloco 25. Classe Usuario. ............................................................................................................ 50
Bloco 26. Classe AbstractLogin .................................................................................................... 51
Bloco 27. Classes LoginPorArquivo e LoginPorBD ....................................................................... 52
Bloco 28. Interfaces Visitable e Visitor. ....................................................................................... 55
Bloco 29. Classe abstrata Place ................................................................................................... 55
Bloco 30. Implementação de Visitable na classe City. ................................................................ 55
Bloco 31. Classes Museum e Park. .............................................................................................. 56
Bloco 32. Classe FirstTimeVisitor. ............................................................................................... 57
Bloco 33. Classe JobSeekerVisitor. .............................................................................................. 57
Sumário
1. INTRODUÇÃO .................................................................................................................... 7
1.1. MOTIVAÇÃO E JUSTIFICATIVA ............................................................................. 7
1.2. OBJETIVOS ................................................................................................................. 8
1.3. ORGANIZAÇÃO DO TRABALHO ............................................................................ 8
2. REFERENCIAL TEÓRICO ................................................................................................. 9
2.1. REUTILIZAÇÃO.......................................................................................................... 9
2.2. ENCAPSULAMENTO ................................................................................................. 9
2.3. HERANÇA, COMPOSIÇÃO, POLIMORFISMO E INTERFACE ........................... 10
2.4. MANUTENIBILIDADE E GRANULARIDADE ...................................................... 11
3. PADRÕES DE PROJETO (DESIGN PATTERNS) ........................................................... 12
3.1. PADRÕES DE CRIAÇÃO ......................................................................................... 14
3.2. PADRÕES ESTRUTURAIS ....................................................................................... 15
3.3. PADRÕES COMPORTAMENTAIS .......................................................................... 16
4. DESCREVENDO E EXEMPLIFICANDO ALGUNS PADRÕES .................................... 17
4.1. ABSTRACT FACTORY ............................................................................................ 17
4.1.1. Exemplo de Abstract Factory .............................................................................. 19
4.2. COMPOSITE .............................................................................................................. 22
4.2.1. Exemplo do Composite ........................................................................................... 24
4.3. DECORATOR ............................................................................................................ 29
4.3.1. Exemplo do Decorator ........................................................................................ 30
4.4. CHAIN OF RESPONSIBILITY ................................................................................. 33
4.4.1. Exemplo do Chain of Responsability ................................................................... 34
4.5. OBSERVER ................................................................................................................ 37
4.5.1. Exemplo de Observer .......................................................................................... 41
4.6. STRATEGY ................................................................................................................ 45
4.6.1. Exemplo de padrão Strategy ............................................................................... 47
4.7. TEMPLATE METHOD .............................................................................................. 49
4.7.1. Exemplo do Template Method ............................................................................ 49
4.8. VISITOR ..................................................................................................................... 53
4.8.1. Exemplo de Visitor .............................................................................................. 54
5. PADRÕES NÃO GOF ........................................................................................................ 58
5.1. SOA DESIGN PATTERNS ........................................................................................ 58
6. CONCLUSÕES ................................................................................................................... 59
6.1. CONTRIBUIÇÕES ..................................................................................................... 59
6.2. LIMITAÇÕES ............................................................................................................. 60
REFERÊNCIAS BIBLIOGRÁFICAS ........................................................................................ 61
7
1. INTRODUÇÃO
Padrões de projeto podem ser vistos como uma solução que já foi testada para
um problema, ou seja, geralmente descreve uma solução ou uma instância da solução
que foi utilizada para resolver um problema específico. Padrões de projetos são
soluções para problemas que alguém um dia teve e resolveu aplicando um modelo que
foi documentado e adaptado integralmente ou de acordo com a necessidade de uma
solução (MACORATTI, 2002).
Um padrão de projeto deve auxiliar e prever alterações de um projeto. Segundo
Freeman et al. (2004), alteração é a única coisa com a qual podemos contar sempre no
desenvolvimento de software, independentemente de como seja desenvolvido um
aplicativo, com o tempo ele precisa crescer e mudar para não morrer.
Os padrões de projeto nos dizem como resolver alguns problemas, porém não
dizem como adaptar esses projetos para adequá-los aos aplicativos, pois eles estão num
nível acima das bibliotecas (FREEMAN et al., 2004). Além disso, fornecem uma
linguagem compartilhada que pode maximizar o valor da comunicação entre
desenvolvedores.
1.1. MOTIVAÇÃO E JUSTIFICATIVA
Frequentemente desenvolvedores deparam-se com problemas que já foram
resolvidos em projetos anteriores, com isso surgiu a necessidade de se criar padrões
como uma ajuda na resolução de problemas baseados em soluções que já funcionavam
em projetos já desenvolvidos. Assim, o agrupamento dessas soluções tende a ajudar na
construção de projetos melhores e com maior manutenibilidade e granularidade.
8
1.2. OBJETIVOS
Este trabalho visa explorar os conceitos de Padrões de Projeto, bem como
apresentar exemplos de alguns padrões escolhidos. Este trabalho se baseia nos padrões
de Gamma et al. (2000), porém também são apresentados alguns padrões de projeto
diferentes da abordagem principal, não se restringindo necessariamente a uma única
linha de pesquisa.
1.3. ORGANIZAÇÃO DO TRABALHO
De forma geral o trabalho apresenta mais cinco capítulos além desta
introdução, conforme descrito a seguir.
No capítulo 2, é apresentado um referencial teórico, onde observa-se os
conceitos de reutilização de software, encapsulamento, herança, composição,
polimorfismo, interface, manutenibilidade e granularidade. Esses conceitos estão
presentes de forma intrínseca em um projeto de Padrão de Projetos e por isso ganharam
espaço no referencial teórico.
O capítulo 3 aborda de forma mais profunda questões sobre design patterns, o
que são e para que servem, as principais propriedades dos padrões de projeto,
apresentará os prós e os contras de se implantar um padrão de projeto em determinados
projetos, os componentes principais de um padrão de projeto, requisitos de um bom
sistema de padrões, e como se utilizar padrões de projeto.
No capítulo 4 alguns padrões foram escolhidos para exemplificar o estudo de
Design Patterns realizado, os exemplos foram feitos em Java na maior parte.
Alguns padrões não desenvolvidos ou agrupados por Gamma et al são
apresentados no capítulo 5, porém sem muita ênfase, apenas a título de informação.
No capítulo 6, e último, são apresentadas algumas conclusões obtidas durante o
desenvolvimento do trabalho.
9
2. REFERENCIAL TEÓRICO
2.1. REUTILIZAÇÃO
A Reutilização é uma atividade já realizada há muito tempo por
desenvolvedores e projetistas de software. Embora a mesma seja feita de uma maneira
ad hoc, ideias, objetos, argumentos e abstrações sempre foram reutilizados por
desenvolvedores de software (PRESSMAN, 2006).
A reutilização pode ser definida como o processo de criação de sistemas a
partir de software preexistente (KRUEGER, 1992), trazendo consigo a promessa de
aumentar a produtividade e diminuir custos de desenvolvimento, além de melhorar a
qualidade do produto, através de artefatos de software já testados e utilizados em outros
contextos (FRAKES e KANG, 2005).
A reutilização de software não é uma técnica nova, ela surgiu no final dos anos
60, porém, anteriormente, se reutilizava apenas bibliotecas de rotinas ou partes de
código em ambientes de programação, mas com a orientação a objeto (OO) passou-se a
utilizar as classes para encapsular dados e funções, aumentando a granularidade
(ODYSSEY, 2007). A reutilização representa uma subárea da Engenharia de Software,
esta busca melhorar a qualidade dos artefatos de software e diminuir o tempo e esforços
necessários para produzi-los, assim como a intenção da reutilização.
Em uma organização o desenvolvimento pode ser para ou com reutilização,
dependendo do interesse. O desenvolvimento de software para reutilização tem como
fim produzir os artefatos para que sejam reutilizados futuramente, e no desenvolvimento
de software com reutilização, artefatos são incorporados ao sistema (BERNADETE et
al., 2007).
2.2. ENCAPSULAMENTO
Encapsular, de forma geral, significa esconder os dados, ou seja, não deixar
campos expostos para serem manipulados diretamente a partir de código externo, o que
lavaria a violações da invariante de representação ou a dependências indesejáveis que
10
impedem a alteração da implementação (MACORATTI, 2002). Segundo Macoratti
(2002) devemos esconder alguns componentes, permitindo apenas acessos estilizados
ao objeto, porém o acesso indireto pode reduzir o desempenho.
O encapsulamento serve para controlar o acesso aos atributos e métodos de
uma classe, e também é uma forma eficiente de proteger os dados manipulados dentro
da classe, além de determinar onde esta classe poderá ser manipulada (LEMOS, 2013).
Então para ter um método encapsulado utilizamos um modificador de acesso que
geralmente é público, além do tipo de retorno dele (LEMOS, 2013).
Encapsular é fundamental para que o sistema seja suscetível a mudanças, assim
não é preciso mudar uma regra de negócio em vários lugares, mas sim em apenas um
único lugar, já que a regra está encapsulada (CAELUM, 2011).
2.3. HERANÇA, COMPOSIÇÃO, POLIMORFISMO E INTERFACE
As duas maneiras mais comuns para reutilização de funcionalidades em
sistemas orientados a objetos são herança de classe e composição de objetos, a herança
de classe permite definir a implementação de uma classe em termos da implementação
de outra, já a composição por objetos é uma alternativa á herança onde a nova
funcionalidade é obtida pela montagem ou composição de objetos para obter
funcionalidades mais complexas (GAMMA et al., 2000).
A herança é um mecanismo da Orientação a Objeto que permite criar novas
classes a partir de classes já existentes, aproveitando-se das características existentes na
classe a ser estendida (LEMOS, 2013). Com herança é possível criar classes derivadas,
subclasses e superclasses. Herança pode ser associada com o termo “é um”.
Composição estende uma classe e delega o trabalho para o objeto desta classe,
onde uma instância da classe existente é usada como componente da outra classe
(MACORATTI, 2011). Composição pode ser associada com o termo “tem um”.
Usando composição os objetos que foram instanciados e estão contidos na
classe que os instanciou são acessados somente através de sua interface, além disso,
uma composição deve ser definida dinamicamente em tempo de execução pela obtenção
de referência de objetos a objetos do mesmo tipo e apresenta uma menor dependência
de implementações (MACORATTI, 2011).
11
Uma interface nada mais é do que um bloco de código definindo um tipo e os
métodos e atributos que esse tipo deve possuir. Na prática o que acontece é que
qualquer classe que quiser ser do tipo definido pela interface deve implementar os
métodos dessa interface (LEMOS, 2013). Segundo a Caelum (2011), uma interface
pode ser vista como um “contrato”, onde quem assinar esse contrato é obrigado, nesse
caso, a implementar os métodos da interface, não se herda métodos e atributos, mas sim
responsabilidades. Uma interface pode herdar de mais de uma interface, o que pode nos
levar a um melhor uso do polimorfismo.
Polimorfismo é o princípio pelo qual duas ou mais classes derivadas de uma
mesma superclasse podem invocar métodos que têm a mesma identificação, assinatura,
mas comportamentos distintos, especializados para cada classe derivada (LEMOS,
2013). Polimorfismo é a capacidade de um objeto poder ser referenciado de várias
formas, porém isso não significa que o objeto fica se transformando, pelo contrário, um
objeto nasce de um tipo e morre do mesmo tipo, o que pode mudar é a maneira como
nos referimos a ele (CAELUM, 2011).
É sempre bom programar pensando na interface da classe, como seus usuários
a estarão utilizando, e não somente em como ela irá funcionar (GAMMA et al, 2000).
2.4. MANUTENIBILIDADE E GRANULARIDADE
Manutenibilidade é uma das características de qualidade de software,
determinando o grau de facilidade com que o mesmo pode ser corrigido ou
aperfeiçoado, assim um software com alto índice de manutenibilidade necessita de
menos tempo e pessoas para ser modificado (BRUSAMOLIN, 2004).
Manutenibilidade de software diz respeito à facilidade com que o mesmo pode
ser modificado para satisfazer requisitos do usuário ou ser corrigido quando deficiências
são detectadas (PIGOSKI, 1996).
Granularidade diz respeito ao nível de detalhamento da classe ou objeto, assim
quanto mais granular for um software, mais partes menores ele vai ter. Um sistema
granular tem um número maior de objetos com tarefas mais específicas, ao passo que
um sistema menos granular possui poucas classes, porém classes maiores, com mais
funcionalidades por exemplo.
12
Granularidade demais impacta no processamento e dificulta o
desenvolvimento, porém granularidade a menos deixa o código com pouca
manutenibilidade e menos coeso.
3. PADRÕES DE PROJETO (DESIGN PATTERNS)
Segundo Macoratti (2002), padrões para arquitetura de software são soluções
de eficiência já comprovadas e amplamente utilizadas para a resolução de problemas
comuns em projeto de software, que são desenvolvidas e conhecidas por especialistas e
tornam-se padrões por serem reutilizadas várias vezes em vários projetos, tendo eficácia
comprovada.
Padrões de projeto são descrições de objetos e classes comunicantes que
precisam ser personalizadas para resolver um problema geral de projeto num contexto
particular (GAMMA et al., 2000).
É importante observar que um padrão de projeto deve nomear, abstrair e
identificar os aspectos que dão base a uma estrutura de projeto comum para torná-la
reutilizável, focalizando um problema ou particularidade do projeto orientado a objetos.
A utilização de padrões possibilita uma maior coesão e minimização da
complexidade e do acoplamento entre os elementos que integram a aplicação
(PEREIRA, 2008).
Com um padrão de projeto é mais fácil reutilizar projetos e arquiteturas bem-
sucedidas, porém, somente devem ser considerados projetos que foram aplicados mais
de uma vez em diferentes sistemas, pois é necessário comprovar a eficiência e a eficácia
da solução para ser considerado um padrão de projeto, assim nenhum padrão de projeto
descreve projetos novos ou não testados.
Conhecer conceitos como abstração, herança e polimorfismo são importantes,
mas não suficientes para ser um bom projetista, é necessário criar projetos flexíveis que
sejam fáceis de manter e modificar (FREEMAN et al., 2004).
Segundo Gamma et al. (2000), um padrão tem quatro elementos essenciais:
nome, problema, solução e consequências. Eles descrevem esses elementos da seguinte
forma: o nome do padrão é uma referência que deve descrever um problema de projeto,
suas soluções e consequências em uma ou duas palavras; o problema descreve em que
13
situação aplicar o padrão, deve explicar o problema e seu contexto; a solução descreve
os elementos que compõem o padrão de projeto, seus relacionamentos, suas
responsabilidades e colaborações, o padrão fornece uma descrição abstrata de um
problema de projeto e como um arranjo geral de elementos o resolve; as consequências
são os resultados e análises das vantagens e desvantagens da aplicação do padrão.
Dentre as principais propriedades dos padrões de projetos podemos citar que
capturam o conhecimento e a experiência de especialistas em projetos de software,
auxiliam o projeto de arquiteturas mais complexas (MACORATTI, 2002), especificam
abstrações que estão acima do nível de classes ou objetos isolados ou de componentes e
definem um vocabulário comum para discussão de problemas e soluções de projeto
(GAMMA et al., 2000), facilitam a documentação e manutenção da arquitetura do
software e também auxiliam o projeto de uma arquitetura com determinadas
propriedades (BUSCHMANN et al., 1996).
Como benefícios da utilização de padrões podemos destacar, segundo
Macoratti (2002):
• Fornecem soluções que já foram testadas e aprovadas.
• Tornam o sistema mais fácil de entender e manter.
• Facilitam o desenvolvimento de módulos coesos.
• A comunicação entre os participantes do projeto fica mais eficiente.
Como desvantagens pode-se citar o aumento da complexidade de entendimento
se não houver grande conhecimento de conceitos OO por parte dos envolvidos, e
também por adicionar acessos indiretos ou ao aumentar a quantidade de código, contudo
podem aumentar a capacidade de compreensão ao melhorar a modularidade, separando
melhor os conceitos e simplificando a descrição (UNIVERSIA).
Os requisitos para um bom sistema de padrões segundo Macoratti (2002): o
sistema deve conter uma boa quantidade de padrões; a descrição do padrão de seguir um
formato padronizado; o sistema deve ser estruturado; o sistema deve mostrar o
relacionamento entre os padrões e sua estrutura deve permitir evolução.
Para se utilizar padrões devemos ter bom senso, não adianta sair
implementando padrões sem ter certeza do porque utilizá-los, então o melhor é
implementar a solução e verificar se ela funciona para assim poder refatorá-la com os
padrões identificados para a melhoria e correção das deficiências do projeto.
14
Os padrões criados por Gamma et al. (2000) são os mais conhecidos
atualmente, e de acordo com eles os padrões podem ser de 3 tipos: padrões de criação,
estruturais e comportamentais. A figura 1 mostra a divisão dos padrões.
Figura 1. Os 23 Padrões GoF (PEREIRA, 2013)
3.1. PADRÕES DE CRIAÇÃO
Os padrões de criação abstraem o processo de instanciação, ajudam a tornar um
sistema independente de como seus objetos são criados, compostos e representados
(GAMMA et al., 2000).
Todos os padrões de criação encapsulam conhecimento sobre quais classes
concretas são usadas pelo sistema, e todas ocultam o modo como as instâncias destas
classes são criadas e compostas, a única coisa que o sistema sabe é que suas classes são
definidas por classes abstratas (GAMMA et al., 2000). Porém um padrão de criação dá
muita flexibilidade ao que, como e quando é criado e a quem cria (GAMMA et al.,
2000).
Uma descrição geral dos padrões de criação é apresentada a seguir.
Abstract Factory – Fornece uma interface para criação de famílias de objetos
relacionadas ou dependentes sem especificar suas classes concretas.
Builder – Separa a construção de um objeto complexo da sua representação, de
forma que o mesmo processo de construção possa criar diferentes representações.
15
Factory Method – Define uma interface para instanciação de objetos, mas deixa
as subclasses decidirem qual classe deve ser instanciada.
Prototype – Especifica os tipos de objetos a serem criados usando uma
instância prototípica e cria novos objetos copiando esse protótipo.
Singleton – Garante que para uma classe específica só possa existir uma única
instância, fornecendo um ponto global de acesso para ela.
3.2. PADRÕES ESTRUTURAIS
Os padrões estruturais se preocupam com a forma como classes e objetos são
compostos para formar estruturas maiores, eles utilizam herança para compor interfaces
ou implementações (GAMMA et al., 2000). Este tipo de padrão é bastante útil para
fazer bibliotecas de classes independentes trabalharem juntas.
Nesse padrão os padrões estruturais de objetos descrevem maneiras de compor
objetos para obter novas funcionalidades, assim a flexibilidade obtida pela composição
de objetos provém da capacidade de mudar a composição em tempo de execução
(GAMMA et al., 2000).
Uma descrição geral dos padrões estruturais é apresentada a seguir.
Adapter – Converte a interface de uma classe em outra interface esperada pelos
clientes. Permite que dois objetos se comuniquem mesmo que tenham interfaces
incompatíveis.
Bridge – Separa uma abstração de sua implementação, de forma que as duas
possam variar independentemente. Oculta detalhes de implementação dos clientes.
Composite – Permite que os clientes tratem objetos individuais e composições
de objetos de maneira uniforme. Ele lida com uma estrutura de elementos agrupada
hierarquicamente.
Decorator – Atribui responsabilidades adicionais a um objeto dinamicamente.
Ele fornece uma alternativa flexível a subclasses para a extensão da funcionalidade.
Facade – Fornece uma interface unificada para u conjunto de interfaces em um
subsistema, tornando o subsistema mais fácil de usar.
Flyweight – Usa compartilhamento para dar suporte a vários objetos de forma
eficiente.
16
Proxy – Fornece um objeto representante ou procurador de outro objeto para
controlar o acesso ao mesmo.
3.3. PADRÕES COMPORTAMENTAIS
Este tipo de padrão se preocupa com algoritmos e a atribuição de
responsabilidade entre objetos, eles não descrevem somente padrões de objetos ou
classes, mas também os padrões de comunicação entre eles, contudo, caracterizam
fluxos de controle difíceis de seguir em tempo de execução (GAMMA et al., 2000).
Padrões comportamentais de classe utilizam herança para distribuir o
comportamento entre as classes enquanto os padrões comportamentais de objetos
utilizam composição de objetos (GAMMA et al., 2000).
Uma descrição geral dos padrões comportamentais é apresentada a seguir.
Chain of Responsibility – Evita dependência do remetente (cliente) de uma
requisição ao seu destinatário, dando a oportunidade de mais de um objeto tratar a
requisição. Ele encadeia os objetos receptores e passa a solicitação ao longo da cadeia
até que um objeto a trate.
Command – Encapsula uma solicitação como um objeto, permitindo
parametrizar clientes com diferentes solicitações, enfileirar ou registrar solicitações e
suportar operações que podem ser desfeitas.
Interpreter – Dada uma linguagem, define-se uma representação para sua
gramática juntamente com um interpretador que usa a representação para interpretar
sentenças nessa linguagem.
Iterator – Provê uma forma de percorrermos os elementos de uma coleção sem
violar o seu encapsulamento.
Mediator – Cria um objeto que age como um mediador controlando a interação
entre um conjunto de objetos, ou seja, encapsula a forma como um conjunto de objetos
interage.
Memento – Torna possível salvar o estado de um objeto de modo que o mesmo
possa ser restaurado, sem violar o encapsulamento.
Observer – Define uma relação de dependência 1:N de forma que quando um
certo objeto (assunto) tem seu estado modificado os demais (observadores) são
17
notificados e atualizados. Possibilita baixo acoplamento entre os objetos observadores e
o assunto.
State – Permite a um objeto alterar seu comportamento quando estado interno
muda.
Strategy – Define uma família de algoritmos, encapsula cada um deles e os
torna intercambiáveis. Permite que o algoritmo varie independentemente dos clientes
que o utilizam.
Template Method – Define o esqueleto de um algoritmo em uma operação
adiando a definição de alguns passos para as subclasses. Ele permite que as subclasses
redefinam certos passos de um algoritmo sem mudar a sua estrutura.
Visitor – Define operações independentes a serem realizadas sobre elementos
de uma estrutura.
4. DESCREVENDO E EXEMPLIFICANDO ALGUNS PADRÕES
Este capítulo apresenta alguns padrões escolhidos para exemplificar o estudo
de Design Patterns realizado. Os exemplos são exibidos em Java e/ou Python.
4.1. ABSTRACT FACTORY
Criar objetos é mais do que simplesmente usar o operador new, criar instâncias
nem sempre deve ser feito de forma pública, isso pode gerar problemas de ligação
(FREEMAN et al., 2004). Os padrões Factory podem ajudar nessa situação. As
factories (fábricas) cuidam dos detalhes da criação dos objetos.
Segundo Gamma et al. (2000), o padrão Abstract Factory, também conhecido
como kit, fornece uma interface para criação de famílias de objetos relacionados ou
dependentes sem especificar suas classes concretas.
Abstract Factory permite que um cliente use uma interface abstrata para criar
um conjunto de produtos relacionados se saber, ou importar-se, sobre os produtos
concretos que são realmente produzidos, assim o cliente é desvinculado de qualquer
especificação dos produtos concretos (FREEMAN
Segundo Gamma et al
• Um sistema deve ser independente de como seus produtos
compostos ou representados;
• Um sistema deve ser configurado como um produto de uma família de
múltiplos produtos;
• Uma família de objetos
conjunto, e for necessário garantir essa restrição;
• Intende-se fo
revelar-se somente suas interfaces, não suas implementações.
Baseado em Gamma
Figura 2. Estrutura do Padrão Abstract
Assim temos os participantes de acordo
compilação de Gamma et al
• AbstractFactory
objetos-produto abstratos
concretas devem implementar, o que consiste em um conjunto de
métodos para fabricar produtos;
• Concrete Factory
concretos.
que são realmente produzidos, assim o cliente é desvinculado de qualquer
especificação dos produtos concretos (FREEMAN et al., 2004).
et al. (2000), este padrão deve ser aplicado quando:
Um sistema deve ser independente de como seus produtos
compostos ou representados;
Um sistema deve ser configurado como um produto de uma família de
múltiplos produtos;
Uma família de objetos-produto for projetada para ser usada em
conjunto, e for necessário garantir essa restrição;
se fornecer uma biblioteca de classes de produtos e quer
se somente suas interfaces, não suas implementações.
Baseado em Gamma et al. (2000) a estrutura do padrão é apresentada a seguir:
. Estrutura do Padrão Abstract Factory (GAMMA et al., 2000)
Assim temos os participantes de acordo da figura 2 explicitados segundo uma
et al. (2000) e Freeman et al. (2004):
Factory – Declara uma interface para operações que criam
produto abstratos. Define a interface que todas as fábricas
concretas devem implementar, o que consiste em um conjunto de
métodos para fabricar produtos;
Concrete Factory – Implementa as operações que criam objetos
As fábricas concretas implementam as diferentes famílias de
18
que são realmente produzidos, assim o cliente é desvinculado de qualquer
. (2000), este padrão deve ser aplicado quando:
Um sistema deve ser independente de como seus produtos são criados,
Um sistema deve ser configurado como um produto de uma família de
produto for projetada para ser usada em
rnecer uma biblioteca de classes de produtos e quer
se somente suas interfaces, não suas implementações.
. (2000) a estrutura do padrão é apresentada a seguir:
explicitados segundo uma
Declara uma interface para operações que criam
. Define a interface que todas as fábricas
concretas devem implementar, o que consiste em um conjunto de
mplementa as operações que criam objetos-produto
ferentes famílias de
19
produtos, e para criar um produto o cliente usa uma dessas fábricas,
assim nunca precisa criar a instância do objeto de um produto;
• AbstractProduct – Declara uma interface para um tipo de objeto-
produto. É a família de produtos onde cada fábrica concreta pode
produzir um conjunto inteiro de produtos;
• ConcreteProduct – Define um objeto-produto a ser criado pela
correspondente fábrica concreta. Também implementa a interface de
Abstract Product.
• Client – Usa somente interfaces declaradas pelas classes Abstract
Factory e Abstract Product. Em suma, é escrita para a Abstract Factory
e, depois, composta no tempo de execução com a Abstract Product.
Uma única instância de uma classe ConcreteFactory é criada em tempo de
execução normalmente. Para criar diferentes objetos-produto, os clientes deveriam usar
fábricas concretas diferentes (GAMMA et al., 2000).
Segundo Freeman et al. (2000), geralmente os métodos de uma Abstract
Factory são implementados como Factory Methods. O trabalho de uma Abstract Factory
é definir uma interface para criar um conjunto de produtos, e cada método nessa
interface é responsável pela criação de um produto concreto, assim implementamos uma
subclasse da Abstract Factory para fornecer essas implementações, dessa forma
percebe-se que os Factory Methods são uma forma natural de implementar métodos
concretos nas Abstract Factory.
As classes Abstract Factory são frequentemente implementadas com Factory
Methods, mas elas também podem ser implementadas usando Prototype, além disso,
uma fábrica concreta é frequentemente um Singleton (GAMMA et al., 2000).
As consequências de se usar esse padrão são apontadas por Gamma et al.
(2000): Isolamento das classes concretas; fácil troca de famílias de produtos; promove
harmonia entre produtos; difícil de suportar novos tipos de produtos.
4.1.1. Exemplo de Abstract Factory
Para Abstract Factory utilizou-se o exemplo de Brizeno (2011), que consiste na
representação de um sistema que, dado um conjunto de carros devemos manipulá-los.
Deve ser feito um agrupamento dos carros em conjuntos, de forma a agrupar objetos
que tenham comportamentos parecidos.
Para exemplificar os objetos foram organizados da seguinte forma:
Sedan (Siena – Fiat
Popular (Palio – Fiat
Assim o que deve ser feito é agrupar
conjunto de carros Popular para cada uma das fábricas.
do projeto de fábricas de carros Sedan e Popular.
Figura 3. Diagrama de c
O bloco 1 mostra uma interface para criação de Factories. Cada fábrica cria um
objeto de cada tipo, nesse caso um método para carros Sedan e outro para carros
Populares.
public class FabricaFiat
@Override
public CarroSedan criarCarroSedan() {
return new Siena();
}
@Override
public CarroPopular criarCarroPopular(
return new Palio();
}
public interface FabricaDeCarro {
CarroSedan criarCarroSedan();
CarroPopular criarCarroPopular();
}
agrupamento dos carros em conjuntos, de forma a agrupar objetos
que tenham comportamentos parecidos.
Para exemplificar os objetos foram organizados da seguinte forma:
Fiat, Fiesta Sedan – Ford);
Fiat, Fiesta – Ford).
o que deve ser feito é agrupar um conjunto de carros S
conjunto de carros Popular para cada uma das fábricas. A figura 3 apresenta a estrutura
do projeto de fábricas de carros Sedan e Popular.
. Diagrama de classe fábrica de carros (BRIZENO, 2011)
mostra uma interface para criação de Factories. Cada fábrica cria um
objeto de cada tipo, nesse caso um método para carros Sedan e outro para carros
FabricaFiat implements FabricaDeCarro {
CarroSedan criarCarroSedan() {
Siena();
CarroPopular criarCarroPopular() {
Palio();
FabricaDeCarro {
CarroSedan criarCarroSedan();
CarroPopular criarCarroPopular();
Bloco 1. Interface FabricaDeCarro
Bloco 2. Classe FabricaFiat
20
agrupamento dos carros em conjuntos, de forma a agrupar objetos
Para exemplificar os objetos foram organizados da seguinte forma:
um conjunto de carros Sedan e outro
apresenta a estrutura
mostra uma interface para criação de Factories. Cada fábrica cria um
objeto de cada tipo, nesse caso um método para carros Sedan e outro para carros
21
O bloco 2 cria os carros de fato, instanciando-os.
Todas as outras fábricas necessitam implementar a interface FabricaDeCarro.
A seguir a criação de mais duas interfaces:
Os dois métodos apresentados nas interfaces do bloco 3 exibem as informações
de seus tipos de carro, e apesar dos métodos executarem a mesma operação poderíamos
supor que os carros populares estão em um banco de dados e os sedans em outro, assim
cada método precisa criar sua própria conexão.
O bloco 4 apresenta os produtos concretos implementando suas interfaces.
public class Palio implements CarroPopular { @Override public void exibirInfoPopular() { System.out.println("Modelo: Palio\nFábrica: Fiat\nCategoria:Popular");
} } public class Siena implements CarroSedan { @Override public void exibirInfoSedan() { System.out.println("Modelo: Siena\nFábrica: Fiat\nCategoria:Sedan"); } }
public interface CarroPopular {
void exibirInfoPopular();
}
public interface CarroSedan {
void exibirInfoSedan();
}
Bloco 3. Interfaces CarroPopular e CarroSedan
Bloco 4. Implementação das interfaces CarroPopular e CarroSedan
22
Analisando o bloco 5 percebe-se que foi criada uma fábrica abstrata e foi
colocado nela qualquer fábrica, de acordo com a necessidade, e de forma semelhante foi
criada referências para um carro Popular e para um carro Sedan, e ainda de acordo com
nossas necessidades foram sendo utilizados os carros dos fabricantes.
Segundo Brizeno (2011) com esse padrão criamos uma estrutura muito grande
de classes e interfaces para resolver o problema de criação de objetos, porém baseado
no princípio da Segregação de Interface isto é uma coisa boa, pois o código cliente fica
dependendo de interfaces simples e pequenas ao invés de depender de uma interface
grande e que nem todos os métodos seriam utilizados.
4.2. COMPOSITE
O Composite é um tipo de padrão estrutural e tem como objetivo permitir a
composição de objetos em estruturas de árvore para representar hierarquias parte-todo
possibilitando o tratamento de objetos individuais ou composições de objetos de modo
uniforme.
Segundo Gamma et al. (2000), Composite deve tratar objetos primitivos e
objetos recipientes de modo diferente, mesmo se na maior parte do tempo o usuário os
trata de forma idêntica. Ter que distinguir entre esses objetos torna a aplicação mais
complexa. O padrão Composite descreve como usar a composição recursiva de maneira
que os clientes não tenham que fazer essa distinção.
public static void main(String[] args) { FabricaDeCarro fabrica = new FabricaFiat(); CarroSedan sedan = fabrica.criarCarroSedan(); CarroPopular popular = fabrica.criarCarroPopular(); sedan.exibirInfoSedan(); System.out.println(); popular.exibirInfoPopular(); System.out.println();
fabrica = new FabricaFord(); sedan = fabrica.criarCarroSedan(); popular = fabrica.criarCarroPopular(); sedan.exibirInfoSedan(); System.out.println(); popular.exibirInfoPopular(); }
Bloco 5. Classe de teste da Fábrica de Carros
O segredo do padrão é a
representa tanto as primitivas
operações que todos os objetos compostos compartilham, tais como operações para
acessar e manipular seus filhos.
demonstrada na Figura 4,
para filhos e composições.
Será de fácil compreensão se olharmos a estrutura como uma árvore de cabeça
para baixo. Um nó pode ter filhos e também um nó que, por sua vez, pode ter outros
filhos e nós. Neste padrão,
sendo declaradas em Component
classe só deve ter uma responsabilidade, no qual o padrão
De acordo com Freeman (2004)
Responsabilidade Única e o troca pela transparência. O fato de o cliente lidar
uniformemente com os compostos e os nó
Contudo, como as operações estão sendo declaradas na classe
pouco de segurança, pois um cliente pode tentar fazer algo inadequado ou sem sentido
com um elemento. Isto é decisão de projeto; pode
direção, separando as responsabilidades em diferentes interfaces. Isto tornaria o projeto
seguro, no sentido de que quaisquer chamadas inadequadas de elementos seriam
detectadas por ocasião da compilação ou da execução. Poré
transparência, e o código necessitaria de usar condicionais e o operador
Portanto, esse é um típico caso de que se perde algo para obter algum benefício.
Um projetista deve ser guiado pelos princípios de projeto, mas deve f
no efeito que eles têm sobre os projetos. Alguma modelagem pode parecer desrespeitar
O segredo do padrão é a recursividade devido a uma classe abstrata que
representa tanto as primitivas como os seus recipientes. Esta classe abstrata declara
operações que todos os objetos compostos compartilham, tais como operações para
acessar e manipular seus filhos. Por exemplo, observando a estrutura padrão
, a classe Component declara métodos que são utilizados só
para filhos e composições.
Figura 4. Estrutura padrão do Composite
Será de fácil compreensão se olharmos a estrutura como uma árvore de cabeça
o. Um nó pode ter filhos e também um nó que, por sua vez, pode ter outros
, nota-se que Leaf herda operações de Composite
Component. Segundo princípios de orientação a objetos, uma
uma responsabilidade, no qual o padrão Composite não respeita.
De acordo com Freeman (2004), o padrão abre mão do princípio de projetos da
Responsabilidade Única e o troca pela transparência. O fato de o cliente lidar
com os compostos e os nós-folha é caracterizado transparência.
Contudo, como as operações estão sendo declaradas na classe Component
pouco de segurança, pois um cliente pode tentar fazer algo inadequado ou sem sentido
com um elemento. Isto é decisão de projeto; pode-se conduzir o processo em outra
direção, separando as responsabilidades em diferentes interfaces. Isto tornaria o projeto
seguro, no sentido de que quaisquer chamadas inadequadas de elementos seriam
detectadas por ocasião da compilação ou da execução. Porém, poderia perder a
transparência, e o código necessitaria de usar condicionais e o operador
Portanto, esse é um típico caso de que se perde algo para obter algum benefício.
deve ser guiado pelos princípios de projeto, mas deve ficar atento sempre
no efeito que eles têm sobre os projetos. Alguma modelagem pode parecer desrespeitar
23
uma classe abstrata que
como os seus recipientes. Esta classe abstrata declara
operações que todos os objetos compostos compartilham, tais como operações para
observando a estrutura padrão
declara métodos que são utilizados só
Será de fácil compreensão se olharmos a estrutura como uma árvore de cabeça
o. Um nó pode ter filhos e também um nó que, por sua vez, pode ter outros
Composite que estão
Segundo princípios de orientação a objetos, uma
não respeita.
o padrão abre mão do princípio de projetos da
Responsabilidade Única e o troca pela transparência. O fato de o cliente lidar
é caracterizado transparência.
Component, perde-se um
pouco de segurança, pois um cliente pode tentar fazer algo inadequado ou sem sentido
se conduzir o processo em outra
direção, separando as responsabilidades em diferentes interfaces. Isto tornaria o projeto
seguro, no sentido de que quaisquer chamadas inadequadas de elementos seriam
m, poderia perder a
transparência, e o código necessitaria de usar condicionais e o operador instanceof.
Portanto, esse é um típico caso de que se perde algo para obter algum benefício.
icar atento sempre
no efeito que eles têm sobre os projetos. Alguma modelagem pode parecer desrespeitar
algum princípio, porém, tudo é uma questão de perspectiva. De acordo com Freeman
(2004), talvez pareça incorreto ter operações de gerenciamento de filho
mas pode-se deslocar um pouco a perspectiva e ver uma folha como um nó com zero
filhos.
4.2.1. Exemplo do CompositePara implementar este tipo de padrão, é
(2004) demonstrado na Figura
Figura 5
De forma resumida, a classe
na composição; implementa comportamento
classes, conforme apropriado; declara uma interface para acessar e gerenciar os seus
componentes-filhos e defin
estrutura recursiva e a implementa. Em
composição (um item de menu
menu em menu. Na classe
filhos; armazena os itens de menu e implementa as operações relacionadas com os
filhos presentes na interface de
os objetos na composição através da interface de
algum princípio, porém, tudo é uma questão de perspectiva. De acordo com Freeman
(2004), talvez pareça incorreto ter operações de gerenciamento de filho
se deslocar um pouco a perspectiva e ver uma folha como um nó com zero
Exemplo do Composite mentar este tipo de padrão, é abordado um exemplo de Freeman
Figura 5.
5. Exemplo de implementação do padrão Composite
De forma resumida, a classe MenuComponent declara a interface para os objetos
na composição; implementa comportamento-padrão para a interface comum a todas a
classes, conforme apropriado; declara uma interface para acessar e gerenciar os seus
define uma interface para acessar o pai de um componente na
estrutura recursiva e a implementa. Em MenuItem, representa objetos
um item de menu não tem filhos) e define comportamento para
Na classe Menu, define comportamento para componentes que têm
itens de menu e implementa as operações relacionadas com os
ce de MenuComponent. E Waitress é a classe que manipula
os objetos na composição através da interface de MenuComponent.
24
algum princípio, porém, tudo é uma questão de perspectiva. De acordo com Freeman
(2004), talvez pareça incorreto ter operações de gerenciamento de filhos nos nós-folha,
se deslocar um pouco a perspectiva e ver uma folha como um nó com zero
abordado um exemplo de Freeman
declara a interface para os objetos
padrão para a interface comum a todas as
classes, conforme apropriado; declara uma interface para acessar e gerenciar os seus
uma interface para acessar o pai de um componente na
objetos-folha na
comportamento para os itens do
, define comportamento para componentes que têm
itens de menu e implementa as operações relacionadas com os
é a classe que manipula
25
A implementação da classe MenuComponent é demonstrada no Bloco 6. É
nítido que esta classe só estabelece o contrato das operações deixando a cargo de suas
sub-classes a implementação de cada operação que lhe interessa.
Bloco 6. Implementação da classe MenuComponent
Em MenuItem, é implementado as operações que dizem respeito aos itens de
menu, ou seja, não é possível implementar a operação add, pois não faz sentido um item
de menu ter um filho, demonstrado no Bloco 7.
As operações referentes a um menu, são implementadas na classe Menu,
apresentada no Bloco 9. Menu é do tipo MenuComponent e que, por sua vez, pode ter
qualquer quantidade de filhos do tipo MenuComponents. Com a herança mais
composição é possível ter a recursividade desejada.
Tanto em MenuItem quanto Menu, possuem as mesmas operações, como por
exemplo print. Em MenuItem, basta somente imprimir o nome, se é um tipo de comida
vegetariana, o preço e a descrição. Já em Menu, a operação print deverá imprimir seu
public abstract class MenuComponent{ public void add(MenuComponent menuComponent){ throw new UnsupportedOperationException(); } public void remove(MenuComponent menuComponent){ throw new UnsupportedOperationException(); } public MenuComponent getChild(int i){ throw new UnsupportedOperationException(); } public String getName(){ throw new UnsupportedOperationException(); } public String getDescription(){ throw new UnsupportedOperationException(); } public double getPrice(){ throw new UnsupportedOperationException(); } public boolean isVegetarian(){ throw new UnsupportedOperationException(); } public void print(){ throw new UnsupportedOperationException(); } }
26
nome, descrição e o print implementado em MenuItem. Para que isso seja feito, foi
implementado o padrão Iterator. Ele busca todos os métodos print dos objetos do tipo
MenuComponent e executa.
Bloco 7. Implementação da classe MenuItem
A classe que interage com esta estrutura é Waitress e está apresentada no Bloco
8. Ela simplesmente executa o método print.
Bloco 8. Implementação da classe Waitress
public class Waitress{ MenuComponent allMenus; public Waitress(MenuComponent allMenus){ this.allMenus = allMenus; } public void printMenu(){ allMenus.print(); } }
public class MenuItem extends MenuComponent{ String name; String description; boolean vegetarian; double price; public MenuItem(String name, String description, boolean vegetarian, double price){ this.name = name; this.description = description; this.vegetarian = vegetarian; this.price = price; } public String getName(){ return name; } public String getDescription(){ return description; } public double getPrice(){ return price; } public boolean isVegetarian(){ return vegetarian; } public void print(){ System.out.print(" "+getName()); if(isVegetarian()){ System.out.print("(v)"); } System.out.println(", "+getPrice()); System.out.println(" -- "+getDescription()); } }
27
Bloco 9. Implementação da classe MenuItem
E por fim, a classe MenuTestDrive mostra um exemplo de como criar um menu
raiz e acrescentar seus menus, itens e as recursividades, caso necessário. Veja no Bloco
10. São criados os menus e também o menu raiz denominado allMenus, este objeto
representa toda a estrutura hierarquizada. Ao objeto allMenus, são adicionados outros
menus, sendo que estes menus serão adicionados itens e, no caso abaixo, o menu
dinerMenu possui um sub-menu dessertMenu, e que este possui um item nomeado
Apple Pie. Para exibir toda a estrutura é chamado o método print de Waitress.
public class MenuItem extends MenuComponent{ import java.util.ArrayList; import java.util.Iterator; public class Menu extends MenuComponent{ ArrayList<MenuComponent> menuComponents = new ArrayList<MenuComponent>(); String name; String description; public Menu(String name, String description){ this.name = name; this.description = description; } public void add(MenuComponent menuComponent){ menuComponents.add(menuComponent); } public void remove(MenuComponent menuComponent){ menuComponents.remove(menuComponent); } public MenuComponent getChild(int i){ return (MenuComponent)menuComponents.get(i); } public String getName(){ return name; } public String getDescription(){ return description; } public void print(){ System.out.print("\n"+getName()); System.out.println(", "+getDescription()); System.out.println("---------------------"); Iterator iterator = menuComponents.iterator(); while(iterator.hasNext()){ MenuComponent menuComponent = (MenuComponent)iterator.next(); menuComponent.print(); } } }
28
Bloco 10. Implementação da classe MenuTestDrive
Gamma et al.(2000) faz algumas considerações sobre este padrão.
• Hierarquias que consistem de objetos primitivos e compostos. Os
primitivos podem compor objetos mais complexos, os quais, por suas
vez, também podem compor outros objetos, e assim por diante
recursivamente. Sempre que o código esperar um objeto primitivo, ele
também poderá aceitar um objeto composto.
• Torna o cliente simples. Os clientes podem tratar estruturas compostas e
objetos individuas de maneira uniforme. Os clientes normalmente não
sabem (e não sabem deveriam se preocupar com isto) se estão tratando
com uma folha ou um componente composto. Isto simplifica o código a
ser escrito nas classes-cliente, porque evita o uso de comandos do tipo
CASE com os rótulos classes que definem composição.
• Torna mais fácil de acrescentar novas espécies de componentes. Novas
subclasses definidas, Composite ou Leaf, funcionam automaticamente
com as estruturas existentes e o código do cliente. Os clientes não
precisam ser alterados para tratar novas classes Component.
• Pode tornar o projeto excessivamente genérico. A desvantagem de
facilitar o acréscimo de novos componentes é que isso torna mais difícil
restringir os componentes de uma composição. Algumas vezes, é
public class MenuTestDrive{ public static void main(String args[]){ MenuComponent pancakeHouseMenu = new Menu("PANCAKE HOUSE MENU","Breakfast"); MenuComponent dinerMenu = new Menu("DINER MENU","Lunch"); MenuComponent cafeMenu = new Menu("CAFÉ MENU","Dinner"); MenuComponent dessertMenu = new Menu("DESSERT MENU","Dessert of course!"); MenuComponent allMenus = new Menu("ALL MENUS","All menus combined"); allMenus.add(pancakeHouseMenu); allMenus.add(dinerMenu); allMenus.add(cafeMenu); dinerMenu.add(new MenuItem("Pasta","Spaghetti with Marinara Sauce, and a slice of sourdough bread",true,3.89)); dinerMenu.add(dessertMenu); dessertMenu.add(new MenuItem("Apple Pie","Apple pie with a flakey crust, topped with vanilla ice cream",true,1.59)); Waitress waitress = new Waitress(allMenus); waitress.printMenu(); } }
desejável que uma composição tenha somente certos componentes. Com
Composite, não pode confiar no sistema de tipos para garantir a
obediência a essas restrições. Ao invés disso, deve
teste em tempo d
4.3. DECORATOR
Este padrão, de forma dinâmica, agrega valores adicionais em qualquer objeto.
Fornece alternativa flexível no uso de subclasses para estender funcionalidades.
De acordo com Gamma
responsabilidades a objetos individuais dinamicamente e sem afetar outros objetos.
Outra característica é que essas responsabilidades pode
de alguma forma, desempenho ou estrutura.
Decorar objetos é muito comum quando não se quer te
subclasses. Por exemplo, havendo a necessidade de combinar responsabilidades entre
classes em um total de 6, i
diferentes.
Segundo Freeman (2004), q
subclasses, este comportament
Todas as subclasses devem herdar o mesmo comportamento. Contudo, por meio da
composição será possível esten
tempo de execução.
O padrão segue a seguinte
desejável que uma composição tenha somente certos componentes. Com
, não pode confiar no sistema de tipos para garantir a
obediência a essas restrições. Ao invés disso, deve-se usar verificações e
teste em tempo de execução.
DECORATOR
Este padrão, de forma dinâmica, agrega valores adicionais em qualquer objeto.
Fornece alternativa flexível no uso de subclasses para estender funcionalidades.
Gamma et al. (2000), o uso do Decorator acrescenta
bilidades a objetos individuais dinamicamente e sem afetar outros objetos.
Outra característica é que essas responsabilidades podem ser removidas sem prejudicar,
de alguma forma, desempenho ou estrutura.
Decorar objetos é muito comum quando não se quer ter uma explosão de
subclasses. Por exemplo, havendo a necessidade de combinar responsabilidades entre
classes em um total de 6, isso acarretará numa explosão de 40 combinações
Segundo Freeman (2004), quando há herança de um comportamento por meio de
subclasses, este comportamento é definido estaticamente no tempo de compilação.
Todas as subclasses devem herdar o mesmo comportamento. Contudo, por meio da
composição será possível estender o comportamento do objeto de forma dinâmica
seguinte estrutura:
Figura 6. Estrutura do padrão Decorator
29
desejável que uma composição tenha somente certos componentes. Com
, não pode confiar no sistema de tipos para garantir a
se usar verificações e
Este padrão, de forma dinâmica, agrega valores adicionais em qualquer objeto.
Fornece alternativa flexível no uso de subclasses para estender funcionalidades.
uso do Decorator acrescenta
bilidades a objetos individuais dinamicamente e sem afetar outros objetos.
ser removidas sem prejudicar,
r uma explosão de
subclasses. Por exemplo, havendo a necessidade de combinar responsabilidades entre 3
combinações de classes
amento por meio de
é definido estaticamente no tempo de compilação.
Todas as subclasses devem herdar o mesmo comportamento. Contudo, por meio da
forma dinâmica e em
A classe Component
responsabilidades acresce
define um objeto para o qual responsabilidades adicionais podem ser atribuídas; a classe
Decorator mantém uma referência para um objeto
que segue a interface de Component
ao componente.
4.3.1. Exemplo do Decorator
Freeman (2004) apresenta
mostrado na figura 5.
Beverage é uma classe abstrata de componente.
sozinha ou englobada por um decorador.
componentes concretos que
são um tipo de bebida. Esses objetos
CondimentDecorator “tem
CondimentDecorator tem uma variável de instância que co
bebida. Mocha, Whip e Soy
Beverage. Os decoradores podem adicionar novos métodos; no entanto, o novo
comportamento geralmente é adicionado fazendo cálculo antes e depois de um método
existente no componente. Eles precisam implementar não somente
getDescription().
Figura 7
Component define a interface para objetos que podem ter
ntadas aos mesmos dinamicamente; Concre
define um objeto para o qual responsabilidades adicionais podem ser atribuídas; a classe
mantém uma referência para um objeto Component e define uma interface
Component e ConcreteDecorator acrescenta responsabi
Exemplo do Decorator
apresenta um exemplo de aplicação do padrão decorator
é uma classe abstrata de componente. Cada bebida
por um decorador. Expresso, DarkRoast e HouseBlend
componentes concretos que estendem de Beverage no que caracteriza que essas classes
Esses objetos receberão dinamicamente novos comportamentos.
“tem-uma” (engloba) uma bebida, o que si
tem uma variável de instância que contém uma referência a uma
Soy são os decoradores e eles pode estender o estado de
es podem adicionar novos métodos; no entanto, o novo
mento geralmente é adicionado fazendo cálculo antes e depois de um método
Eles precisam implementar não somente cost()
7. Exemplo de implementação do padrão Decorator
30
define a interface para objetos que podem ter
ConcretComponent
define um objeto para o qual responsabilidades adicionais podem ser atribuídas; a classe
e define uma interface
acrescenta responsabilidades
padrão decorator
pode ser usada
HouseBlend são
no que caracteriza que essas classes
receberão dinamicamente novos comportamentos.
” (engloba) uma bebida, o que significa que o
ntém uma referência a uma
são os decoradores e eles pode estender o estado de
es podem adicionar novos métodos; no entanto, o novo
mento geralmente é adicionado fazendo cálculo antes e depois de um método
cost(), mas também
31
No bloco abaixo, segue o código para implementação da classe Beverage e suas subclasses.
Bloco 11. Implementação da classe Beverage
A classe CondimentDecorator simplesmente repassa as solicitações de
descrições para a bebida e suas subclasses estendem essa operação. O código é
mostrado no bloco abaixo.
public abstract class Beverage{ String description = "Unknown Beverage";
public String getDescription(){ return description; }
public abstract double cost(); }
public class Expresso extends Beverage{ public Expresso(){ description = "Expresso"; }
public double cost(){ return 1.99; } } public class HouseBlend extends Beverage{ public HouseBlend(){ description = "House Blend Coffee"; } public double cost(){ return 0.89; } }
public class DarkRoast extends Beverage{ public DarkRoast(){ description = "Dark Roast"; } public double cost(){ return 1.05; } }
32
Bloco 12. Implementação das classes dos condimentos
Com o padrão Decorator tornam-se fácil acrescentar os condimentos as bebidas
de forma dinâmica. Assim, não é preciso estabelecer que cada bebida possa ter apenas
um condimento, ou combinações de dois. Por exemplo, Expresso pode ter o condimento
Whip e também Whip com Soy ou até os três. Com uma pequena quantidade pode
public abstract class CondimentDecorator extends Beverage{ public abstract String getDescription(); } public class Soy extends CondimentDecorator{ Beverage beverage; public Soy(Beverage beverage){ this.beverage = beverage; } public String getDescription(){ return beverage.getDescription()+ ", Soy"; } public double cost(){ return 0.15 + beverage.cost(); } } public class Whip extends CondimentDecorator{ Beverage beverage; public Whip(Beverage beverage){ this.beverage = beverage; } public String getDescription(){ return beverage.getDescription()+", Whip"; } public double cost(){ return 0.10 + beverage.cost(); } } public class Mocha extends CondimentDecorator{ Beverage beverage; public Mocha(Beverage beverage){ this.beverage = beverage; } public String getDescription(){ return beverage.getDescription() + ", Mocha"; } public double cost(){ return 0.20 + beverage.cost(); } }
33
parecer fácil, mas se tivesse 20 condimentos para 10 tipos de bebidas ficaria difícil de
criar cada classe representando uma bebida com combinações de condimentos.
A atribuição de comportamentos em tempo de execução é apresentada no
seguinte bloco.
Bloco 13. Implementação da classe StarbuzzCoffee
No objeto beverage é simplesmente instanciado o objeto Expresso sem nenhum
condimento. Seu preço final será de R$ 1.99. Em beverage2, é instanciado o objeto
DarkRoast e é acrescentado 3 tipos de condimentos: Mocha, Soy e Whip. Para que
beverage2 contenha os condimentos é preciso instanciá-los e passar o tipo da bebida
como parâmetro, formando uma espécie de invólucro. Mocha envolve beverage2, assim
como Soy e Whip da mesma forma. A descrição da bebida retornará “Dark Roast,
Mocha, Soy, Whip” e o preço final (somando o custo da bebida mais os condimentos)
de R$ 1,50. De igual modo beverage3 recebe um condimento e a descrição e o preço
são alterados.
4.4. CHAIN OF RESPONSIBILITY
O Chain of Responsability (Cadeia de Responsabilidades) é um tipo de padrão
comportamental e, segundo Gamma et al. (2000), a intenção do padrão é evitar o
acoplamento do remetente de uma solicitação ao seu receptor, ao dar a mais de um
objeto a oportunidade de tratar a solicitação. Encadear os objetos receptores, passando a
solicitação ao longo da cadeia até que um objeto a trate. Na Figura 8 está demonstrada a
public class StarbuzzCoffee{ public static void main(String args[]){ Beverage beverage = new Expresso(); System.out.println(beverage.getDescription()+" R$"+beverage.cost()); Beverage beverage2 = new DarkRoast(); beverage2 = new Mocha(beverage2); beverage2 = new Soy(beverage2); beverage2 = new Whip(beverage2); System.out.println(beverage2.getDescription()+" R$"+beverage2.cost()); Beverage beverage3 = new HouseBlend(); beverage3 = new Mocha(beverage3); System.out.println(beverage3.getDescription()+" R$"+beverage3.cost()); } }
estrutura básica do padrão. A class
e opcionalmente implementa o elo (
solicitações pelas quais é responsável podendo também acessar seu sucessor e se o
ConcreteHandler pode tratar a
solicitação para o seu sucessor. Por fim, a classe
objeto ConcreteHandler da cadeia, (GAMMA et al., 2000).
O efeito que se dá quando um cliente emite uma solic
longo da cadeia até que um objeto
la.
Figura
4.4.1. Exemplo do Chain of Responsability
Para exemplificar, é abordado
uma operação efetuar pagamento entre os bancos.
Para iniciar, criar-
sistema, exibido no Bloco 14
Bloco
A classe BancoChain
responsabilidades. Ela possui um atributo que é o ident
referência para o próximo objeto. O método
e faz verifica se o próximo for nulo, então o próximo na corrente será o parâmetro; caso
public enum IDBancos { bancoA, bancoB, bancoC, bancoD }
drão. A class Handler define uma interface para tratar solicitações
e opcionalmente implementa o elo (link) ao sucessor. ConcreteHandle
solicitações pelas quais é responsável podendo também acessar seu sucessor e se o
pode tratar a solicitação, ele assim o faz; caso contrário, ele repassa a
solicitação para o seu sucessor. Por fim, a classe Client inicia a solicitação para um
da cadeia, (GAMMA et al., 2000).
O efeito que se dá quando um cliente emite uma solicitação, esta se propaga ao
longo da cadeia até que um objeto ConcreteHandler assuma a responsabilidade de tratá
Figura 8. Estrutura do Chain of Responsability
Exemplo do Chain of Responsability
Para exemplificar, é abordado um exemplo de Brizeno (2011) que implementa
uma operação efetuar pagamento entre os bancos.
-se uma enumeração que identificará os bancos utilizado no
14.
Bloco 14. Implementação da enumeração de IDBancos
BancoChain , exibida no Bloco 15, implementará a cadeia de
responsabilidades. Ela possui um atributo que é o identificador do banco e outro a
referência para o próximo objeto. O método setNext recebe uma nova instância da classe
e faz verifica se o próximo for nulo, então o próximo na corrente será o parâmetro; caso
bancoA, bancoB, bancoC, bancoD
34
define uma interface para tratar solicitações
ConcreteHandle trata de
solicitações pelas quais é responsável podendo também acessar seu sucessor e se o
solicitação, ele assim o faz; caso contrário, ele repassa a
inicia a solicitação para um
itação, esta se propaga ao
assuma a responsabilidade de tratá-
um exemplo de Brizeno (2011) que implementa
que identificará os bancos utilizado no
implementará a cadeia de
ificador do banco e outro a
recebe uma nova instância da classe
e faz verifica se o próximo for nulo, então o próximo na corrente será o parâmetro; caso
35
contrário, repassa esta responsabilidade para o próximo elemento. Então, a instância que
deve ser adicionada na corrente irá percorrer os elementos até chegar ao último.
O algoritmo de pagamento é verificar se o banco atual pode fazer o pagamento.
Para isto é utilizado o identificador do banco, que é comparado com o identificador
passado por parâmetro. Se o elemento atual puder responder a requisição é chamado o
método que vai efetuar o pagamento de fato. Este método é abstrato, e as subclasses
devem implementá-lo, com seu próprio mecanismo.
Se o elemento atual não puder responder, ele repassa a chamado ao próximo
elemento da lista. Antes disto é feita uma verificação, por questões de segurança, se este
próximo elemento realmente existe. Caso nenhum elemento possa responder, é
disparada uma exceção.
Bloco 15. Implementação da classe BancoChain
public enum IDBancos { public abstract class BancoChain { protected BancoChain next; protected IDBancos identificadorDoBanco; public BancoChain(IDBancos id) { next = null; identificadorDoBanco = id; } public void setNext(BancoChain forma) { if (next == null) { next = forma; } else { next.setNext(forma); } } public void efetuarPagamento(IDBancos id) throws Exception { if (podeEfetuarPagamento(id)) { efetuaPagamento(); } else { if (next == null) { throw new Exception("banco não cadastrado"); } next.efetuarPagamento(id); } } private boolean podeEfetuarPagamento(IDBancos id) { if (identificadorDoBanco == id) { return true; } return false; } protected abstract void efetuaPagamento(); }
36
Definida a estrutura da cadeia de responsabilidades, implementa-se um banco
concreto, que corresponde a uma chamada. O BancoA inicializa seu ID e, no método de
efetuar o pagamento, exibe no terminal que o pagamento foi efetuado. A implementação
dos ostros bancos segue este exemplo, exibida no Bloco 16 abaixo.
Bloco 16. Implementação do banco concreto
Sendo assim, cada banco chama seu método efetuarPagamento de acordo com a
cadeia de responsabilidades criada, de acordo com o bloco abaixo.
Bloco 17. Implemantação do padrão Chain of Responsability
O diagrama de classes deste exemplo mostrado na Figura 9.
public static void main(String[] args) { BancoChain bancos = new BancoA(); bancos.setNext(new BancoB()); bancos.setNext(new BancoC()); bancos.setNext(new BancoD()); try { bancos.efetuarPagamento(IDBancos.bancoC); bancos.efetuarPagamento(IDBancos.bancoD); bancos.efetuarPagamento(IDBancos.bancoA); bancos.efetuarPagamento(IDBancos.bancoB); } catch (Exception e) { e.printStackTrace(); } }
public class BancoA extends BancoChain { public BancoA() { super(IDBancos.bancoA); } @Override protected void efetuaPagamento() { System.out.println("Pagamento efetuado no banco A"); } }
Figura 9. Diagrama de classes do exemplo do padrão Chain of Responsability
Gamma et al. (2000), dest
• Acoplamento reduzido
qual o outro objeto que trata de uma solicitação. Um objeto tem que
saber somente que uma solicitação será tratada "apropriadamente"
Tanto o receptor como o remetente não conhecimento explícito um do
outro, e um objeto que está na cadeia não necessita conhecer a estrutura
da mesma.
• Flexibilidade adicional na atribuição de responsabilidades a objetos
Chain of Responsability dá uma flex
responsabilidades entre objetos. É possível acrescentar ou mudar
responsabilidades para o tratamento de uma solicitação pelo acréscimo
ou mudança da cadeia em tempo de execução. Pode
com subclasses pa
• A recepção não é garantida
receptor explícito, não há
pode sair pelo final da cadeia sem ter sido tratada. Uma solicitação
também pode não ser tratada quando a cadeia não está configurada
apropriadamente.
4.5. OBSERVER
O padrão Observer
Subscribe, define uma dependência um
. Diagrama de classes do exemplo do padrão Chain of Responsability
Gamma et al. (2000), destaca alguns benefícios e deficiências deste padrão.
Acoplamento reduzido: o padrão libera um objeto de ter que conhecer
qual o outro objeto que trata de uma solicitação. Um objeto tem que
saber somente que uma solicitação será tratada "apropriadamente"
anto o receptor como o remetente não conhecimento explícito um do
outro, e um objeto que está na cadeia não necessita conhecer a estrutura
Flexibilidade adicional na atribuição de responsabilidades a objetos
Chain of Responsability dá uma flexibilidade adicional na distribuição de
responsabilidades entre objetos. É possível acrescentar ou mudar
responsabilidades para o tratamento de uma solicitação pelo acréscimo
ou mudança da cadeia em tempo de execução. Pode-se combinar isto
com subclasses para especializar estaticamente os handlers
A recepção não é garantida. Uma vez que uma solicitação não tem um
receptor explícito, não há garantia de que ela será tratada
pode sair pelo final da cadeia sem ter sido tratada. Uma solicitação
também pode não ser tratada quando a cadeia não está configurada
apropriadamente.
O padrão Observer, também conhecido como Dependents ou Publish
efine uma dependência um-para-muitos entre objetos de forma que quando
37
. Diagrama de classes do exemplo do padrão Chain of Responsability
aca alguns benefícios e deficiências deste padrão.
um objeto de ter que conhecer
qual o outro objeto que trata de uma solicitação. Um objeto tem que
saber somente que uma solicitação será tratada "apropriadamente" .
anto o receptor como o remetente não conhecimento explícito um do
outro, e um objeto que está na cadeia não necessita conhecer a estrutura
Flexibilidade adicional na atribuição de responsabilidades a objetos. O
ibilidade adicional na distribuição de
responsabilidades entre objetos. É possível acrescentar ou mudar
responsabilidades para o tratamento de uma solicitação pelo acréscimo
se combinar isto
handlers.
. Uma vez que uma solicitação não tem um
de que ela será tratada - a solicitação
pode sair pelo final da cadeia sem ter sido tratada. Uma solicitação
também pode não ser tratada quando a cadeia não está configurada
, também conhecido como Dependents ou Publish-
muitos entre objetos de forma que quando
38
um objeto mudar de estado, todos os seus dependentes sejam notificados e atualizados
automaticamente (GAMMA et al., 2000).
No Observer quando um estado de um objeto é alterado, todos os seus
dependentes são notificados, e dependendo do tipo de notificação o observador também
pode ser atualizado com novos valores (FREEMAN et al., 2004). Observer desacopla
um objeto do conhecimento de que outros objetos dependem dele (ROCHA, 2003).
Existem algumas maneiras diferentes de se implementar o padrão Observer,
mas a maioria inclui as interfaces Subject e Observer. Um subject pode ter um número
qualquer de observadores dependentes, e todos os observadores são notificados quando
subject sofre uma mudança de estado, assim em resposta, cada observador inquirirá o
subject para sincronizar o seu estado com o estado do subject (GAMMA et al., 2000).
Esta iteração é o que GAMMA et al. (2000) chama de publish-subscribe, onde o subject
é o publicador de notificações e as envia sem ter que saber quem são os seus
observadores, lembrando que um número qualquer de observadores podem se inscrever
para receber essas notificações, nota-se então que existe uma relação entre um subject e
muitos observadores (1:N).
Como o sujeito é o único proprietário dos dados, os observadores dependem do
sujeito para alterá-los quando os dados são alterados, o que leva a um design orientado a
objetos mais simples do que permitir que muitos objetos controlem os mesmos dados
(FREEMAN et al., 2004).
Segundo Gamma et al. (2000), este padrão deve ser aplicado:
• Quando uma abstração tem dois aspectos, um dependente do outro.
Encapsulam-se esses aspectos separadamente para que assim permita-se
variá-los e reutizá-los independentemente.
• Quando uma mudança em um objeto exige mudanças em outros
objetos, e não sabe-se quantos objetos necessitam ser mudados.
• Quando não deseja-se que objetos sejam fortemente acoplados.
O padrão Observer fornece um design de objeto onde os sujeitos e os
observadores são levemente ligados, ou seja, os objetos podem interagir, mas sabem
muito pouco um do outro, o que trona mais fácil lidar com mudanças, já que minimizam
a interdependência entre os objetos (FREEMAN et al., 2004). Isso porque a única coisa
que o sujeito sabe sobre um observador é que ele implementa uma certa interface
(Observer), com isso podemos adicionar novos observadores a qualquer momento,
nunca precisaremos modificar o sujeito para adicionar novos tipos de observadores e
ainda podemos reutilizar sujeitos ou observadores independentemente uns dos outros
(FREEMAN et al., 2004).
Segundo o Rocha
padrões centrada no padrão Observer
o View é o Observer para o
pelo usuário e lê dados
encapsulando lógica de controle que afeta o
A figura 10 mostra a estrutura do padrão Observer.
Figura 10
Os participantes dessa estrutura são explicitados segundo Gamma
e Freeman et al. (2004):
• Subject –
objetos Observer pode observar um subject. Os objetos usam esta
interface para se regi
removidos.
• Observer –
deveriam ser notificados sobre mudanças em um subject. Todos os
observadores potenciais precisam implementar essa interface.
demos adicionar novos observadores a qualquer momento,
nunca precisaremos modificar o sujeito para adicionar novos tipos de observadores e
ainda podemos reutilizar sujeitos ou observadores independentemente uns dos outros
(2003) o padrão de arquitetura MVC é uma combinação de
padrões centrada no padrão Observer, onde o Model notifica o View sobre as alterações;
é o Observer para o Model e notifica o Controller sobre os eventos iniciados
pelo usuário e lê dados do Model; e o Controller é um Observer para o
encapsulando lógica de controle que afeta o Model e seleciona View.
mostra a estrutura do padrão Observer.
. Estrutura do padrão Observer (GAMMA et al, 2000).
Os participantes dessa estrutura são explicitados segundo Gamma
conhece os seus observadores. Um número qualquer de
objetos Observer pode observar um subject. Os objetos usam esta
interface para se registrarem como observadores e para serem
removidos.
– define uma interface de atualização para objetos que
deveriam ser notificados sobre mudanças em um subject. Todos os
observadores potenciais precisam implementar essa interface.
39
demos adicionar novos observadores a qualquer momento,
nunca precisaremos modificar o sujeito para adicionar novos tipos de observadores e
ainda podemos reutilizar sujeitos ou observadores independentemente uns dos outros
padrão de arquitetura MVC é uma combinação de
sobre as alterações;
sobre os eventos iniciados
é um Observer para o View,
Os participantes dessa estrutura são explicitados segundo Gamma et al. (2000)
conhece os seus observadores. Um número qualquer de
objetos Observer pode observar um subject. Os objetos usam esta
strarem como observadores e para serem
define uma interface de atualização para objetos que
deveriam ser notificados sobre mudanças em um subject. Todos os
observadores potenciais precisam implementar essa interface.
• ConcreteSubject
ConcreteObserver. Envia uma notificação para os seus observadores
quando seu estado muda.
• ConcreteObserver
que implemente a interface Observer. Mantém uma referênc
objeto ConcreteSubject, também armazena estados que deveriam
permanecer consistentes com os do Subject. Implementa a interface de
atualização de Observer, para manter seu estado consistente com o do
subject.
O ConcreteSubject notifica seus obser
mudança que poderia tornar inconsistente o estado deles com o seu próprio
et al., 2000).
A figura 11 apresenta um diagrama de sequencia de como as colaborações
acontecem entre um subject e dois observadores.
Figura 11. Diagrama de sequência entre um subject e dois observadores (GAMMA et al, 2000).
O objeto Observer na figura xxx que inicia a solicitação de mudança posterga
sua atualização até que consiga uma notificação do subject. Notify nã
chamada pelo subject, pode ser chamada por um observador ou por um outro tipo de
objeto (GAMMA et al., 2000).
As conseqüências (benefícios e deficiências) da utilização desse padrão
apontadas por Gamma et al. (2000) são: permite acrescentar obse
modificar o subject ou outros observadores; permite variar subjects e observadores de
forma independente; acoplamento abstrato entre Subject e
ConcreteSubject – armazena estados de interesse para objetos
ConcreteObserver. Envia uma notificação para os seus observadores
quando seu estado muda.
ConcreteObserver – os ConcreteObservers podem ser qualquer classe
que implemente a interface Observer. Mantém uma referênc
objeto ConcreteSubject, também armazena estados que deveriam
permanecer consistentes com os do Subject. Implementa a interface de
atualização de Observer, para manter seu estado consistente com o do
O ConcreteSubject notifica seus observadores sempre que ocorrer uma
mudança que poderia tornar inconsistente o estado deles com o seu próprio
apresenta um diagrama de sequencia de como as colaborações
acontecem entre um subject e dois observadores.
. Diagrama de sequência entre um subject e dois observadores (GAMMA et al, 2000).
O objeto Observer na figura xxx que inicia a solicitação de mudança posterga
sua atualização até que consiga uma notificação do subject. Notify nã
chamada pelo subject, pode ser chamada por um observador ou por um outro tipo de
., 2000).
As conseqüências (benefícios e deficiências) da utilização desse padrão
apontadas por Gamma et al. (2000) são: permite acrescentar obse
modificar o subject ou outros observadores; permite variar subjects e observadores de
forma independente; acoplamento abstrato entre Subject e Observer, e por não serem
40
armazena estados de interesse para objetos
ConcreteObserver. Envia uma notificação para os seus observadores
os ConcreteObservers podem ser qualquer classe
que implemente a interface Observer. Mantém uma referência para um
objeto ConcreteSubject, também armazena estados que deveriam
permanecer consistentes com os do Subject. Implementa a interface de
atualização de Observer, para manter seu estado consistente com o do
vadores sempre que ocorrer uma
mudança que poderia tornar inconsistente o estado deles com o seu próprio (GAMMA
apresenta um diagrama de sequencia de como as colaborações
. Diagrama de sequência entre um subject e dois observadores (GAMMA et al, 2000).
O objeto Observer na figura xxx que inicia a solicitação de mudança posterga
sua atualização até que consiga uma notificação do subject. Notify não é sempre
chamada pelo subject, pode ser chamada por um observador ou por um outro tipo de
As conseqüências (benefícios e deficiências) da utilização desse padrão
apontadas por Gamma et al. (2000) são: permite acrescentar observadores sem
modificar o subject ou outros observadores; permite variar subjects e observadores de
Observer, e por não serem
fortemente acoplados os mesmos podem pertencer a diferentes camadas de abs
suporte a comunicações broadcast sem especificar seu receptor; atualizações
inesperadas, pois como um observador não conhece a presença dos outros, eles podem
ser cegos para o custo global de mudança.
Os padrões relacionados são Mediator, encapsula
atualizações complexas, onde o ChangeManager atua como um mediador entre subjects
e observadores, e o Singleton onde o ChangeManager pode usar o Singleton para torná
lo único e globalmente acessível.
4.5.1. Exemplo de Observer
Para exemplificar o padrão Observer
(2004), incluindo a base da explicação sobre o exemplo
construção de uma estação meteorológica baseada na Internet, que deverá monitorar as
condições atuais do tempo (te
aplicativo que forneça as condições atuais, estatísticas meteorológicas e uma simples
previsão. A figura 12 apresenta o diagrama de classes que será utilizado no exemplo
Figura
fortemente acoplados os mesmos podem pertencer a diferentes camadas de abs
suporte a comunicações broadcast sem especificar seu receptor; atualizações
inesperadas, pois como um observador não conhece a presença dos outros, eles podem
ser cegos para o custo global de mudança.
Os padrões relacionados são Mediator, encapsulando a semântica de
atualizações complexas, onde o ChangeManager atua como um mediador entre subjects
e observadores, e o Singleton onde o ChangeManager pode usar o Singleton para torná
lo único e globalmente acessível.
Exemplo de Observer
car o padrão Observer utilizou-se um exemplo de
, incluindo a base da explicação sobre o exemplo. O exemplo baseia
construção de uma estação meteorológica baseada na Internet, que deverá monitorar as
condições atuais do tempo (temperatura, umidade e pressão). A ideia é criar
que forneça as condições atuais, estatísticas meteorológicas e uma simples
apresenta o diagrama de classes que será utilizado no exemplo
Figura 12. Diagrama de classes da estação meteorológica
41
fortemente acoplados os mesmos podem pertencer a diferentes camadas de abstração;
suporte a comunicações broadcast sem especificar seu receptor; atualizações
inesperadas, pois como um observador não conhece a presença dos outros, eles podem
ndo a semântica de
atualizações complexas, onde o ChangeManager atua como um mediador entre subjects
e observadores, e o Singleton onde o ChangeManager pode usar o Singleton para torná-
um exemplo de Freeman et al.
O exemplo baseia-se na
construção de uma estação meteorológica baseada na Internet, que deverá monitorar as
mperatura, umidade e pressão). A ideia é criar um
que forneça as condições atuais, estatísticas meteorológicas e uma simples
apresenta o diagrama de classes que será utilizado no exemplo.
42
De acordo com o diagrama de classe todos os componentes meteorológicos
implementam a interface Observer, o que dá a Subject uma interface comum com a qual
falar na hora de atualizar os observadores, e a interface DisplayElement deve ser
implementada por todos os elementos de exibição. As interfaces utilizadas são
mostradas no bloco 18.
Na interface Subject temos um método para registro e outro para exclusão de
um elemento, que utilizam um Observer como argumento. A interface Observer possue
o método update recebe os valores de estado do Subject quando uma medição muda.
Essa interface é implementada por todos os observadores. A interface DisplayElement
possui apenas o método display() que é chamado quando o elemento de exibição
precisar ser exibido.
A classe WeatherData monitora os dados vindos de Estação Meteorológica e
atualiza as exibições. O objeto WeatherData sabe como falar com a estação e obter os
dados atualizados de temperatura, umidade de pressão. O método
measurementsChanged() é chamado sempre que dados de medição estão disponíveis.
O bloco 19 mostra a classe WeatherData. Essa classe implementa a interface
Subject e adiciona uma lista para conter os Observers no método construtor. Como
implementamos Subject, os métodos registerObserver(), removeObserver() e
notifyObservers() devem ser aplicados, assim para adicionar um observador ele é
colocado no final da lista e para remover ele é retirado da lista. No notifyObservers() é
onde passamos para todas os observadores sobre o estado. O método
public interface Subject { public void registerObserver(Observer o); public void removeObserver(Observer o); public void notifyObservers(); } public interface Observer { public void update(float temp, float umidade, float pressao); } public interface DisplayElement { public void display(); }
Bloco 18. Interfaces Subject, Observer e DisplayElement
43
measurementsChanged() notifica os Observers quando obtemos medições atualizadas da
Estação meteorológica. O método setMeasurements() foi utilizado para testar os
elementos de exibição.
Após isso, é hora de construir os elementos de exibição: exibição das
condições atuais, exibição das estatísticas e exibição da previsão. Todas as classes que
farão essas exibições devem implementar Observer e DisplayElement, nesse exemplo
public class WeatherData implements Subject{ private ArrayList observers; private float temperatura; private float umidade; private float pressao;
public WeatherData(){
observers = new ArrayList(); } @Override public void registerObserver(Observer o) {
observers.add(o); } @Override public void removeObserver(Observer o) {
int i = observers.indexOf(o); if (i >= 0){
observers.remove(i); }
} @Override public void notifyObservers() {
for (int i = 0; i < observers.size(); i++) { Observer observer = (Observer) observers.get(i); observer.update(temperatura, umidade, pressao);
} } public void measurementsChanged() {
notifyObservers(); } public void setMeasurements(float temperatura, float umidade, float pressao){
this.temperatura = temperatura; this.umidade = umidade; this.pressao = pressao; measurementsChanged();
}
Bloco 19. Classe WeatherData
44
mostra-se apenas a implementação da exibição das condições atuais, pois as outras
seguem um formato parecido. O bloco 20 apresenta essa implementação.
A classe CurrentConditionsDisplay deve receber o objeto WeatherData (o
Subject) para registrar a exibição como um observador. O método display() imprimi
apenas a temperatura e a umidade mais recentes.
A classe de teste deverá criar o objeto WeatherData e as três exibições que
serão passadas para WeatherData, conforme bloco 21.
WeatherData weatherData = new WeatherData(); CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData); StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData); //mostrar algumas estatísticas ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData); //previsão de acordo com a pressão
public class CurrentConditionsDisplay implements Observer, DisplayElement { private float temperatura; private float umidade; private Subject weatherData; public CurrentConditionsDisplay(Subject weatherData){ this.weatherData = weatherData; weatherData.registerObserver(this); } @Override public void update(float temperatura, float umidade, float pressao) { this.temperatura = temperatura; this.umidade = umidade; display(); } @Override public void display() { System.out.println("Condições atuais: " + temperatura + "C graus e " + umidade + "% umidade"); } }
Bloco 20. Implemnetação das interfaces Observer e DisplayElement em CurrentConditionDisplay.
Bloco 21. Criação do objeto weatherData e as 3 exibições
45
Nesse exemplo criamos o um exemplo do padrão Observer, porém se
utilizarmos Java, há a opção de contar com o suporte interno ao padrão Observer dele,
assim tudo que teríamos que fazer é estender Observable e informá-lo quando notificar
os Observadores, pois a API faria o resto.
4.6. STRATEGY
O padrão Strategy, também conhecido como Policy, define uma família de
algoritmos, encapsula cada um deles e os torna intercambiáveis, assim a estratégia deixa
o algoritmo varias independentemente dos clientes que o utilizam (GAMMA et al.,
2000).
Strategy sugere que algoritmos parecidos sejam separados de quem os utiliza
(BRIZENO, 2011). Devemos separar o que muda do que fica igual (FREEMAN et al.,
2004). O padrão também apresenta facilidade para extensão das funcionalidades e
nenhuma outra parte do código precisa ser alterada. Nesse padrão as classes de
estratégia são chamadas de Comportamento e a classe que utiliza o comportamento é
chamada de Contexto, ou seja, para um determinado Contexto pode-se aplicar um
conjunto de comportamentos (BRIZENO, 2011).
Segundo Gamma et al. (2000), o padrão Strategy se aplica quando:
• Muitas classes relacionadas diferem somente no seu comportamento.
• Necessita-se de variantes de um algoritmo. As estratégias podem ser
usadas quando essas variantes são implementadas como uma hierarquia
de classes de algoritmos.
• Um algoritmo usa dados dos quais os clientes não deveriam ter
conhecimento.
• Uma classe define muitos comportamentos, e estes aparecem em suas
operações como múltiplos comandos condicionais da linguagem.
Strategy usa composição. Usar composição dá mais flexibilidade e permite
encapsular uma família de algoritmos em seu próprio conjunto de classes, além disso
permite alterar o comportamento no tempo de execução, desde o objeto com o qual
estiver compondo implemente a interface de comportamento certa (FREEMAN et al.,
2004). A composição é usada em outros padrões de projetos além do Strategy.
A figura 13 apresenta a estrutura do padrão Strategy.
Figura 13
Os participantes dessa estrutura são explicitados segundo Gamma
• Strategy –
suportados, e o Context usa esta interface para chamar o algoritmo
definido por uma ConcreteStrategy.
• ConcreteStrategy
Strategy.
• Context –
uma referência para um objeto Strategy. Pode definir uma interface que
permite a Strategy acessar seus dados.
Strategy e Context interagem para i
disso um contexto repassa solicitações dos seus clientes para sua estratégia (GAMMA
et al., 2000). A chave para aplicação do padrão Strategy é projetar, para a estratégia e
seu contexto, interfaces genéricas o bastant
(GAMMA et al., 2000).
Como consequências, entre benefícios e desvantagens, Gamma
aponta: famílias de algoritmos relacionadas; uma alternativa ao uso de subclasses;
eliminam comandos condicionais da l
escolha de implementações, fornecendo diferentes implementações do mesmo
comportamento; os clientes devem conhecer diferentes
podem ser expostos a detalhes e aspectos de implementação;
entre Strategy e Context pode ser um problema; aumento do número de objetos.
Quando outra pessoa está utilizando seu código ela pode escolher qualquer
comportamento para o contexto que ela deseja aplicar, o que
13. Estrutura do Padrão Strategy (GAMMA et al., 2000).
s dessa estrutura são explicitados segundo Gamma
– define uma interface comum para todos os algoritmos
suportados, e o Context usa esta interface para chamar o algoritmo
definido por uma ConcreteStrategy.
ConcreteStrategy – implementa o algoritmo usando a interface de
é configurado com um objeto ConcreteStrategy e mantém
uma referência para um objeto Strategy. Pode definir uma interface que
permite a Strategy acessar seus dados.
Strategy e Context interagem para implementar o algoritmo escolhido, além
disso um contexto repassa solicitações dos seus clientes para sua estratégia (GAMMA
A chave para aplicação do padrão Strategy é projetar, para a estratégia e
seu contexto, interfaces genéricas o bastante para suportar uma variedade de algoritmos
Como consequências, entre benefícios e desvantagens, Gamma
aponta: famílias de algoritmos relacionadas; uma alternativa ao uso de subclasses;
eliminam comandos condicionais da linguagem de programação; possibilidade de
escolha de implementações, fornecendo diferentes implementações do mesmo
comportamento; os clientes devem conhecer diferentes Strategies, assim os clientes
podem ser expostos a detalhes e aspectos de implementação; custo de comunicação
entre Strategy e Context pode ser um problema; aumento do número de objetos.
Quando outra pessoa está utilizando seu código ela pode escolher qualquer
contexto que ela deseja aplicar, o que pode ser visto como um
46
s dessa estrutura são explicitados segundo Gamma et al. (2000).
define uma interface comum para todos os algoritmos
suportados, e o Context usa esta interface para chamar o algoritmo
ta o algoritmo usando a interface de
é configurado com um objeto ConcreteStrategy e mantém
uma referência para um objeto Strategy. Pode definir uma interface que
mplementar o algoritmo escolhido, além
disso um contexto repassa solicitações dos seus clientes para sua estratégia (GAMMA
A chave para aplicação do padrão Strategy é projetar, para a estratégia e
e para suportar uma variedade de algoritmos
Como consequências, entre benefícios e desvantagens, Gamma et al (2000)
aponta: famílias de algoritmos relacionadas; uma alternativa ao uso de subclasses;
inguagem de programação; possibilidade de
escolha de implementações, fornecendo diferentes implementações do mesmo
, assim os clientes
custo de comunicação
entre Strategy e Context pode ser um problema; aumento do número de objetos.
Quando outra pessoa está utilizando seu código ela pode escolher qualquer
pode ser visto como um
47
potencial problema, já que o usuário deve conhecer bem a diferença entre as estratégias
para saber escolher qual se aplica melhor ao contexto dele (BRIZENO, 2011).
Como padrão relacionado temos o Flyweight, onde objetos Strategy geralmente
são bons flyweights (GAMMA et al., 2000).
4.6.1. Exemplo de padrão Strategy
Para exemplificar o padrão Strategy, utilizou-se um exemplo apresentado por
Brizeno (2011) com suas explicações, onde em uma empresa existe um conjunto de
cargos, para cada cargo existem regras de cálculo de imposto e determinada
porcentagem do salário deve ser retirada de acordo com o salário base do funcionário.
As regras são mostradas a seguir:
• O Desenvolvedor deve ter um imposto de 15% caso seu salário seja
maior que R$ 2000,00 e 10% caso contrário;
• O Gerente deve ter um imposto de 20% caso seu salário seja maior que
R$ 3500,00 e 15% caso contrário;
• O DBA deve ter um imposto de de 15% caso seu salário seja maior que
R$ 2000,00 e 10% caso contrário;
O padrão Strategy sugere que os métodos de cálculo de imposto sejam
separados do funcionário. Assim devemos encapsular todos os algoritmos da mesma
família, ou seja, a família que calcula salários com impostos deve ser separada, e para
encapsulá-las cria-se uma interface. O bloco 22 apresenta a criação da interface de
calculo do imposto.
Após definida a classe que encapsula os algoritmos, as estratégias concretas de
calculo do imposto devem ser definidas, assim como Brizeno (2011) definiremos
apenas uma das classes de cálculo, pois as outras 2 seguem o mesmo padrão. O Bloco
23 mostra a implementação do imposto de 20% ou 15%.
interface CalculaImposto { double calculaSalarioComImposto(Funcionario umFuncionario); }
Bloco 22. Interface CalculaImposto
48
Esta classe depende da classe CalculoImposto, assim como as outras duas
citadas, ou seja, ela utiliza um objeto CalculoImposto, assim de acordo com o cargo, em
tempo de execução, ocorrerá a instância da estratégia de cálculo correta.
No bloco 24, podemos observar a configuração da estratégia de cálculo no
método construtor, assim de acordo com a estratégia o construtor executa um dos cases.
Através do método calcularSalarioComImposto() efetua o calculo do salário com o
imposto.
public Funcionario(int cargo, double salarioBase) { this.salarioBase = salarioBase; switch (cargo) { case DESENVOLVEDOR: estrategiaDeCalculo = new CalculoImpostoQuinzeOuDez(); cargo = DESENVOLVEDOR; break; case DBA: estrategiaDeCalculo = new CalculoImpostoQuinzeOuDez(); cargo = DBA; break; case GERENTE: estrategiaDeCalculo = new CalculoImpostoVinteOuQuinze(); cargo = GERENTE; break; default: throw new RuntimeException("Cargo não encontrado :/"); } } public double calcularSalarioComImposto() { return estrategiaDeCalculo.calculaSalarioComImposto(this); }
public class CalculoImpostoVinteOuQuinze implements CalculaImposto { @Override public double calculaSalarioComImposto(Funcionario umFuncionario) { if (umFuncionario.getSalarioBase() > 3500) { return umFuncionario.getSalarioBase() * 0.8; } return umFuncionario.getSalarioBase() * 0.85; } }
Bloco 23. Implementaação do imposto de 20% ou 15%.
Bloco 24. Método construtor de Funcionario.
49
4.7. TEMPLATE METHOD
O Template Method é um padrão de projeto comportamental, e como um
padrão comportamental, ele tem que dar uma solução para que a interação entre objetos
tenha acoplamento fraco, aumentando a flexibilidade.
Segundo Gamma et al (2000), a intenção do Template Method é definir o
esqueleto de um algoritmo em uma operação, postergando alguns passos para as
subclasses. Template Method permite que subclasses redefinam certos passos de um
algoritmo sem mudar a estrutura do mesmo.
Quando em uma aplicação, dentro de um algoritmo, existem partes que sempre
devem ser executadas de uma determinada forma, mas outras partes deste algoritmo
podem variar, é recomendado o uso do Template Method. Fazendo com que a parte fixa
do algoritmo seja declarada, e dentro do algoritmo tenham chamadas para métodos que
podem ou devem ser redefinidos palas subclasses. Com isso as subclasses podem ser
trocadas a qualquer momento para poder variar o comportamento.
Segundo Gamma et al (2000), este fundamental para a reutilização de código.
É importante em bibliotecas de classe porque são meios para a fatoração dos
comportamentos comuns.
Um detalhe importante é que dentre os métodos que podem ser redefinidos
nesse padrão existem dois tipos, os chamados primitivos que são métodos abstratos que
devem ser obrigatoriamente redefinidos. E os chamados métodos gancho, que pode ou
não ser redefinido, que é utilizado quando queremos modificar um comportamento já
implementado ou deixar um ponto livre para que, quem desenvolve uma subclasse
possa colocar o que quiser naquele ponto.
4.7.1. Exemplo do Template Method
Para exemplificar o uso do padrão Template Method foi desenvolvido uma
estrutura de classes para realizar a autenticação de um usuário em um sistema.
Figura 14. Diagrama de cla
A figura 14 representa o diagrama de classes do exemplo. Nele contem a classe
usuário, que apenas armazena o login e senha. A classe AbstractLogin
método login que é o método template e as classes LoginPorArquivo e LoginPorBD
completam o método template com os trechos de código que variam dentro do
algoritmo de login.
Segue a explicação da implementação desse
classes.
public class Usuario { private String login; private String nome;
public Usuario(String login, this.login = login; this.nome = nome; }
public String getLogin() { return login; }
public void setLogin(String login) { this.login = login; }
public String getNome() { return nome; }
public void setNome(String this.nome = nome; } }
. Diagrama de classe do exemplo do padrão Template Method.
representa o diagrama de classes do exemplo. Nele contem a classe
usuário, que apenas armazena o login e senha. A classe AbstractLogin
método login que é o método template e as classes LoginPorArquivo e LoginPorBD
completam o método template com os trechos de código que variam dentro do
Segue a explicação da implementação desse, baseada nesse diagrama de
login, String nome) { = login;
getLogin() {
setLogin(String login) { = login;
getNome() {
String nome) {
Bloco 25. Classe Usuario.
50
.
representa o diagrama de classes do exemplo. Nele contem a classe
usuário, que apenas armazena o login e senha. A classe AbstractLogin que define o
método login que é o método template e as classes LoginPorArquivo e LoginPorBD
completam o método template com os trechos de código que variam dentro do
baseada nesse diagrama de
51
A classe usuário é uma classe simples com login e senha para exemplificar
uma representação de um usuário em um sistema.
A classe AbstractLogin, Bloco 26, declara o método login, que é um Template
Method. Este método é um algoritmo que começa chamando o método abstrato
doAutentica que é declarado na própria classe como método abstrato, isto quer dizer que
ele deve ser implementado em uma subclasse de AbstractLogin. Mais afrente no código
aparece a chamada para sucessoHook. O sucessoHook é um método chamado de
gancho, que é um ponto específico do algoritmo onde pode-se ou não ser sobrescrito.
Neste caso foi declarado em branco para ser sobrescrito caso o desenvolvedor queira
adicionar uma ação no sucesso do login.
O método doRegistrarSessao assim como o doAutentica é um método abstrato
que deve obrigatoriamente ser implementado nas subclasses. Já o falhaHook também é
um método gancho, onde pode adicionar uma ação quando o login falha.
Note que o método template login foi declarado como final, para que o
algoritmo não possa ser sobrescrito, com isso somente os pontos específicos dentro
desse algoritmo podem variar.
public abstract class AbstractLogin { public final boolean login(String login, String senha){ Usuario usuario = this.doAutentica(login, senha);
if(usuario != null){ this.sucessoHook(usuario); this.doRegistrarSessao(usuario); return true; }else{ this.falhaHook(usuario); return false; } }
public abstract Usuario doAutentica(String login, String senha); public abstract void doRegistrarSessao(Usuario usuario); public void sucessoHook(Usuario usuario){ } public void falhaHook(Usuario usuario){ } }
Bloco 26. Classe AbstractLogin
52
No bloco 27, exibe as classes LoginPorArquivo e LoginPorBD, que herdam da
classe AbstractLogin, por isso são obrigadas a implementar os métodos abstratos
doAutentica e doRegistrarSessao, cada um com sua variação. A classe
LoginPorArquivo substitui o método falhaHook adicionando uma ação quando o login
falha. A LoginPorBD implementa somente o sucessoHook executado quando o login é
efetuado com sucesso.
Um ponto que se deve ter atenção na hora de implementar é que tem que
declarar o método template de forma que ele não possa ser sobrescrito. Somente os
métodos primitivos e ganchos devem ser sobrescritos.
Outra coisa se deve fazer na hora de projetar um método template é não criar
muitas operações primitivas para serem sobrescritas. Pois pode se tornar tedioso para
um cliente implementar uma grande quantidade de operações.
public class LoginPorArquivo extends AbstractLogin{ public Usuario doAutentica(String login, String senha) { System.out.println("Acessando o arquivo de usuários"); if(login == "eduardo" && senha == "senha"){ return new Usuario(login, "Eduardo"); } return null; }
public void doRegistrarSessao(Usuario usuario){ System.out.println("Sessao criada para usuario"); }
public void falhaHook(Usuario usuario){ System.out.println("Ação da falha no login"); } } public class LoginPorBD extends AbstractLogin{ public Usuario doAutentica(String login, String senha) { System.out.println("Acessando banco de dados"); if(login == "eduardo" && senha == "senha" ){ return new Usuario(login, "Eduardo"); } return null; }
public void doRegistrarSessao(Usuario usuario){ System.out.println("Sessao criada para usuario"); }
public void sucessoHook(Usuario usuario){ System.out.println("Registrando login no log do sistema"); } }
Bloco 27. Classes LoginPorArquivo e LoginPorBD
4.8. VISITOR
Assim como o Template Method, o Visitor também é um padrão de projeto
comportamental.
A intenção do padrão Visitor
operação a ser executada nos elementos de uma estrutura de objetos. Visitor permite
definir uma nova operação sem mudar as classes dos elementos sobre os quais opera.
Pode-se usar o padrão Visitor, quando tempos uma estrutura de objetos, com
objetos em que suas interfaces diferem e você deseja executar operações sobre esses
objetos que dependem de suas cla
uma estrutura de objetos é compartilhada por várias aplicações e se quer definir
operações específicas para cada aplicação.
Aplicando esse padrão facilita a adição de novas operações. Quando se quer
adicionar novas operações sobre uma estrutura de objetos, basta criar uma nova
subclasse visitante. Outra vantagem do Visitor é que ele pode acumular estados de
acordo com que ele visita cada elemento da estrutura.
Um fator que tem que levar em conta na hora de de
padrão, é se a estrutura de objetos irá mudar com frequência. Caso isso possa acontecer,
é aconselhável que não seja usado, pois cada nova classe que é adicionada para a
estrutura, terá que ser criado um método abstrato em Visito
método em cada ConcreteVisitor. Ele é bom quando a quantidade de operações muda
com frequência e a estrutura de objetos muda pouco.
Figura
como o Template Method, o Visitor também é um padrão de projeto
A intenção do padrão Visitor segundo Gamma et al (2000) é representar uma
operação a ser executada nos elementos de uma estrutura de objetos. Visitor permite
peração sem mudar as classes dos elementos sobre os quais opera.
se usar o padrão Visitor, quando tempos uma estrutura de objetos, com
objetos em que suas interfaces diferem e você deseja executar operações sobre esses
objetos que dependem de suas classes concretas. Também pode ser utilizado quando
uma estrutura de objetos é compartilhada por várias aplicações e se quer definir
operações específicas para cada aplicação.
Aplicando esse padrão facilita a adição de novas operações. Quando se quer
r novas operações sobre uma estrutura de objetos, basta criar uma nova
utra vantagem do Visitor é que ele pode acumular estados de
acordo com que ele visita cada elemento da estrutura.
Um fator que tem que levar em conta na hora de decidir, sobre usar ou não o
padrão, é se a estrutura de objetos irá mudar com frequência. Caso isso possa acontecer,
é aconselhável que não seja usado, pois cada nova classe que é adicionada para a
estrutura, terá que ser criado um método abstrato em Visitor e ser implementado o
método em cada ConcreteVisitor. Ele é bom quando a quantidade de operações muda
com frequência e a estrutura de objetos muda pouco.
Figura 15. Diagrama de sequência do padrão Visitor
53
como o Template Method, o Visitor também é um padrão de projeto
é representar uma
operação a ser executada nos elementos de uma estrutura de objetos. Visitor permite
peração sem mudar as classes dos elementos sobre os quais opera.
se usar o padrão Visitor, quando tempos uma estrutura de objetos, com
objetos em que suas interfaces diferem e você deseja executar operações sobre esses
sses concretas. Também pode ser utilizado quando
uma estrutura de objetos é compartilhada por várias aplicações e se quer definir
Aplicando esse padrão facilita a adição de novas operações. Quando se quer
r novas operações sobre uma estrutura de objetos, basta criar uma nova
utra vantagem do Visitor é que ele pode acumular estados de
cidir, sobre usar ou não o
padrão, é se a estrutura de objetos irá mudar com frequência. Caso isso possa acontecer,
é aconselhável que não seja usado, pois cada nova classe que é adicionada para a
r e ser implementado o
método em cada ConcreteVisitor. Ele é bom quando a quantidade de operações muda
O diagrama de sequenci
padrão Visitor, executando uma operação sobre uma estrutura de objetos. Primeiro cria
um objeto ConcreteVisitor(aVisitor) e para cada classe da estrutura de
objetos(aConcreteElementA e aConcreteElementB),
accept passando o Visitor criado.
Para cada objeto que recebeu a chamada de método, ele se envia para o
ConcreteVisitor que realiza a operação em si, utilizando dados do ConcreteElement.
4.8.1. Exemplo de Visitor
Para exemplificar o padrão
programcreek.com (2013),
Visitor. Este exemplo cria uma estrutura de uma cidade com lugares que podem ser
visitados. Dependendo da necessidade essa visi
diferentes. A figura 16 apresenta o diagrama de classes que será utilizado no exemplo.
Figura
Segue abaixo o código da implementação do padrão de a
classes exibido.
sequencia exibido na figura 15 demonstra o funcionamento do
padrão Visitor, executando uma operação sobre uma estrutura de objetos. Primeiro cria
um objeto ConcreteVisitor(aVisitor) e para cada classe da estrutura de
objetos(aConcreteElementA e aConcreteElementB), na ObjectStruture chama o método
accept passando o Visitor criado.
Para cada objeto que recebeu a chamada de método, ele se envia para o
ConcreteVisitor que realiza a operação em si, utilizando dados do ConcreteElement.
Exemplo de Visitor
icar o padrão Visitor foi adaptado um exemplo de
, para que ficasse mais claro alguns conceitos do padrão
cria uma estrutura de uma cidade com lugares que podem ser
visitados. Dependendo da necessidade essa visita pode ser executada de formas
apresenta o diagrama de classes que será utilizado no exemplo.
Figura 16. Diagrama de classes do exemplo de Visitor.
Segue abaixo o código da implementação do padrão de acordo com o diagrama de
54
demonstra o funcionamento do
padrão Visitor, executando uma operação sobre uma estrutura de objetos. Primeiro cria
um objeto ConcreteVisitor(aVisitor) e para cada classe da estrutura de
na ObjectStruture chama o método
Para cada objeto que recebeu a chamada de método, ele se envia para o
ConcreteVisitor que realiza a operação em si, utilizando dados do ConcreteElement.
um exemplo de
para que ficasse mais claro alguns conceitos do padrão
cria uma estrutura de uma cidade com lugares que podem ser
ta pode ser executada de formas
apresenta o diagrama de classes que será utilizado no exemplo.
cordo com o diagrama de
55
Começamos pelo bloco 28 vê-se as interfaces Visitable e Visitor. A Visitable
define a assinatura de um método chamado accept. Uma classe deve implementar esse
método para que ela possa ser visitada por um Visitor. Já a interface Visitor declara,
que uma classe que tenha essa interface precisa implementar o método visit para City,
Museum e Park. O método visit é o método onde os comportamentos são executados.
A classe abstrata Place, vista no bloco 29 implementa a classe Visitable para
que possa ser visitado. Com isso ele tem que implementar o método accept, mas ele
declara esse método como abstrato para que a tarefa de implementar fique para suas
subclasses. A classe abstrata inclui o atributo nome e o método para seu acesso.
public class City implements Visitable{ ArrayList<Place> places = new ArrayList<Place>(); public void addPlace(Place lugar){ places.add(lugar); }
public void accept(Visitor visitor) { System.out.println("A Cidade está aceitando um visitor."); visitor.visit(this);
for(Visitable e: places){ e.accept(visitor); }
public abstract class Place implements Visitable{ private String nome;
protected Place(String nome){ this.nome = nome; }
public abstract void accept(Visitor visitor);
public String getNome(){ return this.nome; } }
public interface Visitable { public void accept(Visitor visitor); } public interface Visitor { public void visit(City city); public void visit(Museum museum); public void visit(Park park); }
Bloco 28. Interfaces Visitable e Visitor.
Bloco 29. Classe abstrata Place
Bloco 30. Implementação de Visitable na classe City.
56
O bloco 30 mostra como é a classe City, que é implementa Visitable. Ela tem
uma lista de lugares (Places) que são visitáveis. Ela como também é visitável, ela
implementa o método accept. Sendo que quando a cidade é visitada, ela chama a acão
visit do visitor passando seu próprio objeto e depois executa a operação accept em todos
os lugares de sua lista para que sejam visitados.
A Classe Museum e Park do bloco 31 herdam de Lugar, sendo ambos
visitáveis. Eles implementam o método accept para que sobre eles, possam ser
executados novos comportamentos somente passando visitantes diferentes como
parâmetro.
public class Museum extends Place {
public Museum(String nome){ super(nome);
} public void accept(Visitor visitor) {
System.out.println("O Museu está aceitando um visitor."); visitor.visit(this);
} public void seeThePaintings(){
System.out.println("O visitante viu as pinturas."); } } public class Park extends Place { public Park(String nome){
super(nome); } public void accept(Visitor visitor) {
System.out.println("O Parque está aceitando um visitor."); visitor.visit(this);
} public void enjoy(){
System.out.println("O visitante se divertiu no parque."); } }
Bloco 31. Classes Museum e Park.
57
A classe FirstTimeVisitor, bloco 32, realiza a interface Visitor. Com isso ele
adiciona uma operação nova para a estrutura de classes visitáveis. O FirstTimeVisitor
conhece as três classes concretas e executa a operação visit para cada uma de acordo
com suas estrutura. Ela armazena a quantidade de lugares que foram visitados, para que
possa ser consultado posteriormente.
Assim como o FirstTimeVisitor, o JobSeekerVisitor, bloco 33, também é um
visitante. Neste caso ele adiciona o comportamento de um visitante à procura de
emprego em uma cidade. Dado uma cidade com lugares ele visita cada lugar da cidade a
procura de emprego.
public class JobSeekerVisitor implements Visitor{ public void visit(City city) {
System.out.println("Estou procurando emprego na Cidade."); } public void visit(Museum museum) {
System.out.println("Estou procurando emprego no Museu " +museum.getNome()); } public void visit(Park park) {
System.out.println("Estou procurando emprego no Parque "+park.getNome()); } }
public class FirstTimeVisitor implements Visitor{ public int qtdeLugaresVisitados = 0;
public void visit(City city) { System.out.println("Estou visitando a Cidade."); }
public void visit(Museum museum) { this.qtdeLugaresVisitados++; System.out.println("Estou visitando o Museu " +museum.getNome()); museum.seeThePaintings(); }
public void visit(Park park) { this.qtdeLugaresVisitados++; System.out.println("Estou visitando o Parque " +park.getNome()); park.enjoy(); }
public int getQtdeLugaresVisitados(){ return this.qtdeLugaresVisitados; } }
Bloco 32. Classe FirstTimeVisitor.
Bloco 33. Classe JobSeekerVisitor.
58
O Visitor pode ser visto com frequência sendo utilizado junto ao padrão
Composite, usando um visitante para adicionar novas operações aos elementos da
estrutura.
Este padrão torna fácil a adição de novas operações, mas deve se ver se a
estrutura de objetos mudará com pouca frequência. Pois se ela for mudar com grande
frequência, não valerá a pena usar esse padrão devido ao custo de adicionar novas
classes na estrutura. Vale a pena quando a estrutura não muda muito, mas aparecem
novas operações com frequência.
5. PADRÕES NÃO GOF
GoF (Gang of Four) foi como ficou conhecido ao autores do livro Design
Patterns: Elements of Reusable Object-Oriented Software, principal referência deste
trabalho e da maioria de projetos sobre padrões de projeto. Neste capítulo daremos uma
visão geral de alguns padrões de projeto não criados por eles, porém todos relacionados
com o SOA Design Patterns, pois não foi encontrado outras referências significativas
para efeito de apresentação neste trabalho. Contudo outros padrões foram encontrados,
como EAA Patterns - Patterns of Enterprise Application Architecture (Padrões de
Arquitetura de Aplicações Corporativas) e Analysis Patterns (Padrões de Análise),
ambos de Martin Fowler, mas acreditamos estarem fora do contexto pois expressam
mais arquitetura e design de análise respectivamente. Apesar de SOA Patterns também
possuir um apelo arquitetônico, ele também possui uma base voltada para padrões de
design.
5.1. SOA DESIGN PATTERNS
SOA Patterns fornece orientação arquitetônica através de padrões e anti-
padrões, ele mostra como construir serviços SOA (Service Oriented Architecture) que
apresentem flexibilidade, disponibilidade e escalabilidade, e também possuem um vasto
conjunto de padrões (Rotem-GAL_OZ, 2012).
Um Padrão de Design SOA fornece uma solução comprovada para problemas
que normalmente surgem quando construímos um Serviço (ERL, 2009). Os Padrões de
59
Design SOA podem ser agrupados em: Padrões de Serviços, Padrões de Inventário de
Serviços, Padrões de Composições de Serviços, Padrões ESB (Enterprise Service Bus)
e Padrões de Orquestração. Cada um desses grupos possuem diversos padrões.
Como são muitos os padrões citaremos apenas alguns baseados na explicação
de Barbosa (2010) a título de conhecimento apenas.
No grupo “Serviços” podemos citar como exemplo o “Service Façade”, que
introduz componentes de “fachada” de modo a abstrair partes da arquitetura dos
Serviços preservando sua lógica principal evitando acoplamentos indesejados.
No grupo “Inventário de Serviços”, um dos mais importantes é o “Schema
Centralization” que evita redundância na representação de dados.
No grupo “Composição de Serviços”, um dos mais utilizados é o “Service
Agent” que são agentes capazes de processar rotinas a partir da interceptação de
mensagens.
O padrão “Asynchronous Queuing”, do grupo “ESB-Enterprise Service Bus” é
bastante importante, uma vez que ele resolve o problema de latência, que pode ocorrer
quando um Serviço Provedor não se encontra disponível para atender a uma chamada de
um Serviço Consumidor.
Dentre os padrões de “Orquestração” podemos citar o “State Repository” que
trata de persistir dados referentes a Informações de Estado por longos períodos, sem
consumir recursos dos Serviços.
6. CONCLUSÕES
6.1. CONTRIBUIÇÕES
Para desenvolvedores e projetistas, os padrões de projetos tornaram-se uma
ferramenta imprescindível no desenvolvimento de software. Os padrões aqui estudados,
são os mais conhecidos e usados por pessoas experientes na indústria do software
orientado a objetos.
Segundo Gamma et al. (2000), existem várias razões para usar os padrões.
Dentre elas, a manipulação dos padrões de projeto, fornece um vocabulário comum para
comunicar, documentar, e explorar alternativas de projeto. Os padrões tornam um
60
sistema menos complexo ao permitir falar sobre ele em um nível de abstração mais alto
do que aquele de uma notação de projeto ou uma linguagem de programação. Eleva o
nível no qual projeta e discute o projeto com outras pessoas.
Outra razão para usar padrões é o auxílio para documentação e para o
aprendizado. Pessoas que estão aprendendo programação orientada a objetos
frequentemente se queixam que os sistemas com os quais estão trabalhando usam
herança de forma confusa e inconsciente e que é fácil perder o fluxo de controle. Na
maioria das vezes, isso deve-se ao fato dessas pessoas não compreenderem os padrões
de projeto no sistema. O aprendizado dos padrões irá ajudar a compreender os sistemas
orientados a objetos existentes.
6.2. LIMITAÇÕES
O estudo dos padrões de projeto por iniciantes contribui para o uso de novos
algoritmos ou técnicas de programação orientada a objetos e fornece um método
rigoroso para projetar sistemas. Para programadores experientes em orientação a
objetos, padrões de projetos simplesmente documentam projetos existentes. De acordo
com Gamma et al. (2000), o autor reconhece a limitação de que seu livro é apenas um
catálogo de projetos, mas incentiva aos programadores experientes a pesquisarem e
estudarem e buscar novas soluções e resolver limitações de projetos existentes.
61
REFERÊNCIAS BIBLIOGRÁFICAS
BARBOSA, R. C. 2010. “Padrões de Design SOA”. Disponível em:
http://www.soamaster.com.br/component/content/article/38-artigos/82-padroes-design-soa.html.
Última consulta em 19/08/2013.
BERNADETE, D. H. C.; GONÇALVES, E. D.; CHAGAS, R. L. S.; “Aplicando a Engenharia
de Domínio para Construção de Um Framework de Emissão do Perfil Profissiográfico
Previdenciário”. Trabalho de Conclusão de Curso - CEFET (Centro Federal de Educação
Tecnológica) Campos. Campos dos Goytacazes, RJ, 2007.
BRIZENO, M. 2011. “Mãos na Massa: Chain of Responsability”. Disponível em:
http://brizeno.wordpress.com/category/padroes-de-projeto/chain-of-responsibility/. Última
consulta em 04/09/2013.
BRIZENO, M. 2011. “Mãos na Massa: Strategy”. Disponível em:
http://brizeno.wordpress.com/category/padroes-de-projeto/strategy/. Última consulta em
22/08/2013.
BRIZENO, M. 2011. “Mãos na Massa: Abstract Factory”. Disponível em:
http://brizeno.wordpress.com/category/padroes-de-projeto/abstract-factory/. Última consulta em
20/08/2013.
BRUSAMOLIN, V. 2004. “Manutenibilidade de Software”. Artigo publicado em Revista
Digital em 20/01/2004. Instituto Científico de Ensino Superior e Pesquisa (ICESP). Disponível
em: http://www.revdigonline.com/artigos_download/art_10.pdf. Última consulta em
11/08/2013.
BUSCHMANN, F., MEUNIER, R., ROHNERT, H., SOMMERLAD, P., & STAL, M. (1996).
“Pattern-oriented software architecture: A system of patterns”. Chichester, UK.
CAELUM. FJ-11: Java e Orientação a Objetos. Rio de Janeiro, RJ, Brasil, 2011. Curso de
Formação Java.
ERL, T. “SOA Design Patterns”. Editora Prentice Hall. 1ª Edição. New York. 2009.
62
FRAKES, W. B. e KANG, K. apud MOURA, A. M. M.; CARVALHO, J. S.; SILVA, R. J.
Abordagens de Reutilização de Software e sua Aplicação no Domínio de Arrecadação
Tributária Municipal. Monografia de Pós Graduação - CEFET (Centro Federal de Educação
Tecnológica) Campos. Campos dos Goytacazes, RJ, 2006.
FREEMAN, Eric & FREEMAN, Elisabeth. “Use a Cabeça! Padrões de Projetos”. 2ª edição
revisada. Tradução. Editora Alta Books, 2004.
GAMMA, E., HELM, R., JOHNSON, R., VLISSIDES, J. “Padrões de Projeto: Soluções
reutilizáveis de Software Orientado a Objetos”. Reimpressão 2008. Trad: Luíz A. M. Salgado.
Porto Alegre, editora Bookman, 2000.
KRUEGER, C. W. apud VASCONCELOS, A. P. V., Uma Abordagem de apoio à Criação de
Arquiteturas de Referência de Domínio baseada na Análise de Sistemas Legados. Tese de
D.Sc. - COPPE/UFRJ (Laboratório de Engenharia de Software / Universidade Federal do Rio de
Janeiro), Rio de Janeiro, RJ, Brasil 2007.
LEMOS, H. D. 2013. “Encapsulamento, Polimorfismo, Herança”. Disponível em:
http://www.devmedia.com.br/encapsulamento-polimorfismo-heranca-parte-01/12991. Última
consulta em 21/08/2013.
MACORATTI, J. C. 2002. “Padrões de Projeto – Design Patterns”. Disponível em:
http://www.macoratti.net/vb_pd1.htm. Última consulta em 12/08/2013.
MACORATTI, J. C. 2011. “Padrões de Projeto – Os 7 princípios Básicos do
Desenvolvimento de Software”. Disponível em: http://www.macoratti.net/11/05/sd_prnc1.htm.
Última consulta em 20/08/2013.
MACORATTI, J. C. 2011. “Herança x Composição”. Disponível em:
http://www.macoratti.net/11/05/oop_cph1.htm. Última consulta em 10/08/2013.
ODYSSEY, 2007; “Odyssey SDE”. Ambiente acadêmico de reutilização desenvolvido pela
COPPE/UFRJ – Laboratório de Engenharia de Software / Universidade Federal do Rio de
Janeiro. Disponível em: http://reuse.cos.ufrj.br/odyssey. Última consulta em 11/08/2013.
63
PEREIRA, V. C. “Design Patterns na Prática – Desvendando Mistérios”. Disponível em:
http://www.devmedia.com.br/desing-patterns-na-pratica-desvendando-misterios-parte-1/14713.
Última consulta em 19/08/2013.
PIGOSKI, T. M. apud BRUSAMOLIN, V. 2004. “Manutenibilidade de Software”. Artigo
publicado em Revista Digital em 20/01/2004. Instituto Científico de Ensino Superior e Pesquisa
(ICESP). Disponível em: http://www.revdigonline.com/artigos_download/art_10.pdf. Última
consulta em 11/08/2013.
PRESSMAN, R.S., Engenharia de Software, Trad., McGraw Hill, New York, 2006.
PROGRAMCREEK.COM. 2013. “Java Design Pattern: Visitor”. Disponível em:
http://www.programcreek.com/2011/05/visitor-design-pattern-example/. Última consulta em
22/08/2013.
ROCHA, H. 2003. Argo Navis Curso J930 – GoF Design Patterns em Java. Disponível em:
http://www.argonavis.com.br/cursos/java/j930/. Última consulta em 16/08/2013.
Rotem-GAL-OZ , A. 2012. “SOA Patterns”. Disponível em: http://arnon.me/soa-patterns/. Última consulta em 19/08/2013. UNIVERSIA. Curso 6170: Padrões de Projetos. Disponível em:
http://mit.universia.com.br/6/6.170/pdf/6.170_lecture-12.pdf. Última consulta em 19/08/2013.
Recommended