Você está na página 1de 185

PADRÕES DE PROJETOS

DE SOFTWARE

autor
LUIZ ANTONIO LEÃO LISBOA JUNIOR

1ª edição
SESES
rio de janeiro  2018
Conselho editorial  roberto paes e gisele lima

Autor do original  luiz antonio leão lisboa junior 

Projeto editorial  roberto paes

Coordenação de produção  gisele lima, paula r. de a. machado e aline karina


rabello

Projeto gráfico  paulo vitor bastos

Diagramação  bfs media

Revisão linguística  bfs media

Revisão de conteúdo  raphael alves bruce

Imagem de capa  elnur | shutterstock.com

Todos os direitos reservados. Nenhuma parte desta obra pode ser reproduzida ou transmitida
por quaisquer meios (eletrônico ou mecânico, incluindo fotocópia e gravação) ou arquivada em
qualquer sistema ou banco de dados sem permissão escrita da Editora. Copyright seses, 2018.

Dados Internacionais de Catalogação na Publicação (cip)

L576p Leão, Luiz


Padrões de projeto de software / Luiz Leão.
Rio de Janeiro : SESES, 2018.
184 p: il.

isbn: 978-85-5548-573-2.

1. Arquitetura de Software. 2. Padrões de Projeto. 3. Engenharia


de Software. 4. Programação de Computadores. I. SESES. II. Estácio.
cdd 005.1

Diretoria de Ensino — Fábrica de Conhecimento


Rua do Bispo, 83, bloco F, Campus João Uchôa
Rio Comprido — Rio de Janeiro — rj — cep 20261-063
Sumário
Prefácio 15

1. Fundamentos de padrões de projetos 17


Introdução aos padrões de projeto 19

Um breve histórico 19

O que é um padrão de projeto? 20


Nome 20
Problema 21
Solução 21
Consequências 21

Principais padrões de projetos 22

Antipattern (Anti-Padrão) 24

Considerações 25

Aspectos técnicos 25

2. Padrões GoF 27
Famílias de padrões 28
Padrões de criação 29
Padrões estruturais 29
Padrões comportamentais 30

Abstract factory 30
Apresentação 30
Aplicabilidade 31
Estrutura 31
Descrição 32
Exemplo 32

Factory method 33
Apresentação 33
Aplicabilidade 34
Estrutura 34
Descrição 34
Exemplo 35

Builder 35
Apresentação 35
Aplicabilidade 35
Estrutura 36
Descrição 36
Exemplo 36

Prototype 37
Apresentação 37
Aplicabilidade 38
Estrutura 38
Descrição 38
Exemplo 38

Singleton 39
Apresentação 39
Aplicabilidade 40
Estrutura 40
Descrição 40
Exemplo 40

Adapter 41
Apresentação 41
Aplicabilidade 41
Estrutura 41
Descrição 42
Exemplo 42

Bridge 43
Apresentação 43
Aplicabilidade 43
Estrutura 43
Descrição 44
Exemplo 44
Composite 45
Apresentação 45
Aplicabilidade 45
Estrutura 45
Descrição 46
Exemplo 46

Decorator 47
Apresentação 47
Aplicabilidade 47
Estrutura 48
Descrição 48
Exemplo 48

Façade 49
Apresentação 49
Aplicabilidade 49
Estrutura 50
Descrição 50
Exemplo 50

Flyweight 51
Apresentação 51
Aplicabilidade 51
Estrutura 51
Descrição 52
Exemplo 52

Proxy 53
Apresentação 53
Aplicabilidade 53
Estrutura 54
Descrição 54
Exemplo 55

Chain of responsibility 55
Apresentação 55
Aplicabilidade 56
Estrutura 56
Descrição 56
Exemplo 56

Command 57
Apresentação 57
Aplicabilidade 57
Estrutura 58
Descrição 58
Exemplo 58

Interpreter 59
Apresentação 59
Aplicabilidade 59
Estrutura 60
Descrição 60
Exemplo 61

Iterator 61
Apresentação 61
Aplicabilidade 62
Descrição 62
Exemplo 63

Mediator 63
Apresentação 63

Aplicabilidade 64
Estrutura 64
Descrição 65
Exemplo 65

Memento 65
Apresentação 65
Aplicabilidade 66
Estrutura 66
Descrição 66
Exemplo 67

Observer 67
Apresentação 67
Aplicabilidade 68
Descrição 68
Exemplo 69

State 69
Apresentação 69
Aplicabilidade 70
Descrição 70
Exemplo 70

Strategy 72
Apresentação 72
Aplicabilidade 72
Descrição 72
Exemplo 73

TemplateMethod 73
Apresentação 73
Aplicabilidade 74
Estrutura 74
Descrição 74
Exemplo 75

Visitor 76
Apresentação 76
Aplicabilidade 76
Estrutura 77
Descrição 77
Exemplo 78

3. Padrões GRASP 81
Princípios básicos do catálogo de padrões GRASP 82

Padrões Básicos 83
Information expert (Especialista na informação); 83
Creator (Criador); 84
Low coupling (Baixo acoplamento) 85
High cohesion (Alta coesão) 88
Controller (Controlador) 89

Padrões avançados 91
Polymorphism (Polimorfismo) 91
Pure fabrication (Invenção pura) 92
Indirection (Indireção) 92
Protected variations (Variações protegidas) 93

4. Arquitetura em camadas 95
Conceito 96

Arquiteto de software 98

Complexidade dos projetos de software 99


Projetos simples 99
Projetos complexos 99

Arquitetura monolítica 100


Vantagens 100
Desvantagens 100

Arquitetura multi-camadas 100

MVC 101
Visão (View) 102
Controlador (Controller) 102
Modelo (Model) 103
Vantagens 103
Desvantagens 103

Arquitetura cliente-servidor 103


Metodologia de desenvolvimento 104
Vantagens 104
Desvantagens 105

SOA 105
Vantagens 106
Desvantagens 107
5. Aplicações práticas dos padrões GoF 109
Considerações sobre os exemplos 110

Abstract factory 110


GuiFactory.java 111
MotifGuiFactory.java 111
QtGuiFactory.java 111
Botao.java 111
BotaoMotif.java 112
BotaoQt.java 112
Janela.java 112
JanelaMotif.java 112
JanelaQt.java 112
Cliente.java 113

Factory method 113


Aplicacao.java 113
MinhaAplicacao.java 113
Documento.java 114
MeuDocumento.java 114
Cliente.java 114

Builder 115
ConversorTexto.java 115
ConversorPDF.java 115
ConversorTeX.java 116
ConversorASCII.java 116
LeitorRTF.java 116
Cliente.java 117

Prototype 117
DocumentoPrototype.java 118
ASCIIPrototype.java 118
PDFPrototype.java 118
Cliente.jav 119
Singleton 119
Singleton.java 119
Cliente.java 120

Adapter 120
IImage.java 120
JpgImagem.java 120
JpgImagemAdapter.java 121
PngImagem.java 121
PngImagemAdapter.java 122
Cliente.java 122

Bridge 122
Janela Abstrata.java 123
IJanela.java 123
JanelaWindows.java 123
JanelaLinux.java 124
JanelaAviso.java 124
JanelaDialogo.java 124
Cliente.java 125

Composite 125
ArquivoComponent.java 125
ArquivoComposite.java 126
ArquivoVideo.java 127
Cliente.java 127

Decorator 127
Coquetel.java 128
CoquetelDecorator.java 128
Vodka.java 129
SucoLaranja.java 129
Cliente 129

Façade 130
Facade.java 130
Funcionario.java 130
Cargo.java 131
Setor.java 131
Cliente.java 132

Flyweight 132
ElementoFlyweight.java 132
Elemento.java 133
FlyweightFactory.java 133
Imagem.java 134
Ponto.java 134
Cliente.java 135

Proxy 136
Banco.java 136
BancoProxy.java 136
Cliente.java 137

Chain of responsibility 138


Pagamento Chain.java 138
EnumPagamentos.java 139
Boleto.java 139
CartaoA.java 140
CartaoB.java 140
Cliente.java 140

Command 141
PagamentoCommand.java 141
Compra.java 141
PagamentoBoleto.java 143
PagamentoCartãoCredito.java 143
PagamentoCartãoDebito.java 143
Cliente.java 144

Interpreter 144
NumRomanoInterpreter.java 144
Contexto.java 146
UnidadeRomano.java 146
DezenaRomano.java 147
CentenaRomano.java 148
MilharRomano.java 148
Cliente.java 149

Iterator 150
IteradorCanais.java 150
CanaisAgregado.java 151
CanaisNoticia.java 151
Canal.java 152
Cliente.java 152

Mediator 153
Mediator.java 153
MensagemMediator.java 154
Colleague.java 154
AndroidColleague.java 155
IOSColleague.java 155
WindowsPhoneColleague.java 155
Cliente.java 156

Memento 156
TextoMemento.java 157
TextoCareTaker.java 157
Texto.java 158
Cliente.java 158

Observer 159
DadosObserver.java 159
Dados.java 159
DadosSubject.java 160
Barra Observer.java 160
PercentualObserver.java 161
TabelaObserver.java 162
Cliente.java 162

State 163
MarioState.java 163
Mario.java 163
Mario Pequeno.java 164
MarioGrande.java 164
MarioFogo.java 165
MarioPena.java 166
MarioMorto.java 167
Cliente.java 167

Strategy 168
StrategyCalculoImposto.java 168
CalculoImposto10_15.java 168
CalculoImposto15_20.java 169
Funcionario.java 169
Cliente.java 170

Template Method 171


OrdenarTemplate.java 172
EnumModoReproducao.java 172
OrdenarAno.java 173
OrdenarAutor.java 173
OrdenarEstrela.java 173
OrdenarNome.java 174
Musica.java 174
Cliente.java 174

Visitor 175
Arvore Visitor.java 176
ArvoreBinaria.java 176
No.java 177
ExibirIdentadoVisitor.java 178
ExibirInOrderVisitor.java 178
Exibir Post Order Visitor.java 179
ExibirPreOrderVisitor.java 179
Prefácio

Prezados(as) alunos(as),

Essa obra tem como objetivo de ser um guia prático para a implementação
de padrões de projeto (design pattern) em softwares, tornando esse assunto mais
próximo da comunidade acadêmica. Em muitos momentos, a dificuldade em uti-
lizarmos tais práticas reside na carência de exemplos práticos. Com uma aborda-
gem destinada a solucionar problemas próximos do cotidiano das organizações, a
familiarização com os conceitos ocorrerá de forma mais natural.
Larman [1997] explica o seu entendimento sobre os padrões de projeto:
Novo padrão deveria ser considerado uma contradição se ele descrever uma
nova ideia. O próprio termo “padrão” sugere algo longamente repetido. A ideia de
padrões de projeto não é expressar novas ideias de projeto. Muito pelo contrário –
grandes padrões tentam codificar conhecimento, idiomas e princípios existentes
testados e verdadeiros. Quanto mais familiar, antigo e amplamente usado, melhor.

Ou seja, todo conhecimento acerca dos padrões catalogados, são fruto de pes-
quisas minuciosas em diversos projetos de software, para que pudessem servir de
subsídio paras as obras consagradas da literatura.
Com exemplos práticos, esse livro tem como objetivo ser um guia comple-
mentar para a implementação desses padrões, tornando o aprendizado desse tema
prazeroso e facilitado.

Bons estudos!

15
1
Fundamentos de
padrões de projetos
Fundamentos de padrões de projetos
Nesse capítulo serão abordadas as razões que motivaram a criação dos padrões
de projeto de software, um histórico sobre o surgimento do conceito de padrões
de projeto, como descrevê-los e utilizá-los.
Segundo Gamma et al [1995]

Os padrões de projeto tornam mais fácil reutilizar projetos e arquiteturas bem-sucedi-


das. Expressar técnicas testadas e aprovadas as torna mais acessíveis para os desen-
volvedores de novos sistemas.

Os padrões de projeto ajudam a escolher alternativas de projeto que tornam


um sistema reutilizável e a evitar alternativas que comprometam a reutilização. Os
padrões de projeto podem melhorar a documentação e a manutenção de sistemas
ao fornecer uma especificação explícita de interações de classes e objetos e o seu
objetivo subjacente. Em suma, ajudam um projetista a obter mais rapidamente
um projeto adequado.
O estudo de padrões deve ser direcionado a aplicações práticas, inerentes
ao processo de desenvolvimento de software, como explanou brilhantemen-
te Freeman[2009]: Em vez de reutilização de código, com padrões você tem uma
reutilização de experiência. A experiência adquirida em projetos anteriores pode
ser materializada em artefatos de software que, além de solucionarem problemas
recorrentes, são um instrumento eficiente para a melhoria da qualidade de soft-
ware pretendida.
Em resumo, um padrão consolida o conhecimento de um profissional ou uma
equipe muito experiente,sobre um determinado assunto de tal forma que este
conhecimento pode ser transmitido para outros profissionais menos experientes.

OBJETIVOS
•  O que são padrões de projetos de software;
•  Um breve histórico da sua criação;
•  Como descrever um padrão;
•  Os principais Padrões de Projetos existentes.

capítulo 1 • 18
Introdução aos padrões de projeto

Desenvolver softwares utilizando o paradigma orientado a objetos é bastante


complexo, visto que a representação dos artefatos de negócio em artefatos de soft-
ware provoca a produção elevada de linhas de código.
Projetar esses mesmos softwares, tornando-os flexíveis (classes podem ser mo-
dificadas ou criadas, e, em seguida, acopladas de forma harmônica às demais im-
plementações) e reutilizáveis, torna essa tarefa ainda mais árdua.
Cada projeto de software deve ser específico ao problema proposto, porém
genérico o suficiente para acomodar futuras mudanças, gerando assim, um grande
desafio para as equipes de desenvolvimento.
É difícil obter um projeto flexível e reutilizável na primeira tentativa, pois ana-
listas inexperientes levam um tempo considerável para entender como um bom
projeto orientado a objetos deve ser desenvolvido.
Por outro lado, profissionais mais experientes já utilizam um conjunto de boas
práticas, que visam atingir a qualidade de projeto desejada:
•  Reutilizam soluções que funcionaram no passado;
•  Criam sistemas orientados a objetos que compartilham padrões de funcio-
namento das classes e da comunicação entre objetos;
•  Utilizam padrões que tornam os projetos mais elegantes;
•  Aplica o padrão de projeto sem ter que redescobrir seu funcionamento.

Se você conhece o padrão, uma série de decisões de projeto surgem auto-


maticamente. Um padrão de projeto registra uma determinada experiência bem
sucedida em projeto de software e cada padrão sistematicamente nomeia, explica e
avalia um projeto importante e recorrente.
O grande desafio é encontrar uma forma orgânica de fazer uso desse adven-
to poderoso nos projetos de software, sem que haja prejuízo na produtividade
da equipe, visto que um processo de desenvolvimento de software só é con-
siderado eficiente quando for simples o bastante para fazer parte do dia a dia
dos desenvolvedores.

Um breve histórico

Christopher Alexander, um arquiteto que escreveu dois livros descrevendo pa-


drões para a arquitetura de prédios e planejamentos urbanos (A PatternLanguage:

capítulo 1 • 19
Towns, Buildings, Construction [Oxford University Press, 1977] e The Timeless Way
of Building [Oxford Unisersity Press, 1979]). As ideias apresentadas nesses livros
são aplicadas a diversas áreas de conhecimento, incluindo a de software.
Em 1987, na conferência OOPSLA (Object-Oriented Programming, Systems,
Languages&Applications), Ward Cunnhingham e Kent Beck publicaram um ar-
tigo (que pode ser encontrado no site de Cunnhingham: <http://c2.com/doc/
oopsla87.html>, abordando o uso dos conceitos criados por Alexander, para
desenvolver cinco padrões para design de interfaces com o usuário (UI Design),
iniciando o uso do conhecimento de padrões de projeto no contexto do projeto
de softwares.
No início dos anos 90, Erich Gamma, Richard Helm, Ralph Johnson e
John Vlissides iniciaram trabalhos na área de padronização de software, que
acabou por gerar um dos mais influentes livros de computação dessa década:
Design Patterns - Elements of Reusable Object-Oriented (em português, Padrões de
Projeto – Soluções Reutilizáveis de Software Orientado a Objetos). Publicado
em 1994, o livro popularizou a ideia de padrões. Esse livro é comumente conhe-
cido como Gang of Four ou GoF (Grupo dos Quatro), fazendo uma referência a
quantidade de autores da obra.

O que é um padrão de projeto?

Segundo Alexander[1977], cada padrão descreve um problema que ocorre fre-


quentemente em nosso ambiente e descreve o núcleo da solução para o problema,
de uma forma que ela possa ser utilizada inúmeras vezes.
A definição criada por Alexander, mesmo em se tratando de projetos de cons-
trução, tem uma relação análoga com os projetos de software, visto que, os objetos
da construção precisam se relacionar de forma harmônica e relevante entre si,
como os objetos que compõem os projetos de software.
De acordo com Gamma et al [1995], um padrão possui quatro elemen-
tos essenciais:

Nome

É uma referência que podemos usar para descrever um problema de projeto,


suas soluções e consequências em uma ou duas palavras. Dar nome a um padrão
aumenta imediatamente o nosso vocabulário de projeto. Isso nos permite projetar

capítulo 1 • 20
em um nível mais alto de abstração. Ter um vocabulário para padrões permite-nos
conversar sobre eles com nossos colegas, em nossa documentação e até com nós
mesmos.
O nome torna mais fácil pensar sobre projetos e a comunicá-los, bem como
os custos e benefícios envolvidos, a outras pessoas. Encontrar bons nomes foi uma
das partes mais difíceis do desenvolvimento do nosso catálogo.

Problema

Define o momento em que o padrão deve ser aplicado. Ele explica o proble-
ma e seu contexto. Pode descrever problemas de projeto específicos, tais como
representar algoritmos como objetos. Pode descrever estruturas de classe ou objeto
sintomáticas de um projeto inflexível. Algumas vezes, o problema incluirá uma
lista de condições que devem ser satisfeitas para que faça sentido aplicar o padrão.

Solução

Descreve os elementos que compõem o padrão de projeto, seus relaciona-


mentos, suas responsabilidades e colaborações. A soluçãonão descreve um projeto
concreto ou uma implementação em particular porque um padrão é como um
gabarito que pode ser aplicado em muitas situações diferentes. Em vez disso, o
padrão fornece uma descrição abstrata de um problema de projeto e de como as
classes e objetos o resolve.

Consequências

São os resultados e as análises do balanço da utilização (vantagens e desvanta-


gens) do padrão no projeto. Embora as consequências sejam raramente mencio-
nadas quando descrevemos decisões de projeto, elas sãocríticas para a avaliação de
alternativas de projetos e para a compreensão dos custos e benefícios da aplicação
do padrão.
As consequências para o software frequentemente envolvem balanceamento
entre espaço e tempo. Elas também podem abordar aspectos sobre linguagens e
implementação. Uma vez que a reutilização é frequentemente um fator no projeto
orientado a objetos, as consequências de um padrão incluem o seu impacto sobre a

capítulo 1 • 21
flexibilidade, a extensibilidade ou a portabilidade de um sistema. Relacionar essas
consequências explicitamente ajuda a compreendê-las e avaliá-las.

Principais padrões de projetos

Os padrões abordados no livro Design Pattern (GAMMA et al, 1995), foram


divididos em 3 categorias:
•  Padrões de criação:
99 Abstract Factory;
99 Builder;
99 FactoryMethod;
99 Prototype;
99 Singleton.
•  Padrões estruturais:
99 Adapter;
99 Bridge;
99 Composite;
99 Decorator;
99 Façade(ouFacade);
99 Flyweight;
99 Proxy.
•  Padrões comportamentais:
99 Chain of Responsibility;
99 Command;
99 Interpreter;
99 Iterator;
99 Mediator;
99 Memento;
99 Observer;
99 State;
99 Strategy;
99 Template Method;
99 Visitor.

capítulo 1 • 22
Memento
Builder Proxy
saving state
of iteration
avoiding Adapter
hysteresis
defining
Iterator
traversais Bridge
creating
composites
enumerating
children composed
Command
using
adding
Decorator responsibilities Composite
to objects
sharing
composites adding
Visitor
operations
chaging skin definig
defining
versus guts Flyweight the chain
grammar
adding
operations
sharing
sharing
terminal Interpreter
strategies
symbols
sharing Chain of Responsibility
states

complex
Strategy State Mediator dependency Observer
management

defining
algorithm’s Template Method often uses
steps

configure factory
Prototype Factory Method
dynamically

implement using

single
Abstract Factory
instance

single
Singleton Facade
instance

Figura 1.1  –  Relacionamentos entre Padrões de Projeto (GAMMA et al, 1995).

PROPÓSITO

DE CRIAÇÃO ESTRUTURAL COMPORTAMENTAL


Factory Method Adapter (Class) Interpreter (231)
ESCOPO CLASSE (112) (140) Template Method (301)

Abstract Factory Adapter (object) Chain of Responsibility (212)


OBJETO (95) (140) Command (222)
Builder (104) Bridge (151) Iterator (244)
Prototype (121) Composite (160) Mediator (257)
Singleton (130) Decorator (179) Memento (266)
Flyweight (187) Observer (274)
Proxy (198) State (284)
Strategy (292)
Visitor (305)

Tabela 1.1  –  Espaço dos padrões de projeto (GAMMA et al, 1995).

capítulo 1 • 23
Há também os conhecidos padrões GRASP (General Responsibility Assignment
Software Patterns / Principles), abordados no livro Applying UML and Patterns – An
Introduction to Object-Oriented Analysisand Designde Craig Larman. São eles:
•  Padrões Básicos:
99 Information Expert (Especialista na Informação);
99 Creator (Criador);
99 High Cohesion (Alta Coesão);
99 Low Coupling (Baixo Acoplamento);
99 Controller (Controlador).

•  Padrões Avançados:
99 Polymorphism (Polimorfismo);
99 Pure Fabrication (Invenção Pura);
99 Indirection (Indireção);
99 Protected Variations (Variações Protegidas).

Apesar de diversos autores publicarem obras com catálogos de padrões de proje-


tos, os estudos desse livro serão concentrados nos padrões descritos anteriormente.
O detalhamento do uso desses padrões será apresentado nos capítulos seguin-
tes, relacionando a teoria com implementações práticas, voltadas para a solução de
problemas cotidianos, presentes nas organizações.
Outros padrões, relacionados à construção de interfaces gráficas para o usuá-
rio, utilização de arquitetura orientada à serviço (SOA), de uso de bancos de dados
relacionais ou orientados a objeto, demandam padrões específicos para essa finali-
dade, que não serão abordados explicitamente nessa publicação.

Antipattern (Anti-Padrão)

Andrew Koening, em um artigo intitulado “Patterns and Antipatterns”, para


o Journal of Object-Oriented Programming em 1995, publicou o seguinte texto,
já traduzido:
Um antipattern é exatamente como um padrão, exceto que em vez de uma solução, dá
algo que parece superficialmente como uma solução, mas não é um.

capítulo 1 • 24
Algumas soluções que são habitualmente utilizadas em projetos de software
foram catalogadas nesse artigo, visando alertar a comunidade de desenvolvedores
sobre a ineficácia dessas práticas.

Considerações

Quando surge a necessidade de se criar um padrão de projeto, esse vem do


surgimento de problemas rotineiros, que precisam de uma solução esquemática.
Logo, como explana Larman [1997]:

Novo padrão deveria ser considerado uma contradição se ele descrever uma nova
idéia. O próprio termo “padrão” sugere algo longamente repetido. A idéia de padrões
de projeto não é expressar novas idéias de projeto. Muito pelo contrário – grandes
padrões tentam codificar conhecimento, idiomas e princípios existentes testados e ver-
dadeiros. Quanto mais familiar, antigo e amplamente usado, melhor.

Novos padrões seguramente terão relevância quando a sua proposta for a de


trazer qualidade na elaboração de projetos de TI, através de soluções para proble-
mas que, de tão presentes, merecem uma sistematização na sua resolução.

Aspectos técnicos

A linguagem que adotada para a representação das implementações será o


Java 1.8, basicamente, por 2 motivos: Além de ser uma linguagem utilizada pela
Estácio em diversas disciplinas, há diversos frameworks que a utilizam para a im-
plementação de um grande número de padrões de projeto.
Serão utilizados diagramas UML (Casos de Uso, Classe, Sequência etc.) para
a representação dos problemas que os padrões resolvem. O Astah Community 7 é
o software escolhido para a construção dessas representações.
Os capítulos seguintes contêm códigos fonte em java, para aprimorar o pro-
cesso de aprendizagem.

capítulo 1 • 25
ATIVIDADES
01. Qual a importância dos padrões em um projeto de software?

02. Por que um padrão de projeto não descreve exatamente como a solução deve
ser implementada?

03. Desenvolver um padrão de projeto de software, onde haja os 4 elementos fundamen-


tais, sendo que esse padrão deve estar relacionado a um problema recorrente de sites de
e-commerce.

REFERÊNCIAS BIBLIOGRÁFICAS
GAMMA, ERICH et al. Design Patterns: Elements of Reusable Object-Oriented Software.
Addison-Wesley Professional. 1ª Edição. Estados Unidos da América: Addison-Wesley, 1995.
LARMAN, CRAIG. Utilizando UML e Padrões. 3ª Edição. Estados Unidos: Bookman, 2007.
FREEMAN,ELISABETH; FREEMAN,ERIC.Use a Cabeça! Padrões de Projetos (design Patterns).
2ª Edição. Estados Unidos: Alta Books, 2009.
KOENIG, ANDREW. Patterns and Antipatterns. Journal of Object-Oriented Programming, 1995; foi
re-impressoem: RISING, LINDA. The patterns handbook: techniques, strategies, and applications.
Cambridge, U.K.: Cambridge University Press, 1998.

capítulo 1 • 26
2
Padrões GoF
Padrões GoF
Será apresentado o catálogo de padrões de projeto dos autores Eric Gamma,
Richard Helm, Ralph Johnson e John Vlissedes, também conhecidos como GoF
(Gang of Four – Gangue/Grupo dos Quatro), que está publicado no livro, já re-
ferenciado, de 1995.
Esses padrões estão divididos pelas seguintes famílias de padrões: Padrões de
Criação ou de Construção, Padrões Estruturais e Padrões Comportamentais. Após
a apresentação das suas estruturas, serão apresentados exemplos práticos para a
implementação dessas soluções.
Cada padrão terá a seguinte estrutura:
•  Apresentação;
•  Aplicabilidade;
•  Estrutura;
•  Descrição;
•  Exemplo.

A implementação dos exemplos, contendo códigos fonte Java, estãodisponí-


veis no capítulo 5.

OBJETIVOS
Ao fim dessa unidade, será possível compreender:
•  O catálogo de padrões de projeto GoF;
•  As famílias de padrões;
•  A estrutura de cada padrão;
•  Como aplicar os padrões a problemas cotidianos.

Famílias de padrões

De acordo com o catálogo de padrões publicados em Gamma et al(1995), os


23 padrões foram divididos em 3 famílias ou classificações:

capítulo 2 • 28
Padrões de criação

De acordo com Gamma et al[1995]:

Os padrões de criação abstraem o processo de instanciação. Eles ajudam a tornar um


sistema independente de como seus objetos são criados, compostos e representados.
Um padrão de criação de classe usa a herança para variar a classe que é instanciada,
enquanto que um padrão de criação de objeto delegará a instanciação para outro objeto.

Seu objetivo principal é abstrair o processo de criação de objetos, ou seja, a


sua instanciação. Desta maneira o sistema não precisa se preocupar com questões
sobre, como o objeto é criado, como é composto, qual a sua representação real.
Quando se diz que o sistema não precisa se preocupar com a instanciação do
objeto quer dizer que, caso ocorra alguma mudança neste contexto, o sistema em
geral não será afetado. É um reflexo da flexibilidade adquiridaao adotar o uso dos
padrões de projeto.
Padrões de criação com escopo de classe vão utilizar herança para garantir essa
flexibilidade. Por exemplo, o padrão Factory Method pode criar várias subclasses
para criar o produto. Já os padrões com escopo de Objeto, como o Prototype,
delegam para um objeto (no caso o protótipo) a responsabilidade de instanciar
novos objetos.

Padrões estruturais

Segundo Gamma et al[1995]:

se preocupam com a forma como classes e objetos são compostos para formar es-
truturas maiores. Os padrões estruturais de classes utilizam a herança para compor
interfaces ou implementações.

Têm como propósito se preocupar em como as classes e objetos são compos-


tos, ou seja, como é a sua estrutura. O objetivo desses padrões é facilitar o design
do sistema, identificando maneiras de realizar o relacionamento entre as entida-
des, deixando o desenvolvedor livre desta preocupação.
Os padrões com escopo de classe utilizam a herança para compor implemen-
tações ou interfaces. O padrão Adapter, por exemplo, pode definir uma nova

capítulo 2 • 29
interface para adaptar duas outras já existentes, assim uma nova classe é criada
para adaptar uma interface a outra. Os padrões com escopo de objeto utilizam
a composição de objetos para definir uma estrutura. Por exemplo, o padrão
Composite define uma estrutura de hierarquia para classes primitivas e compostas
em um objeto.

Padrões comportamentais

Conforme Gamma et al[1995]:

se preocupam com algoritmos e a atribuição de responsabilidades entre objetos. Os


padrões comportamentais não descrevem apenas padrões de objetos ou classes, mas
também os padrões de comunicação entre eles.

Atuam sobre como responsabilidades são atribuídas às entidades, ou seja, qual


o comportamento das entidades. Estes padrões facilitam a comunicação entre os
objetos, distribuindo as responsabilidades e definindo a comunicação interna.
Padrões com escopo de classe utilizam herança para realizar a distribuição do com-
portamento. Um bom exemplo é o padrão Template Method, que fornece um algorit-
mo padrão e deixa as subclasses definirem alguns pontos da execução do algoritmo. Já
os padrões de objetos vão compor os objetos para definir a comunicação, como o pa-
drão Mediator, que define um objeto que realiza a comunicação muitos-para-muitos.

Abstract factory

Apresentação

De acordo com Gamma et al[1995]:

Fornecer uma interface para criação de famílias de objetos relacionados ou dependen-


tes sem especificar suas classes concretas.

Em outras palavras, provê uma interface para criação de famílias de objetos


relacionados sem especificar sua classe concreta.
Considere uma aplicação com interface gráfica que é implementada para
plataformas diferentes (Motif para UNIX e outros ambientes para Windows
e MacOS).

capítulo 2 • 30
As classes implementando os elementos gráficos não podem ser definidas es-
taticamente no código. Precisamos de uma implementação diferente para cada
ambiente. Até em um mesmo ambiente, gostaríamos de dar a opção ao usuário de
implementar diferentes aparências (look-and-feels).
Podemos solucionar este problema definindo uma classe abstrata para cada
elemento gráfico e utilizando diferentes implementações para cada aparência ou
para cada ambiente.
Ao invés de criarmos as classes concretas com o operador new, utilizamos uma
Fábrica Abstrata para criar os objetos em tempo de execução.
O código cliente não sabe qual classe concreta será utilizada.

Aplicabilidade

Pode ser usado quando:


•  Um sistema deve ser independente da forma como seus produtos são cria-
dos e representados;
•  Um sistema deve poder lidar com uma família de vários produtos diferentes;
•  Você quer prover uma biblioteca de classes de produtos, mas não quer reve-
lar as suas implementações, quer revelar apenas suas interfaces.

Estrutura

AbstractFactory

CreateProductA() Client
CreateProductB() AbstractProductA

ConcreteFactory1 ConcreteFactory2 ProductA2 ProductA1

CreateProductA() CreateProductA()
AbstractProductB
CreateProductB() CreateProductB()

ProductB2 ProductB1

Figura 2.1  –  Estrutura das classes do padrão Abstract Factory (GAMMA et al, 1995).

capítulo 2 • 31
Descrição

•  Abstract Factory: Declara uma interface para operações quecriam objetos


(produto) abstratos.
•  Concrete Factory: Implementa as operações que criam objetos (produtos)
concretos.
•  Abstract Product: Declara uma interface para um determinado objeto
(produto).
•  Product: Classe concreta que define um objeto (produto) a ser criado,
segundo a sua fábrica concreta relacionada. Implementa também a interface da
Abstract Product.
•  Cliente: Usa apenas as interfaces declaradas pela Abstract Factory e pelas
classes Abstrat Product.

Exemplo

Será implementado um sistema de escolha domotor gráfico usado em um


sistema operacional.
De acordo com a escolha, a visualização dos componentes será personalizada
através de uma renderização específica de cada motor.
Segue o diagrama de classes da solução:

GuiFactory Cliente Botao

MotifGuiFactory QtGuiFactory BotaoMotif BotaoQt

Janela

JanelaMotif JanelaQt

Figura 2.2  –  Exemplo de implementação do padrão Abstract Factory.

capítulo 2 • 32
Para a compreensão da estrutura, segue um resumo explicando cada classe:
•  Gui Factory: Classe abstrata que estabelece as regras de criação de GUI –
Graphic User Interface.
•  Motif Gui Factory: Classe concreta que implementa os componentes da
GUI Motif.
•  Qt Gui Factory: Classe concreta que implementa os componentes da
GUI QT.
•  Botao: Classe abstrata que representa todos os componentes Botao.
•  Botao Motif: Classe concreta de construção de componentes Botao, usan-
do a GUI Motif.
•  Botao Qt: Classe concreta de construção de componentes Botao, usando
a GUI Qt.
•  Janela: Classe abstrata que representa todos os componentes Janela.
•  Janela Motif: Classe concreta de construção de componentes Janela, usan-
do a GUI Motif.
•  Janela Qt: Classe de concreta construção de componentes Janela, usando
a GUI Qt.

Factory method

Apresentação

De acordo comGamma et al[1995]

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

Ou seja, ao invés de criar objetos diretamente em uma classe concreta, nós


definimos uma interface de criação de objetos e cada subclasse fica responsável
por criar seus objetos.
Seria como se, ao invés de ter uma fábrica de TVs, nós tivéssemos uma fábrica
da Sony, que cria TV da Sony, uma fábrica da CCE, que cria um modelo de TV
da CCE e assim por diante.
O grande diferencial ao utilizar esse padrão é a grande facilidade que ele ofere-
ce para a inclusão de novos produtos. Não é necessário alterar qualquer código an-
teriormente implementado. Apenas deve-se criar o produto e a sua fábrica. Todo
o código já escrito não será modificado.

capítulo 2 • 33
Aplicabilidade

Recomenda-se a utilização desse padrão quando uma classe não pode anteci-
par ou conhecer a classe dos objetos que deve criar. Outra possibilidade é quando
uma classe quer que suas subclasses especifiquem os objetos que criam. Quando
classes delegam responsabilidade a alguma das várias subclasses ajudantes, e dese-
ja-se localizar qual é a subclasse ajudante acessada.

Estrutura

Creator
Product
FactoryMethod() Product = FactoryMethod()
AnOperation()

ConcreteProduct ConcreteCreator

FactoryMethod() Return new ConcreteProduct

Figura 2.3  –  Estrutura das classes do padrão Factory Method (GAMMA et al, 1995).

Descrição

•  Product: Define a interface de objetos que o método fábrica cria.


•  Concrete Product: Implementa a interface de Product.
•  Creator: Declara o método fábrica, o qual retorna um objeto do tipo
Product. Creator pode também definir uma implementação por omissão do mé-
todofactory que retorna por omissão um objeto Concrete Product. Pode chamar
o métodofactory para criar um objeto Product.
•  Concrete Creator: Redefine o método-fábrica para retornar a uma instân-
cia de um Concrete Product.

capítulo 2 • 34
Exemplo

Um exemplo seria implementar uma suíte de escritório, como o Microsoft


Office. Através de uma interface única, poder acessar e manipular os diversos for-
matos de arquivo que a ferramenta contempla.

Aplicação Documento

criaDocumento() : Documento abrir()


novoDocumento() fechar()
abrirDocumento() gravar()

MinhaAplicação Meudocumento

criaDocumento() : Documento

Figura 2.4  –  Exemplo de Aplicação do padrão FactortyMethod.

Builder

Apresentação

Conforme Gamma et al[1995]:

Separar a construção de um objeto complexo da sua representação de modo que o


mesmo processo de construção possa criar diferentes representações.

Apresenta uma proposta para a construção de objetos, de natureza complexa,


que sejam separados da sua definição, para que possamos ter diferentes algoritmos
de construção permitindo diferentes representações para esses objetos.

Aplicabilidade

O padrão Builder visa organizar processos complexos de criação de objetos.


Em uma situação hipotética,surge a necessidade de se construir diferentes tipos de
computadores com diferentes componentes: servidor, computador para games e

capítulo 2 • 35
computador básico. Poderiam ser criados métodos em classes aleatórias, que criam
os computadores, porém as diferentes lógicas de criação do computador ficariam
espalhadas pelo código. Essa solução provavelmente decairia em um anti-pattern
(anti-padrão), abordado no capítulo 1, situação essa que o padrão Builder resolve.

Estrutura

builder
Director Builder

Construct() BuildPart()

for all objects in structure ConcreteBuilder Product


{builder–>BuildPart()}
BuildPart()
GetResult()

Figura 2.5  –  Estrutura das classes do padrão Builder (GAMMA et al, 1995).

Descrição

•  Builder: Especifica uma interface abstrata para criação de partes de um


objeto-produto.
•  Concrete Builder: Constrói e monta partes do produto pela implementa-
ção da interface de Builder; define e mantém a representação que cria;fornece uma
interface para recuperação do produto.
•  Director: Constrói um objeto usando a interface de Builder.
•  Product: Representa o objeto complexo em construção. Concrete Builder
constrói a representação interna do produto e define o processo pelo qual ele é
montado; inclui classes que definem as partes constituintes, inclusive as interfaces
para a montagem das partes no resultado final. 


Exemplo

Um sistema de conversão de texto em diversos formatos. Partindo de uma


mesma fonte de dados, diversos formatos de apresentação do texto podem ser
implementados, segundo o modelo a seguir:

capítulo 2 • 36
Builders
ConversorTexto
LeitorRTF
converterCaractere()
converterParagrafo() lerRTF()
converterFonte()

ConversorPDF ConversorTexto ConversorASCII


converterCaractere() converterCaractere()
converterCaractere()
converterParagrafo() converterParagrafo()
converterFonte() converterFonte()

Figura 2.6  –  Exemplo do padrão Builder (sistema conversor de texto).

Neste exemplo, o método ler RTF (classe Leitor RTF) percorre uma lista com
os tokens encontrados no texto de entrada (formato RTF) e, para cada tipo de
token, chama um método do objeto de tipo Conversor Texto.
Dependendo do formato escolhido para o texto de destino, será escolhida
uma implementação da classe Conversor Texto: Conversor PDF, Conversor TeX
ou Conversor ASCII. Cada uma destas classes implementa os métodos de acordo
com as características do formato desejado. A classe Conversor ASCII não im-
plementa os métodos converte Paragrafo() e converte Fonte(), pois este formato
(ASCII) não possui elementos de estilo.

Prototype

Apresentação

A intensão, segundo Gamma et al[1995], é:

Especificar os tipos de objetos a serem criados usando uma instância protótipo e criar
novos objetos pela cópia desse protótipo.

Indicado quando um sistema exige independência na forma de criação dos


seus produtos. Nesse caso, essa operação é realizada sem que a aplicação saiba qual
classe específica está sendo instanciada.

capítulo 2 • 37
Aplicabilidade

Em situações nas quais devem ser criadas instâncias de objetos a partir de hie-
rarquias de classes complexas e quando a classe de um objeto é conhecida somente
em tempo de execução são exemplos do uso deste padrão de projeto.

Estrutura

Client Prototype Prototype

Operation() Clone()

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

retun copy of self return copy of self

Figura 2.7  –  das classes do padrão Prototype (GAMMA et al, 1995).

Descrição

•  Prototype: Declara uma interface para clonar a si próprio.


•  Concrete Prototype: Implementa uma operação para clonar a si próprio.
•  Client: Cria um novo objeto solicitando a um protótipo que clone a si
próprio.

Exemplo

Neste exemplo é mostrado uma hierarquia de classes representando documen-


tos de formato ASCII e PDF que são criados através da classe Cliente.
A partir de duas instâncias prototípicas, ascii e pdf, o método criar Documento
cria clones de documentos de acordo com o tipo desejado.
A tarefa de realizar a criação da instância é implementada na classe Documento
e herdada por suas classes filhas, ASCII e PDF.

capítulo 2 • 38
<<Interface>>
Cloneable

Cliente Documento

+ Clone() : Documento

ASCII PDF

Figura 2.8  –  Exemplo de modelo para geração de documentos em vários formatos.

Singleton

Apresentação

Para Gamma et al[1995], o Singleton serve para:

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

Muitas aplicações necessitam garantir a ocorrência de apenas uma instância


de classes, pois tais objetos podem fazer uso de recursos cuja utilização deve ser
exclusiva, ou porque se deseja que os demais elementos do sistema compartilhem
um único objeto particular. Como exemplo, um filtro digital terá somente um
conversor do formato analógico para digital.
Um sistema de contabilidades será dedicado somente a uma companhia. O
acesso ao banco de dados ocorrerá a partir de um único objeto etc.

capítulo 2 • 39
Aplicabilidade

É recomendada a utilização quando deve haver uma única instância de uma


classe e esta deve ser a partir de um ponto de acesso bem conhecido.
A instância única deve ser extensível através de subclasses, possibilitando aos
clientes usar instância estendida sem alterar o seu código original.

Estrutura

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

Figura 2.9  –  Estrutura das classes do padrão Singleton (GAMMA et al, 1995).

Descrição

•  Singleto: Define uma operação Instance que permite aos clientes acessar
em sua única instância. Instance é um método estático da classe. Pode ser respon-
sável pela criação da sua própria instância única.

Exemplo

Singleton

– singleton : Singleton
– Singleton()
+ getInstance() : Singleton

Figura 2.10  –  Exemplo de classe Singleton.

capítulo 2 • 40
Adapter

Apresentação

De acordo com Gamma et al[1995]:

Converter a interface de uma classe em outra interface, esperada pelos clientes.


O Adapter permite que classes com interfaces incompatíveis trabalhem em conjunto –
o que, de outra forma, seria impossível.

Quando existir um determinado conjunto de classes com mesma responsabili-


dade, mas interfaces diferentes, o Adapter será o ponto de convergência de acesso
a qualquer uma delas.

Aplicabilidade

Quando surgir a necessidade de usar uma classe existente, mas sua interface
não corresponder à interface de que o projeto precisa.
Quando quiser criar uma classe reutilizável que coopere com classes não-rela-
cionadas ou não-previstas, ou seja, classes que não necessariamente tenham inter-
faces compatíveis.
Ao utilizar adaptadores de objetos e for necessário usar várias subclasses exis-
tentes, porém, for impraticável adaptar essas interfaces criando subclasses para
cada uma. Um adaptador de objeto pode adaptar a interface da sua classe-mãe.

Estrutura

Client Target Adaptee

Request() SpecificRequest

adaptee
Adapter

Request() adaptee–>SpecifcRequest()

Figura 2.11  –  Estrutura das classes do padrão Adapter (GAMMA et al, 1995).

capítulo 2 • 41
Descrição

•  Target: Define a interface específica do domínio que Client usa.


•  Client: Colabora com objetos compatíveis com a interface de Target.
•  Adaptee: Define uma interface existente que necessita ser adaptada.
•  Adapter: Adapta a interface do Adaptee à interface de Target.

Exemplo

Desenvolver um sistema que manipule imagens, e devem ser criadas APIs que
ofereçam essas funcionalidades. Será necessário ter um método para carregar a
imagem de um arquivo e outro para exibir a imagem na tela. Como pode o siste-
ma ser construído de tal forma que ele apresente uma interface independente de
qual API será utilizada? Para exemplificar,serão desenvolvidas classes de geração de
imagens baseadas nas bibliotecas OpenGL e GTK, como pode ser observado no
diagrama a seguir:

Gtklmagem OpenGLImagens

GtklmagemAdapter OpenGLImagensAdapter

<<interface>>
Imagem

Cliente

Figura 2.12  –  Exemplo de aplicação do padrão Adapter.

capítulo 2 • 42
Bridge

Apresentação

Segundo Gammaelat[1995]:

Desacoplar uma abstração da sua implementação, de modo que as duas possam variar
independentemente.

É uma solução muito parecida com o Adapter, porém, o Bridge fornece um ní-
vel de abstração maior, pois são separadas as implementações (Classes Concretas)
e as definições (Classes Abstratas), permitindo que cada um seja modificado de
forma independente.

Aplicabilidade

É recomendado quando se deseja evitar um vínculo permanente entre uma


classe abstrata e sua classe concreta. Isso pode ocorrer, por exemplo, quando a im-
plementação deve ser selecionada ou alterada em tempo de execução do software,
de acordo com a necessidade de negócio.

Estrutura

Cliente

Abstraction imp Implementor


Operation() Operationlmp()
imp–>Operationlmp()

ConcretelmplementorA ConcretelmplementorB
RefinedAbstraction Operationlmp() Operationlmp()

Figura 2.13  –  Estrutura das classes do padrão Bridge (GAMMA et al, 1995).

capítulo 2 • 43
Descrição

•  Abstraction: Define a interface da abstração; mantém uma referência para


um objeto do tipo Implementor.
•  Refined Abstraction: Estende a interface definida por Abstraction.
•  Implementor: Define a interface para as classes de implementação. Essa
interface não precisa corresponder exatamente à interface de Abstraction; de fato,
as duas interfaces podem ser bem diferentes. A interface de Implementor fornece
somente operações primitivas e Abstraction define operações de nível mais alto
baseadas nessas primitivas.
•  Concrete Implementor: Implementa a interface de Implementor e define
sua implementação concreta.

Exemplo

Será desenvolvido um programa multiplataformas, ou seja, que funcione em


diversos sistemas operacionais como Windows, Linux, Mac etc. O programa po-
derá utilizar diversas abstrações de janelas gráficas, como por exemplo, janela de
diálogo, janela de aviso, janela de erro etc. A modelagem da solução pode ser
analisada no diagrama a seguir:

<<interface>> JanelaAbstrata
IJanela

JanelaWindows JanelaLinux JanelaAviso JanelaDialogo

Figura 2.14  –  Diagrama de classe do exemplo do padrão Bridge.

capítulo 2 • 44
Composite

Apresentação

De acordo com Gamma et al[1995]:

Compor objetos em estruturas de árvore para representarem hierarquias partes-todo.


Composite permite aos clientes tratarem de maneira uniforme objetos individuais e
composições de objetos.

A ideia do Composite é criar uma classe base que contém toda a interface necessária
para todos os elementos (objetos individuais) e criar um elemento especial que agrega
outros elementos (composições de objetos), como pode ser observado na figura.

Aplicabilidade

Quando houver a necessidade de uma representação hierárquica de objetos no


formato parte-todo, ou, quando a classe utilizadora desses objetos não fizer distin-
ção no uso entre a composição de objetos ou os objetos individuais. Ambos serão
tratados da mesma forma na aplicação. Em uma estrutura de dados em árvore, os
nós raíz e os nós folha serão manipulados de maneira similar.

Estrutura

Client Component

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

Leaf Composite

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

Figura 2.15  –  Estrutura das classes do padrão Composite (GAMMA et al, 1995).

capítulo 2 • 45
aComposite

aLeaf aLeaf aComposite aLeaf

aLeaf aLeaf aLeaf

Figura 2.16  –  Estrutura comum de objetos Composite (GAMMA et al, 1995).

Descrição

•  Component: Declara a interface para os objetos na composição; imple-


menta comportamento-padrão para a interface comum a todas as classes, confor-
me apropriado; declara uma interface para acessar e gerenciar os seus componen-
tes-filhos; define uma interface para acessar o pai de um componente na estrutura
recursiva e a implementa, se isso for apropriado(opcional).
•  Leaf: Representa objetos-folha na composição. Uma folha não tem filhos;
define comportamento para objetos primitivos na composição.
•  Composite: Define comportamento para componentes que têm filhos; ar-
mazena os componentes-filho; implementa as operações relacionadas como filhos
presentes na interface de Component.
•  Client: Manipula objetos na composiçãoatravés da interface de Component.

Exemplo

Um sistema de gerenciamento de arquivos, que seja possível criar arquivos


concretos (texto, imagem etc.) e arquivos pastas, que armazenem esses arquivos
concreto. Para isso, é sugerida a seguinte modelagem.

capítulo 2 • 46
Cliente ArquivoComponent

– nome : String

ArquivoVideo ArquivoComposite

– listaArquivos : ArrayList<ArquivoComponent>

Figura 2.17  –  Diagrama de classe do exemplo do padrão Composite.

No modelo anterior, foi criado uma implementação para ArquivoComponent


chamada ArquivoVideo, visando um tratamento específico para vídeos, porém,
pode ser criada uma classe de arquivos genérica, dependendo da necessidade.

Decorator

Apresentação

O padrão referido tem como objetivo, segundo Gamma et al[1995]:

Dinamicamente, agregar responsabilidades adicionais a um objeto. Os Decorators for-


necem uma alternativa flexível ao uso de subclasses para extensão de funcionalidades.

Dado um objeto Coquetel, seja possível adicionar funcionalidades a ele, e


somente a ele, em tempo de execução.

Aplicabilidade

Usado para adicionar funcionalidades ao objeto em tempo de execução. Ao


contrário da herança, que aplica funcionalidades a todos os objetos especialistas, o
padrão Decorator permite aplicar funcionalidades apenas a um objeto específico.
Devido a essa característica, o padrão Decorator possui uma flexibilidade
maior que a herança tradicional. Além disso, como o Decorator aplica apenas as
funcionalidades necessárias ao objeto, nós evitamos o problema de classes sobre-
carregadas, que possuem funcionalidade que nunca serão utilizadas.

capítulo 2 • 47
Estrutura

Component
Operation()

component
ConcreteComponent Decorator
Operation() Operation() component–>Operation()

ConcreteDecoratorA ConcreteDecoratorA
Operation() Operation() Decorator::Operation();
addedState AddedBehavion() AddedBehavior();

Figura 2.18  –  Estrutura das classes do padrão Decorator (GAMMA et al, 1995).

Descrição

•  Component: Define a interface para objetos que podem ter responsabilida-


des acrescentadas aos mesmos dinamicamente.
•  Concrete Component: Define um objeto para o qual responsabilidades
adicionais podem ser atribuídas.
•  Decorator: Mantém uma referência para um objeto Componente, define
uma interface que segue a interface de Component.
•  Concrete Decorator: Acrescenta responsabilidades ao componente.

Exemplo

Em um sistema de pedidos de um bar, será implementado um módulode


montagem de coquetéis, que será estruturado de acordo com o modelo a seguir:

Cliente Coquetel CoquetelDecorator


– nome : Stringg
– coquetel : Coquetel
– preço : double

Vodka SucoLaranja SucoTomate

Figura 2.19  –  Diagrama de classe exemplo do padrão Decorator.

capítulo 2 • 48
Após a escolha da implementação do coquetel (Vodka, por exemplo), são es-
colhidas as implementações dos decoradores (Suco Laranja, Suco Tomate etc.) que
irão compor o coquetel.

Façade

Apresentação

Tem como finalidade, segundo Gamma et al[1995]:

Fornecer uma interface unificada para um conjunto de interfaces em um subsistema.


Façade define uma interface de nível mais alto que torna o subsistema mais fácil de
ser usado.

Este padrão visa abastrair a complexidades de um sistema e apresentar para a


classe cliente uma interface amigável para acesso às classes de negócio. Pode ser
implementado em uma única classe responsável por englobar as classes do sistema
ou pode ser dividido em várias interfaces, afim de promover uma modularização
das fachadas (tradução de façade).

Aplicabilidade

É indicado para a criação de uma interface simplificada para o sistema ou para


os subsistemas relacionados, quando é avaliado que o software se tornou comple-
xo o bastante que justificasse uma modularização mais evidente. É uma estrutura
que pode ser usada também para o controle das dependências internas das classes,
facilitando a criação de objetos.
A classe que representa a fachada, quando analisado o modelo arquitetural do
software (será detalhado no capítulo 4), é o ponto de entrada dos dados prove-
nientes da interface do usuário (View), que serão repassadas para a camada con-
troladora (Controller), onde esses dados serão processados.

capítulo 2 • 49
Estrutura

Classes do subsistema Façade

Figura 2.20  –  Estrutura das classes do padrão Façade (GAMMA et al, 1995).

Descrição

•  Façade: Conhece quais as classes do subsistema são responsáveis pelo aten-


dimento de uma solicitação; delega solicitações de clientes a objetos apropriados
do subsistema.
•  Classes de subsistema: Implementam a funcionalidade do subsistema; en-
carregam-se do trabalho atribuído a elas pelo objeto Façade; não têm conhecimen-
to da façade; isto é, não mantêm referências para a mesma.

Exemplo

Um sistema de gestão de recursos humanos contém, normalmente, diver-


sos módulos ou subsistemas. Utilizando uma interface (Façade), o acesso a todas
as classes de negócio se torna facilitado, como mostra o diagrama a seguir:

Funcionario

Cliente Facade Setor

Cargo

Figura 2.21  –  Diagrama de classe do exemplo de uso do padrão Façade.

capítulo 2 • 50
Flyweight

Apresentação

Segundo Gamma et al[1995], aplicação do Flyweight visa:

Usar compartilhamento para suportar eficientemente grandes quantidades de objetos


de granularidade fina.

O padrão Flyweight cria uma estrutura de compartilhamento de objetos pe-


quenos. Em um sistema que utiliza vários objetos de uma mesma classe ao mesmo
tempo, o padrão irá compartilhá-los de maneira eficiente deles dentro da aplicação.

Aplicabilidade

É recomendado em aplicações que utilizam uma grande quantidade de obje-


tos, e, esses mesmos objetos, demandam um enorme custo de uso de memória.
No caso de aplicações que não dependam rigidamente da identidade dos objetos,
também é uma oportunidade de utilização desses objetos.

Estrutura

FlyweighFactory flyweights Flyweigh


GetFlyweigh(key) Operation(extrinsicState)

if (flyweyght[key] exists) {
return existing flyweight;
} elise {
Create new flyweight;
add it to pool of flyweight;
return the new flyweight;
}
UnsharedConcreteFlyweight
ConcreteFlyweight
Operation(extrinsicState)
Operation(extrinsicState)
allState
IntrinsicState
Client

Figura 2.22  –  Estrutura das classes do padrão Flyweight (GAMMA et al, 1995).

capítulo 2 • 51
aClient aClient

flyweight
pool
aFlyweighFactory aConcreteFlyweight aConcreteFlyweight
flyweigh intrinsicState intrinsicState

Figura 2.23  –  Estrutura de compartilhamento dos objetos Flyweight (GAMMA et al, 1995).

Descrição

•  Flyweight: Declara uma interface através da qual flyweights podem receber


e atuar sobre estados extrínsecos.
•  Concrete Flyweight: Implementa a interface de Flyweight e acrescenta ar-
mazenamento para estados intrínsecos, se houver. Um objeto Concrete Flyweight
deve ser compartilhável. Qualquer estado que ele armazene deve ser intrínseco, ou
seja, independente do contexto do objeto ConcreteFlyweight.
•  Unshared Concrete Flyweight: Nem todas as subclasses de Flyweight
necessitam ser compartilhadas. A interface de Flyweight habilita o compartilha-
mento; ela não o força ou o garante. É comum para objetos Unshared Concrete
Flyweight não compartilhar objetos Concrete Flyweight como filhos em algum
nível da estrutura de objetos de Flyweight.
•  Flyweight Factory: Cria e gerencia objetos flyweight; garante que os fly-
weights sejam compartilhados apropriadamente. Quando um cliente solicita um
flyweight, um objeto Flyweight Factory fornece uma instância existente ou cria
uma, se nenhuma existir.
•  Client: Mantém uma referência para flyweight(s); computa ou armazena o
estado extrínseco do flyweight(s).

Exemplo

Na programação para jogos, são utilizados vários elementos distribuídos no


screen do jogador e esses elementos consomem uma quantidade significativa de
memória. Usando a estrutura de compartilhamento do flyweight, o uso da memó-
ria se torna mais eficiente. A seguir veja um modelo a ser utilizado:

capítulo 2 • 52
Client FlyweightFactory

– Flyweights : List<ElementoFlyweight
+ getFlyweight(componente : EnumElementos) : void

ElementoFlyweight Ponto

– Flyweights : List<ElementoFlyweight – x : int


– y : int

Elemento Imagem

– imagem : Imagem – nomeImagem : String


+ desenharImagem(ponto : Ponto) : void

Figura 2.24  –  Exemplo de modelagem do padrão Flyweight.

Proxy

Apresentação

Segundo Gamma et al[1995], o padrão Proxy:

Fornece um substituto (surrogate) ou marcador da localização de outro objeto para


controlar o acesso a esse objeto.

Será utilizada uma classe substituta à classe original, para controlar o acessoàs
suas propriedades. A classe Proxygarantirá que ela possa realmente substituir a
classe original, através da compreensão doseu comportamento.

Aplicabilidade

Quando houver uma necessidade de uso mais sofisticado das classes, não me-
ramente fazendo referência aos seus objetos. Para isso é importante conhecer os
tipos de Proxy:
•  Remote Proxies: São responsáveis pela codificação de uma solicitação e de
seus argumentos, bem como pelo envio da solicitação codificada para o objeto real
num espaço de endereçamento diferente;

capítulo 2 • 53
•  Virtual Proxies: Podem manter informações adicionais sobre o objeto real,
de maneira que possam postergar o acesso ao mesmo.
•  Protection Proxies: Verificam se quem chama tem as permissões de acesso
requeridas para executar uma consulta.
•  Smart Reference: Este proxy é apenas um substituto simples para executar
ações adicionais quando o objeto é acessado, por exemplo para implementar me-
canismos de sincronização de acesso ao objeto original.

Estrutura

Client Subject

Request()
...

RealSubject Proxy ...


realSubject
realSubject–>Request()
Request() Request() ...
... ...

Figura 2.25  –  Estrutura das classes do padrão Proxy (GAMMA et al, 1995).

Descrição

•  Proxy: Mantém uma referência que permite ao proxy acessar o objeto real
(real subject). O proxy pode referenciar um Subject se as interfaces de Real Subject
e Subject forem as mesmas; fornece uma interface idêntica a de Subject, de modo
que o proxy possa substitur o objeto real (real subject); controla o acesso ao objeto
real e pode ser responsável pela sua criação e exclusão. Outras responsabilidades
dependem do tipo de proxy.
•  Subject: Define uma interface comum para Real Subject e Proxy, de ma-
neira que um Proxy possa ser usado em qualquer lugar em que um Real Subject
é esperado.
•  Real Subject: Define o objeto real que o proxy representa.

capítulo 2 • 54
Exemplo

Em um sistema bancário, determinadas informações só podem ser acessadas


por pessoas com permissões para tal. Nesse caso, temos uma classe que contém
as informações bancárias (Banco) e uma classe estendida que adiciona a camada
de segurança necessária para controlar o acesso (BancoProxy), como podem ser
visualizadas no modelo a seguir:

Banco
Cliente
– totalClientes : int
– saldoClientes :double
+ getTotalClientes() : int
+ getSaldoClientes() : double

BancoProxy

– usuario : String
– senha : String
+ getTotalClientes() : int
+ getSaldoClientes() : double
+ checarPermissao : boolean

Figura 2.26  –  Exemplo de modelo que aplica o padrão Proxy.

Chain of responsibility

Apresentação

Tem como objetivo, segundo Gamma et al[1995]:

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.

O Chain of Responsibility acaba com as estruturas de decisão e estabelece uma


cadeia de objetos,onde há uma distribuição de responsabilidades entre eles até que
algum objeto responda a requisição.

capítulo 2 • 55
Aplicabilidade

Este padrão de objeto é comumente utilizado quando mais de um objeto é


capaz de tratar um pedido e o mesmo não é conhecido antecipadamente. O objeto
tratador da requisição deve ser reconhecido automaticamente. Uma requisição
deve ser tratada por um entre vários objetos sem que o objeto tratador seja espe-
cificado explicitamente.

Estrutura

successor
Client Handler

HandleRequest()

ConcreteHandler1 ConcreteHandler2

HandleRequest() HandleRequest()

Figura 2.27  –  Diagramado padrão Chain of Responsability (GAMMA et al, 1995).

Descrição

•  Handler: Define uma interface para tratar solicitações; implementa o elo


(link) ao sucessor(opcional).
•  Concrete Handler: Trata de solicitações pelas quais é responsável; pode
acessar seu sucessor; se o Concrete Handler pode tratar a solicitação, ele assim o
faz; caso contrário, ele repassa a solicitação para o seu sucessor.
•  Client: Inicia a solicitação para um objeto Concrete Handler da cadeia.

Exemplo

Um site de comércio eletrônico disponibiliza diversas formas de pagamento


para os seus clientes. Ao modelar uma forma de execução do pagamento, é neces-
sário selecionar uma entre diversas formas de pagamento disponíveis. A primeira

capítulo 2 • 56
ideia que surge é utilizar uma estrutura de decisão para verificar, dado um parâ-
metro, qual aforma de pagamento a ser utilizada na transação, porém será imple-
mentada a estrutura proposta pelo Chain of Responsability, que pode ser observada
no modelo a seguir.

Client PagamentoChain

+ efetuarPagamento(pagamento : EnumPagamentos) : void


+ verificarPagamento(pagamento : EnumPagamentos) : boolean

<<enum>> Boleto Cartao A Cartao B


EnumPagamentos
+ efetuarPagamento() : void + efetuarPagamento() : void + efetuarPagamento() : void

Figura 2.28  –  Exemplo de aplicação do padrão Chain of Responsability.

Command

Apresentação

De acordo com Gamma et al[1995]:

Encapsular uma solicitação como um objeto, desta forma permitindo parametrizar


clientes com diferentes solicitações, enfileirar ou fazer o registro (log) de solicitações
e suportar operações que podem ser desfeitas.

Este padrão de projeto representa comandos como objetos. Isto possibilita


realizar requisições a objetos sem o conhecimento de como a operação é executada
ou qual o destinatário da requisição.

Aplicabilidade

Nesse padrão, os objetos podem ser parametrizados pela ação a ser executada,
como, por exemplo, uma opção de menu ou um botão em uma barra de ferra-
mentas. Opcionalmente, um comando pode especificar uma operação de desfazer
(undo), permitindo reverter os efeitos no próprio comando. Assim, a interface
Command deve declarar uma operação (método) – undo() – para realizar tal
operação.

capítulo 2 • 57
Estrutura

Client Invoker Command


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

state

Figura 2.29  –  Estrutura das classes do padrão Command (GAMMA et al, 1995).

Descrição

•  Command: Declara uma interface para a execução de uma operação.


•  Concrete Command: Define uma vinculação entre um objeto Receiver
e uma ação; implementa Execute através da invocação da(s) correspondente(s)
operação(ões) no Receiver.
•  Client: Cria um objeto Concrete Command e estabelece o seu receptor.
•  Invoker: Solicita ao Command a execução da solicitação.
•  Receiver: Sabe como executar as operações associadas a uma solicitação.
Qualquer classe pode funcionar como um Receiver.

Exemplo

Uma loja que vende produtos e oferece várias formas de pagamento. Ao exe-
cutar uma compra, o sistema registra o seu valor e, de acordo com a forma de
pagamento selecionada, por exemplo, cartão de crédito, emite o valor total da
compra para o cartão de crédito do cliente, exibindo a quantidade de parcelas.
Outras formas de pagamento, como boleto bancário, teriam outras informações
relacionadas a compra, e assim por diante.

capítulo 2 • 58
Para este caso, vamos supor as seguintes classes para simplificar o exemplo:
Loja e Compra. As classes se relacionam, segundo o modelo a seguir:

<<interface>> PagamentoDebito
PagamentoCommand

+ processarComprar(compra : Compra) : void + processarComprar(compra : Compra) : void

PagamentoCredito
Cliente Loja
– qtdParcelas :int
+ executarCompra() : void + processarComprar(compra : Compra) : void

Compra PagamentoBoleto

+ processarComprar(compra : Compra) : void

Figura 2.30  –  Exemplo de aplicação do padrão Command.

Interpreter

Apresentação

A solução proposta pelo Interpreter, segundo Gamma et al[1995], é:

Dada uma linguagem, definir uma representação para a sua gramática juntamente
com um interpretador que usa a representação para interpretar sentenças dessa
linguagem.

Padrão de projeto utilizado para modelar a gramática para uma linguagem


específica a um domínio de problemas.

Aplicabilidade

Analisadores de expressões regulares, expressões algébricas e partituras musi-


cais (um dado som e sua duração), além de linguagens de consulta (query language)
e protocolos de comunicação são exemplos da aplicabilidade deste padrão de
projeto.

capítulo 2 • 59
Estrutura

Context

AbstractExpression
Client
Interpret(Context)

TerminalExpression NonterminalExpression

Interpret(Context) Interpret(Context)

Figura 2.31  –  Estrutura das classes do padrão Interpreter (GAMMA et al, 1995).

Descrição

•  Abstract Expression: Declara uma operação abstrata Interpret comum a


todos os nós na árvore sintática abstrata.
•  Terminal Expression: Implementa uma operação Interpret associada aos
símbolos terminais da gramática; é necessária uma instância para cada símbolo
terminal em uma sentença.
•  Non terminal Expression: É necessária uma classe desse tipo para cada
regra R::=R1R2...Rn da gramática; mantém variáveis de instância do tipo Abstract
Expression para cada um dos símbolos R1 a Rn; implementa uma operação
Interpret para símbolos não-terminais da gramática. Interpret chama a si próprio
recursivamente nas variáveis que representam R1 a Rn.
•  Context: Contém informação que é global para o interpretador.
•  Client: Constrói (ou recebe) uma árvore sintática abstrata que representa
uma determinada sentença na linguagem definida pela gramática. A árvore sintá-
tica abstrata é montada a partir de instâncias das classes Non Terminal Expression
e Terminal Expression; invoca a operação Interpret.

capítulo 2 • 60
Exemplo

Desenvolver um software para a interpretação de números, que estão represen-


tados por algarismos romanos, para algarismos arábicos (a representação numérica
amplamente conhecida por todos). Percorrer a String e procurar cada um dos
possíveis casos não é a melhor solução, pois dificultaria bastante a manutenção do
código. Uma solução recomendada éa criação de uma gramática. Para simplificar,
serão tratados apenas números de quatro dígitos. Iniciando a definição da gramá-
tica, um número romano é composto por caracteres que representam números
de quatro, três, dois ou um dígito, e será modelado de acordo com o diagrama
a seguir:

NumRomanoInterpreter

+ interpretar(contexto : Contexto) : void


+useUmaCasainput(contexto : Contexto) : void
Cliente Contexto + useDuasCasasinput(contexto : Contexto) : void
– input : String + um () : String
– output : int + quatro() : String
+ cinco() : String
+ nove() : String
+ multiplicador() : int

UnidadeRomano DezenaRomano CentenaRomano MilharRomano

+ um () : String + um () : String + um () : String + um () : String


+ quatro() : String + quatro() : String + quatro() : String + quatro() : String
+ cinco() : String + cinco() : String + cinco() : String + cinco() : String
+ nove() : String + nove() : String + nove() : String + nove() : String
+ multiplicador() : int + multiplicador() : int + multiplicador() : int + multiplicador() : int

Figura 2.32  –  Exemplo de modelagem do padrão Interpreter.

Iterator

Apresentação

Segundo Gamma et al[1995], o Iterator propõe:

Fornecer um meio de acessar, sequencialmente, os elementos de um objeto agregado


sem expor a sua representação subjacente.

capítulo 2 • 61
Este padrão de projeto oferece uma interface comum para que uma coleção
de objetos possa ser percorrida sem que a aplicação tome conhecimento de como
esses objetos estão agrupados.

Aplicabilidade

A utilização deste padrão de projeto possibilita à aplicação ter acesso ao con-


teúdo de um objeto agregado sem que sua representação interna seja exposta.
A coleção de objetos pode ser percorrida de diversas formas, de acordo com a
declaração da interface.

Aggregate Iterator
Client
CreateIterator() First()
Next()
IsDone()
Currentitem()

ConcreteAggregate
Concretelterator
CreateIterator()

return new Concretelterator(this)

Figura 2.33  –  Estrutura das classes do padrão Iterator (GAMMA et al, 1995).

Descrição

•  Iterator: Define uma interface para acessar e percorrer elementos.


•  Concrete Iterator: Implementa a interface de Iterator; mantém o controle
da posição corrente no percurso do agregado.
•  Aggregate: Define uma interface para a criação de um objeto Iterator.
•  Concrete Aggregate: Implementa a interface de criação do Iterator para
retornar uma instância do Concrete Iterator apropriado.

capítulo 2 • 62
Exemplo

Em uma empresa de TV a cabo, cada programador implementa os canais


de determinada categoria usando um tipo de coleção diferente (ArrayList, List,
Vector etc.). O Iterator é uma solução onde pode ser padronizada a ação de per-
curso dessas listas. A solução pode ser vista no modelo a seguir:

Cliente

CanaisAgregado IteradorCanais

+ primeiro() : void
+ proximo() : void
+ anterior() : void
+ ultimo() : void

CanaisNoticia Canal

Figura 2.34  –  Exemplo de modelagem de utilização do padrão Iterator.

Mediator

Apresentação

Segundo Gamma et al[1995], visa:

Definir um objeto que encapsula a forma como um conjunto de objetos interage.


O Mediator promove o acoplamento fraco ao evitar que os objetos se refiram uns aos
outros explicitamente e permite variar suas interações independentemente.

capítulo 2 • 63
O Mediator provê uma interface unificada a um conjunto de interfaces em
uma aplicação, onde um objeto promove a comunicação entre os vários objetos.
Desta forma, os objetos da aplicação deixam de manter relações entre si, reduzin-
do, assim, o acoplamento.

Aplicabilidade

A aplicabilidade para este padrão de projeto dá-se quando se quer separar a co-
municação que vários objetos têm entre si através de uma interface bem definida,
diminuindo, assim, suas interdependências.

Estrutura

mediator
Mediator Colleague

ConcreteMediator ConcreteColleague1 ConcreteColleague2

Figura 2.35  –  Estrutura das classes do padrão Mediator (GAMMA et al, 1995).

aColleague

mediator
aColleague aColleague

mediator mediator

aConcreteMediator

aColleague aColleague

mediator mediator

Figura 2.36  –  Estrutura comum de objetos no padrão Mediator (GAMMA et al, 1995).

capítulo 2 • 64
Descrição

•  Mediator: Define uma interface para comunicação com objetos de clas-


se Colleague.
•  Concrete Mediator: Implementa comportamento cooperativo através da
coordenação de objetos de classe Colleague; conhece e mantém seus colegas.
•  Colleague classes: Cada classe Collegue conhece seu objeto Mediator de
outra forma; cada colega se comunica com o seu mediador sempre que, de outra
forma, teria que se comunicar com outro colega.

Exemplo

Desenvolver um aplicativo de troca de mensagem, entre dispositivos móveis


de diversas plataformas (IOS, Android, Windows Phone etc.). Porém, como cada
plataforma tem uma implementação própria do seu serviço de troca de mensagem,
será utilizado o padrão Mediator para estabelecer um barramento de comunicação
entre esses dispositivos distintos. A estrutura pode ser conferida no modelo a seguir:

Cliente

Colleague <<interface>>
Mediator

AndroidColleague IOSColleague WindowsPhoneColleague MensagemMediator

Figura 2.37  –  Exemplo de aplicação do padrão Mediator.

Memento

Apresentação

De acordo com Gamma et al[1995]:

Sem violar o encapsulamento, capturar e externalizar um estado interno de um


objeto, de maneira que o objeto possa ser restaurado para esse estado mais tarde.

capítulo 2 • 65
Esse padrão oferece uma maneira simples de salvar estados internos de um
objeto. Basta salvar todas as informações necessárias em uma classe de armazena-
mento de estados(Memento) e mais tarde recuperá-las.

Aplicabilidade

Quando se deseja realizar algum processamento temporário em determinado


objeto da aplicação, o Memento é usado para capturar (snapshot) o estado (ou de
parte deste) do objeto em questão, esalvá-lo para que ele seja restaurado posterior-
mente ao processamento realizado, caso necessário, como uma espécie de cópia de
segurança (backup).

Estrutura

memento
Originator Memento Caretaker
SetMemento(Memento m) GetState()
CreateMemento() SetState()
state state

return new Memento(state) state = m–>GetState()

Figura 2.38  –  Estrutura das classes do padrão Memento (GAMMA et al, 1995).

Descrição

•  Memento: Armazena o estado interno do objeto Originator. O memento


pode armazenar pouco ou muito do estado interno do originator, conforme ne-
cessário e segundo critérios do seu originador; protege contra acesso por objetos
que não o originador. Mementos têm efetivamente duas interfaces. O Caretakervê
uma interface mínima do memento – ele somente pode passar o memento para
outros objetos. O originador, diferentemente, vê uma interface ampla, que lhe
permite acessar todos os dados necessários para se restaurar ao seu estado prévio.
Idealmente, somente o originador que produziu o memento teria o acesso permi-
tido ao seu estado interno.

capítulo 2 • 66
•  Originator: Cria um memento contendo um instantâneo do seu estado
interno corrente; usa o memento para restaurar o seu estado interno.
•  Caretaker: É responsável pela custódia do memento; nunca opera ou exa-
mina os conteúdos de um memento.

Exemplo

Desenvolver um editor de texto, com recurso de recuperação de estados an-


teriores ao atual (undo). Para isso será utilizado o padrão Memento, onde serão
armazenados os diversos estados da produção de um texto, como pode ser visto
no modelo a seguir:

Texto TextoCaretaker

Cliente + addTexto(texto : String) : void – estados : List<TextoMemento>


+ showTexto() : void
+ undoTexto() : void

TextoMemento

Figura 2.39  –  Exemplo de aplicação do padrão Memento.

Observer

Apresentação

De acordo com Gamma et al[1995]

Definir uma dependência um-para-muitos entre objetos, de maneira que quando um


objeto muda de estado todos os seus dependentes são notificados e atualizados
automaticamente.

Esse padrão tem como objetivo definir uma estrutura onde um objeto, que
esteja relacionado a vários outros, crie uma situação de observância, ou seja, quan-
do esse objeto muda de estado, todos os seus dependentes serão notificados sobre
essa mudança.

capítulo 2 • 67
Aplicabilidade

É utilizado quando se deseja observar um evento externo, tal como, uma


ação do usuário, o que é muito empregado em programação orientada a eventos.
Mudanças no valor de propriedades (variáveis) também empregam o uso deste
padrão de projeto.

Subject Obsever
observers
Attach(Observer) Update()
Detach(Observer) for all o in observers {
Notify() o–>Update()
}

ConcreteSubject ConcreteObserver
subject observerState=
GetState() Update()
SetState() subject–>GetState()
observerState
subjectState return subjectState

Figura 2.40  –  Estrutura das classes do padrão Observer (GAMMA et al, 1995).

Descrição

•  Subject: Conhece os seus observadores. Um número qualquer de objetos


Observer pode observar um subject; fornece uma interface para acrescentar e re-
mover objetos, permitindo associar e desassociar objetos observer.
•  Observer: Define uma interface de atualização para objetos que deveriam
ser notificados sobre mudanças em um Subject.
•  Concrete Subject: Armazena estados de interesse para objetos
ConcreteObserver; envia uma notificação para os seus observadores quando seu
estado muda.
•  Concrete Observer: Mantém uma referência para um objeto
ConcreteSubject; 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.

capítulo 2 • 68
Exemplo

Em um sistema de informações gerenciais, há diversas maneiras de se visuali-


zar os seus dados (gráfico, planilha etc.). A arquitetura desses dados deve ser cons-
truída de tal modo que qualquer alteração no conjunto de dados compartilhados
provoque alterações encadeadas em todas as formas de representação, garantindo
assim, que uma visão nunca tenha dados invalidados. Logo, o padrão Observer
apresenta uma solução que garante a integridade dos dados em todas as suas for-
mas de visualização, como pode ser observado no modelo a seguir:

DadosObserver
Cliente DadosSubject

– observers : List<DadosObderver> – dados : Dados


– dados : Dados
+ update() : void

Dados TabelaObserver PercentualObserver BarraObserver

– valorA : int + update() : void + update() : void + update() : void


– valorB : int
– valorC : int

Figura 2.41  –  Exemplo de aplicação do padrão Observer.

State

Apresentação

Segundo com Gamma et al[1995], o padrão State:

Permite a um objeto alterar seu comportamento quando o seu estado interno muda. O
objeto parecerá ter mudado sua classe.

A situações onde um objeto precisa ter seu comportamento alterado em tem-


po de execução. O padrão State permite que esse objeto tenha seu estado altera-
do,passando a impressão que o objeto muda sua classe.

capítulo 2 • 69
Aplicabilidade

É recomendado quando o comportamento de um objeto depende de seu esta-


do e este comportamento deve se modificar em tempo de execução (runtime) de
acordo com este estado e, também, quando muitas declarações condicionais (if-el-
se-if-else) dependem do estado do objeto. Neste caso, cada operação, que é realizada
de acordo com um valor constante, deve ser transferida para uma classe própria. Esta
transferência permite tratar o estado do objeto como um objeto por si só.

state
Context State

Request() Handle()

state–>Handle()

ConcreteStateA ConcreteStateB

Handle() Handle()

Figura 2.42  –  Estrutura das classes do padrão State (GAMMA et al, 1995).

Descrição

•  Context: Define a interface de interesse para os clientes; mantém uma ins-


tância de uma subclasse Concrete State que define o estado corrente.
•  State: Define uma interface para encapsulamento associado com um deter-
minado estado do Context.
•  Concrete State subclasses: Cada subclasse implementa um comportamen-
to associado com um estado do Context.

Exemplo

No jogo Mario Bros, a troca de estados do personagem do jogo é uma bastante


comum. Por exemplo, ao pegar uma flor de fogo o Mario pode crescer, se estiver

capítulo 2 • 70
pequeno, e ficar com a habilidade de soltar bolas de fogo, entre outras situações.
Adiante segue o diagrama de transição de estados que aborda o esquema de trocas:

Pequeno pegarCogumelo Grande

pegarPena
pegarFlor
sofrerDano
pegarPena
sofrerDano
sofrerDano Capa Fogo

pegarFlor
Morto
sofrerDano

pegarPena
pegarFlor

Figura 2.43  –  Diagrama de Transição de Estados do exemplo.

O State é um padrão que possibilita a gestão dos estados de formas bastante


escalável. Caso surja a necessidade de se criar um novo estado, basta se criar uma
classe que deve ser disposta de acordo com o modelo a seguir:

Cliente Mario <<interface>>


MarioState
– estado : MarioState

+ pegarCogumelo() : void + pegarCogumelo() : MarioState


+ pegarFlor() : void + pegarFlor() : MarioState
+ pegarPena() : void + pegarPena() : MarioState
+ sofrerDano : void + sofrerDano : MarioState

MarioPequeno MarioGrande MarioFogo MarioCapa MarioMorte

Figura 2.44  –  Exemplo de aplicação do padrão State.

capítulo 2 • 71
Strategy

Apresentação

De acordo com Gamma et al[1995], o Strategy busca:

Definir uma família de algoritmos, encapsular cada uma delas e torná-las intercam-
biáveis. Strategy permite que o algoritmo varie independentemente dos clientes que
o utilizam.

O padrão Strategy, além de encapsular os algoritmos da mesma família tam-


bém permite a reutilização do código. Ele define uma família de algoritmos e os
torna intercambiáveis, utilizáveis de acordo com a demanda.

Aplicabilidade

Permitir configurar uma classe com diversos comportamentos ou implemen-


tações. Ele permite abstrair as estruturas de dados, possivelmente complexas, utili-
zadas pelos diversos algoritmos. Isso possibilita a implementação de diversas regras
de negócios.

strategy
Context Strategy

ContextInterface() Algorithminterface

ConcreteStrategyA ConcreteStrategyB ConcreteStrategyC

Algorithminterface() Algorithminterface() Algorithminterface()

Figura 2.45  –  Estrutura das classes do padrão Strategy (GAMMA et al, 1995).

Descrição

•  Strategy: Define uma interface comum para todos os algoritmos supor-


tados. Context usa esta interface para chamar o algoritmo definido por uma
Concrete Strategy.

capítulo 2 • 72
•  Concrete Strategy: Implementa o algoritmo usando a interface de Strategy.
•  Context: É configurado com um objeto Concrete Strategy; mantém uma
referência para um objeto Strategy; pode definir uma interface que permite a
Strategy acessar seus dados.

Exemplo

Um sistema de cálculo do desconto impostos a serem pagos, de acordo com


cargo assumido pelos funcionários. De acordo com o cargo, o algoritmo que cal-
cula os impostos a serem pagos será diferente (Desconto de 10% -15% do salário,
desconto de 15% -20% etc.). Para implementarmos essa solução, usando o padrão
Strategy, serão criadas classes que implementarão uma mesma interface, porém,
cada classe conterá o algoritmo que resolva a especificidade de desconto, como
pode ser visto no modelo a seguir:

Cliente Funcionario <<interface>>


StrategyCalculoImposto

CalculoImposto10_15 CalculoImposto15_20

Figura 2.46  –  Exemplo de uso do padrão Strategy.

TemplateMethod

Apresentação

Tem como proposta, segundo Gamma et al[1995]:

Definir o esqueleto de um algoritmo em uma operação, postergando alguns passos


para as subclasses. Template Method permite que subclasses redefinam certos pas-
sos de um algoritmo sem mudar a estrutura do mesmo.

capítulo 2 • 73
Tem a função de generalizar um processo, em um nível mais genérico, em um
conjunto de passos, permitindo que etapas comuns sejam implementadas e que
etapas específicas tenham suas implementações realizadas por classes descenden-
tes. Em outras palavras, o Template Method permite que subclasses de uma classe
comum possam variar partes de um algoritmo mais geral.

Aplicabilidade

Partes de um algoritmo genérico são implementadas em uma classe comum, a


partir da qual suas subclasses podem ou devem implementar as partes específicas.
Algumas vezes um algoritmo genérico pode-se valer de passos que, se não imple-
mentados por classes descendentes, podem não existir ou são padronizados. Neste
caso, as classes descendentes podem escolher ou não realizar suas implementações.

Estrutura

AbstractClass ...
PrimitiveOperation1()
TemplateMethod() ...
PrimitiveOperation1() PrimitiveOperation2()
PrimitiveOperation2() ...

ConcreteClass

PrimitiveOperation1()
PrimitiveOperation2()

Figura 2.47  –  Estrutura das classes do padrão Template Method (GAMMA et al, 1995).

Descrição

•  Abstract Class: Define operações primitivas abstratas que as subclasses con-


cretas definem para implementar passos de um algoritmo; implementa um méto-
do-template que define o esqueleto de um algoritmo. O método-template invoca

capítulo 2 • 74
operações primitivas, bem como operações definidas em Abstract Class ou ainda
outros objetos.
•  Concrete Class: Implementa as operações primitivas para executarem os
passos específicos do algoritmo da subclasse.

Exemplo

Em um player de música, que oferece várias maneiras de ordenar a playlist de


reprodução (nome da música, nome do Autor, ano etc.). Uma ideia seria utilizar
o padrão Strategy e implementar uma classe que define o método de reprodu-
ção para cada tipo de reprodução da playlist. Esta seria uma solução viável, pois
manteríamos a flexibilidade para implementar novos modos de reprodução de
maneira bem simples. No entanto, o algoritmo para reprodução de uma playlist
é o mesmo, independente de qual modo está sendo utilizado. A única diferença é
a criação da playlist, que leva em consideração um dos atributos da música para
a ordenação. Para suprir esta dificuldade,é recomendado o uso padrão Template
Method, que pode ser conferido no modelo a seguir:

Playlist

+ addMusical()

Musica
Client <<enum>>
EnumModoReproducao – nome : String
– autor : String
– ano : int
– avaliacao : int

OrdenadorTemplate

+ ordenarMusical() : ArrayList<Musica>
+ isPrimeiro() : boolean

OrdenadorNome OrdenadorAutor OrdenadorAno OrdenadorAvaliacao

+ isPrimeiro() : boolean + isPrimeiro() : boolean + isPrimeiro() : boolean + isPrimeiro() : boolean

Figura 2.48  –  de aplicação do padrão Template Method.

capítulo 2 • 75
Visitor

Apresentação

De acordo com a publicação de Gamma et al [1995]:

Representar uma operação a ser executada nos elementos de uma estrutura de ob-
jetos. Visitor permite definir uma nova operação sem mudar as classes dos elementos
sobre os quais opera.

Pela descrição, é possível ver como o padrão deve ser usado. A sua ideia é
separar as operações que serão executadas em determinada estrutura de sua re-
presentação. Assim, incluir ou remover operações não terá nenhum efeito sobre a
interface da estrutura, permitindo que o resto do sistema funcione sem depender
de operações específicas.

Aplicabilidade

Este padrão de projeto pode ser utilizado quando uma estrutura de objetos
contém muitas classes com interfaces diferentes e se deseja executar operações
distintas ou sem relação entre si.
Isto evita a “poluição” de código e mantém códigos relacionados em uma
única classe.
Além disto, normalmente, a frequência de mudanças nas estruturas de objetos
é pequena, enquanto que novas operações podem ser acrescentadas a medida que
os requisitos se modificam.

capítulo 2 • 76
Estrutura

Client Visitor

VisitConcreteElementA(ConcreteElementA)
VisitConcreteElementB(ConcreteElementB

ConcreteVisitor1 ConcreteVisitor2
VisitConcreteElementA(ConcreteElementA) VisitConcreteElementA(ConcreteElementA)
VisitConcreteElementB(ConcreteElementB VisitConcreteElementB(ConcreteElementB

ObjectStructure Element

Accept(Visor)

ConcreteElementA ConcreteElementA

Accept(Visitor v) Accept(Visitor v)
OperationA() OperationB()

v–>VisitConcreteElementA(this) v–>VisitConcreteElementB(this)

Figura 2.49  –  Estrutura das classes do padrão Visitor (GAMMA et al, 1995).

Descrição

•  Visitor: Declara uma operação Visit para cada classe Concrete Element na
estrutura do objeto. O nome e a assinatura da operação identifica a classe que
envia a solicitação Visit ao visitante. Isso permite ao visitante determinar a classe
concreta do elemento que está sendo visitado. Então, o visitante pode acessar o
elemento diretamente através da sua interface específica.
•  Concrete Visitor: Implementa cada operação declarada por Visitor. Cada
operação implementa um fragmento do algoritmo definido para a correspondente
classe de objeto na estrutura. Concrete Visitor fornece o contexto para o algoritmo
e armazena o seu estado local. Esse estado frequentemente acumula resultados
durante o percurso da estrutura.
•  Element: Define uma operação Accept que aceita um visitante como um
argumento.

capítulo 2 • 77
•  Concrete Element: Implementa uma operação Accept que aceita um visi-
tante como um argumento.
•  Object Structure: Pode enumerar seus elementos; pode fornecer uma in-
terface de alto nível para permitir ao visitante visitar seus elementos; pode ser uma
composição (ver o padrão Composite), ou uma coleção, tal como uma lista ou
um conjunto.

Este padrão de projeto é aplicado quando há necessidade de se executar ope-


rações semelhantes em objetos de diferentes tipos agrupados em uma estrutura,
possivelmente uma coleção.
Ele também fornece um meio prático para o acréscimo de novos visitors que
estendem funcionalidades pré-existentes sem que o código seja modificado.

Exemplo

Um sistema que possua a necessidade de apresentar dados provenientes de


uma árvore binária. A estrutura da solução pode ser vista a seguir:

Client ArvoreBinaria No
+ <<create>> ArvoreBinaria(chaveRaiz : int) : void – chave : int
+ inserir(chave : int) : void – esquerdo : No
+ remover(chave : int) : void – direito : No
+ buscar(chave : int) : void
+ inserir(chave : int, no : No) : void
+ aceitarVisitante(visitor : ArvoreVisitor) void

Element

Accept(Visor)

ExibirIdentadoVisitor ExibirInOrderVisitor ExibirPostOrderVisitor ExibirPreOrderVisitor

+ visitar(no : No) : void + visitar(no : No) : void + visitar(no : No) : void + visitar(no : No) : void
+ visitar(no : No, qtdEspacos : int) : void

Figura 2.50  –  Diagrama do exemplo de implementação do padrão Visitor.

capítulo 2 • 78
ATIVIDADES
01. Qual o objetivo dos padrões Comportamentais, segundo o catálogo GOF?

02. Qual o objetivo dos padrões Estruturais, segundo o catálogo GOF?

03. Qual o objetivo dos padrões de Criação, segundo o catálogo GOF?

04. Em uma janela pode-se adicionar objetos como barras de rolagem, caixas de texto, la-
bels etc. Pode-se criar uma classe Auxiliar que será estendida pelos decoradores que irão in-
serir propriedades na janela. Qual o padrão de projetos que possibilita essa implementação?

05. Dois dos principais padrões utilizados atualmente são descritos a seguir:
I. Visa garantir que uma classe só tenha uma única instância e prover um ponto de acesso
global a ela.
II. Visa definir uma dependência um-para-muitos entre objetos para que quando um objeto
mudar de estado os seus dependentes sejam notificados e atualizados automaticamente.
Quais os padrões de projeto descritos?

REFERÊNCIAS BIBLIOGRÁFICAS
GAMMA, ERICH et al. Design Patterns: Elements of Reusable Object-Oriented Software.
Addison-Wesley Professional. 1ª Edição. Estados Unidos da América: Addison-Wesley, 1995.
LARMAN, CRAIG. Utilizando UML e Padrões. 3ª Edição. Estados Unidos da América: Prentice Hall,
2007.
FREEMAN,ELISABETH; FREEMAN,ERIC.Use a Cabeça! Padrões de Projetos (design Patterns).
2ª Edição. Estados Unidos da América: Alta Books, 2009.

capítulo 2 • 79
capítulo 2 • 80
3
Padrões GRASP
Padrões GRASP
Craig Larman, através do livro “Applying UML and Patterns”[1997], publicou
o catálogo de padrões de projeto GRASP (General Responsability Assignment
Software Patterns – Padrões de Software de Atribuição de Responsabilidade Geral),
De acordo comLarman[1997], sobre os padrões GRASP:

Os padrões de projeto tornam mais fácil reutilizar projetos e arquiteturas bem-sucedi-


das. Expressar técnicas testadas e aprovadas as torna mais acessíveis para os desen-
volvedores de novos sistemas.

OBJETIVOS
•  Os princípios sobre os padrões GRASP;
•  Os padrões GRASP básicos e avançados;
•  O detalhamento do problema e da solução proposta por cada padrão.

Princípios básicos do catálogo de padrões GRASP

Um pouco diferente do catálogo de padrões GoF, Larman resolveu referenciar


os padrões descritos em seu livro como princípios elementares na utilização de
boas práticas de programação orientada a objetos.

ESTUDO DE CASO
Um sistema de e-com-
Pedido ItemPedido –> Produto
merce, que tem a repre-
– descrição : String
sentação básica do modelo – dataPedido : Date – preço : double
na figura a seguir, será o
ItemPedido
ponto de partida para o
estudo de caso dos pa- – quantidade : int

drões abordados nesse


Figura 3.1  –  Diagrama de Classe de um sistema de
capítulo:
e-commerce.

capítulo 3 • 82
Padrões Básicos

Information expert (Especialista na informação);

Problema:

Qual é o princípio geral para a atribuição de responsabilidades aos objetos?

Solução:

Atribuir a responsabilidade ao especialista: a classe que tem as informações


necessárias para assumir a responsabilidade.
Tem como benefícios a manutenção do encapsulamento; o Fraco acoplamen-
to existente entre as classes, facilitando assim a de manutenção e a alta coesão,
onde os objetos envolvidos fazem apenas o que estiver relacionado à sua pró-
pria informação.
No fim, vários especialistas "parciais" podem colaborar;
Com o Expert, vários objetos inanimados podem ter ações;

Exemplo:

Há a necessidade de identificar quem é o responsável por conhecer o valor


total do pedido e o sub-total dos produtos de um pedido.

Especialista em Pedido

Pedido ItemPedido –> Produto

– dataPedido : Date – descrição : String


– preço : double
+ totalPedido() : double
ItemPedido
Adicionar o método
Adicionar o método – quantidade : int subTotal()
totalPedido
+ subTotal() : double

Figura 3.2  –  Responsabilidades identificadas.

capítulo 3 • 83
Creator (Criador);

Problema:

Quem deve ser responsável por criar uma nova instância de uma classe?

Solução:

Atribuir à classe B a responsabilidade de criar uma instância de A se pelo me-


nos umaou várias dessas situações forem verdadeiras:
•  B contém ou agrega objetos de A;
•  B registra instâncias de A;
•  B usa muitos objetos de A;
•  B tem os dados necessários para a inicialização de A que serão passados ao
construtor de A.

Exemplo:

Ainda no problema de e-commerce, como definir qual classe é responsável por


criar instâncias de ItemPedido?

Pedido agrega as instâncias


de ItemPedido

Pedido Produto
ItemPedido –>
– descrição : String
– dataPedido : Date
– preço : double
+ totalPedido() : double
+ adicionarItemVenda(qtd : int) : void ItemPedido

– quantidade : int
Adicionar o método
adicionarItemVenda() + subTotal() : double

Figura 3.3  –  Diagrama de Classe de um sistema de e-commerce, com as responsabilida-


des identificadas.

capítulo 3 • 84
Sobre o acoplamento de classes

Classes que, por natureza, são genéricas e que têm alta probabilidade de reuti-
lização deveriam ter acoplamento baixo.
O caso extremo do baixo acoplamento é o não acoplamento: contraria o prin-
cípio da orientação a objetos: objetos conectados, trocando mensagens entre si.
O acoplamento alto não é o problema em si. O problema é o acoplamento a
classes que, de alguma forma são instáveis: sua interface, sua implementação ou
sua mera presença.

Low coupling (Baixo acoplamento)

Acoplamento é uma medida de quanto um elemento está conectado a outro,


ou depende de outros.
Uma classe com acoplamento forte depende de muitas outras classes. Por isto,
acoplamento forte é indesejável. Outras razões para evitar o forte acoplamento:
– Classes mais difíceis de compreendidas isoladamente;
– Maior dificuldade de reutilização (seu uso depende da reutilização das
outras classes da qual ela depende);
– Classes mais sensíveis a mudanças nas classes associadas.

Problema:

Como prover baixa dependência entre classes, reduzir o impacto de mudanças


e obter alta reutilização?

Solução:

Atribuir as responsabilidades de modo que o acoplamento entre classes per-


maneça baixo. Use este princípio para avaliar alternativas.

Exemplo:

Com a criação de um objeto pagamento e associá-lo ao Pedido, que classe


deve ser responsável por essa operação?

capítulo 3 • 85
Segundo o padrão Creator, o módulo de cobrança do e-commerce deve criar
um objeto Pagamento e repassá-lo ao Pedido, como pode ser visto no diagrama de
comunicação a seguir:
1: efetuarPagamento() 2: criar()

<<actor>> Cobrança p : Pagamento


: Cliente

3: efetuarPagamento(p:Pagamento) : void

: Pedido

Figura 3.4  – 

O problema desse modelo é que foi gerado um acoplamento entre três classes.
Em busca do baixo acoplamento, o modelo deve ser refatorado, ignorando o
que foi estabelecido, nesse caso, pelo padrão Creator.
1: efetuarPagamento() 2: efetuarPagamento()
<<actor>>
Cobrança : Pedido
: Cliente
2.1: Criar

p : Pagamento

Figura 3.5  – 

Após a refatoração, haverá um acoplamento de apenas duas classes, ao invés de


três, como visto anteriormente.
Há diversos tipos de acoplamento que podem ser encontrados em um software:
•  Acoplamento de Dados:
– Objeto A passa objeto X para objeto B;
– Objeto X e B estão acoplados;
– Uma mudança na interface de X pode acarretar mudanças em B.

•  Acoplamento de Controle:
– Passar flags de controle entre objetos de forma que um objeto controle as
etapas de processamento de outro objeto;

capítulo 3 • 86
– Ocorrência comum:
•  Objeto A manda uma mensagem para objeto B;
•  B usa um parâmetro da mensagem para decidir o que fazer.

A seguir, um código fonte a ser analisado:


class Lampada {
publicfinalstaticint ON = 0;
publicvoidsetLampada(int valor){
if (valor == ON) {
// liga lampada
} else if (valor == 1) {
// desliga lampada
} else if (valor == 2) {
// pisca
}
}
}
Lampada lampada = newLampada();
lampada.setLampada(Lampada.ON);

Uma solução para diminuir o acoplamento funcional da classe anterior, é de-


compor o método em outros.
class Lampada {
public voidon() {
// liga lampada
}
public voidoff() {
// desliga lampada
}
public voidpisca() {
// pisca
}
}

capítulo 3 • 87
High cohesion (Alta coesão)

A coesão é uma medida do quão fortemente relacionadas e focalizadas são as


responsabilidades de uma classe.
A alta coesão é fundamental para a construção de um projeto modular.
Um exemplo de alta coesão seria uma classe Avião. Ela pode ser considerada
uma classe coesa se:
•  Possuir unicamente operações relacionadas ao Avião (voar, aterrissar, abas-
tecer etc.);
•  Esses métodos são ligados apenas aos objetos de Avião (um método para
listar aviões não estaria nessa classe).

Problema:

Como manter a complexidade sob controle? As classes que fazem muitas tare-
fas não relacionadas são:
•  Mais difíceis de entender;
•  Mais difíceis de manter e de reusar;
•  São mais vulneráveis à mudança.

Solução:

Atribuir uma responsabilidade para que a coesão se mantenha alta.

Exemplo:

Que classe é responsável por criar um pagamento e associá-lo a uma venda? De


acordo com o Creator, novamente,o objeto Cobrança deveria criar o Pagamento:
1: efetuarPagamento() 2: criar()
<<actor>>
Cobrança p : Pagamento
: Cliente

3: efetuarPagamento(p:Pagamento) : void

: Pedido

Figura 3.6  –  Diagrama de Comunicação da operação de cobrança.

capítulo 3 • 88
Suponha que isto ocorra várias vezes (outras classes), no decorrer do processo.
Pode ocorrer que Cobrança acumule métodos não relacionados a ele, provocando
uma baixa coesão da classe.
É mais coerente que o pagamento seja parte do Pedido, e não da Cobrança,
como aparecia na solução anterior; logo, Cobrança delega a responsabilidade a
Venda, aumentando a coesão de Cobrança:
1: efetuarPagamento() 2: efetuarPagamento()
<<actor>>
Cobrança : Pedido
: Cliente
2.1: criar()

p : Pagamento

Figura 3.7  –  Operação de cobrança refatorada, visando o padrão High Cohesion.

Considerações

Em um bom projeto OO, cada classe deve ser construída fazer unicamente o
trabalho a que ela se propõe. Nem mais, nem menos.
A baixa coesão da classe é identificada em algumas situações, tais como:
•  Quando alguns atributos começam a depender de outros.
•  Quando há subgrupos de atributos correlacionados na classe.

Quando bem projetada, a classe proporciona uma maior facilidade de com-


preensão do projeto, assim como um ambiente de manutenção mais simplificado,
além de que, nativamente, o processo de conquista da característica de baixo aco-
plamento é atingida.

Controller (Controlador)

Problema:

Que objeto, fora da camada de apresentação, deve receber e coordenar a soli-


citação da execução de uma operação?

capítulo 3 • 89
Solução:

Atribuir responsabilidades para receber ou lidar com um evento do sistema


para:
•  Uma classe que representa todo o sistema ou subsistema (façade controller);
•  Uma classe que representa cenário de caso de uso (controlador de caso de
uso ou de sessão).

Deve ser identificada a classe que deve ser responsável por receber os eventos do
sistema e, responder a tais requisições, como num processo de troca de mensagens.
Normalmente, quando bem projetado, o controlador não realiza o trabalho,
mas delega para outras subpartes do sistema.

Exemplo:

A construção de uma camada controladora pode seguir o mesmo princípio


proposto por Gamma et al [1995] com o padrão Façade.

Façade
Classes do subsistema

Figura 3.8  –  Estrutura das classes do padrão Façade (GAMMA et al, 1995).

Considerações

Os principais benefícios no uso do padrão Controllersão diminuir a sensibili-


dade da camada de apresentação em relação à lógica de domínio e criar a oportu-
nidade de controlar o estado do caso de uso.

capítulo 3 • 90
Mas, para um uso eficiente desse recurso, é necessário balancear a quantidade
de controladores. Um dos problemas mais comuns na sua implementação é o de
construir poucos controladores,provocando uma possível sobrecarga nessas classes.

Padrões avançados

Polymorphism (Polimorfismo)

Problema:

Como tratar alternativas baseadas no tipo? Como criar componentes de


software facilmente reutilizáveis?

Solução:

Quando alternativas ou comportamentos relacionados variam com o tipo


(classe), atribua as responsabilidades aos tipos usando operações polimórficas.

Exemplo:

No sistema de vendas existem vários tipos de taxas, cujo comportamento varia


de acordo com seu tipo.
Através de polimorfismo, podemos criar vários adaptadores semelhantes (mesma
interface), que recebem uma venda e se adaptam para diferentes cálculos de taxas.

<<interface>>
IcalculadoraTaxaAdapter
+ getTaxas() : List<Taxa>

TaxaPrincipalAdapter TaxaPromocionalAdapter <???>Adapter


+ getTaxas() : List<Taxa> + getTaxas() : List<Taxa> + getTaxas() : List<Taxa>

Figura 3.9  –  Estrutura das classes do padrão Polymorphism.

capítulo 3 • 91
Pure fabrication (Invenção pura)

Problema:

Que objeto deve ter a responsabilidade quando você não quer violar "Alta
Coesão" e "Baixo Acoplamento", mas as soluções oferecidas pelo "Especialista"
não são apropriadas?

Solução:

Atribua um conjunto coeso de responsabilidades a uma classe artificial que


não representa um conceito no domínio da aplicação, uma classe fictícia que pos-
sibilite alta coesão, baixo acoplamento e o reuso.

Exemplo:

Construir uma classe que será responsável por salvar um Pedido no banco
de dados
Apesar da classe Pedido ser a candidata lógica para ser a Expert para salvar a
si mesma em um banco de dados, isto levaria o projeto a ter baixo acoplamento,
alta coesão e baixo reuso.
Uma solução seria criar uma classe responsável somente por isto.

PedidoDB ClienteBD

+ insert(obj : Pedido) : void + insert(obj : Pedido) : void


+ update(obj : Pedido) : void + update(obj : Pedido) : void
+ delete(obj : Pedido) : void + delete(obj : Pedido) : void

Figura 3.10  –  Estrutura das classes do padrão Pure Fabrication.

Indirection (Indireção)

Problema:

Onde colocar uma responsabilidade de modo a evitar o acoplamento direto


entre duas ou mais classes? Como desacoplar objetos de modo a possibilitar o
baixo acoplamento e manter alta a possibilidade de reuso?

capítulo 3 • 92
Solução:

Atribuir a responsabilidade a um objeto intermediário que faça a mediação en-


tre componentes ou serviços de modo que eles não sejam diretamente acoplados.

Exemplo:

No sistema de e-commerce, para controlar as diversas formas de pagamento


e as suas peculiaridades, pode ser criada uma classe que sirva de interface para as
operações de pagamento.

Cartão

Cliente FormaPagamento Dinheiro

Cheque

Figura 3.11  –  Estrutura das classes do padrão Indirection.

Considerações

Deve haver, assim como os Controllers, um uso moderado de indireção. Assim


como a maior parte dos problemas em Ciência da Computação pode ser resolvida
por um nível adicional de indireção, o desempenho pode ser comprometido, logo,
equilíbrio é a palavra de ordem no uso dessa abordagem.

Protected variations (Variações protegidas)

Problema:

Como projetar objetos, subsistema e sistemas para que as variações ou instabi-


lidades nesses elementos não tenha um impacto indesejável nos outros elementos?

capítulo 3 • 93
Solução:

Identificar pontos de variação ou instabilidade potenciais e atribuir responsa-


bilidades para criar uma interface estável em volta desses pontos; Encapsulamento,
interfaces, polimorfismo, indireção e padrões são motivados por este princípio;
evite enviar mensagens a objetos muito distantes.

Exemplo:

No sistema de e-commerce, um pedido pode ser pago em várias moedas. Para


manter a compatibilidade cambial, uma classe Cambio é criada para intermediar
a conversão para a moeda desejada.

Real

Cliente Pedido Cambio Dolar

Euro

Figura 3.12  –  Estrutura das classes do padrão Protected Variations.

REFERÊNCIAS BIBLIOGRÁFICAS
GAMMA, ERICH et al. Design Patterns: Elements of Reusable Object-Oriented Software.
Addison-Wesley Professional. 1ª Edição. Estados Unidos da América: Addison-Wesley, 1995.
LARMAN, CRAIG. Utilizando UML e Padrões. 3ª Edição. Estados Unidos da América: Bookman,
2007.
FREEMAN,ELISABETH; FREEMAN,ERIC.Use a Cabeça! Padrões de Projetos (design Patterns).
2ª Edição. Estados Unidos da América: Alta Books, 2009.

capítulo 3 • 94
4
Arquitetura em
camadas
Arquitetura em camadas
A arquitetura de software consiste na organização dos seus componentes e suas
relações internas, assim como as relações externas, através das suas interfaces. Ela
pode ser influenciada por diversos aspectos do projeto, tais como: linguagens de
programação, sistemas operacionais e equipamentos. Por esse motivo, na medida
em que a evolução da engenharia de software acontece o tipo de arquitetura varia.
Há diversos modelos arquiteturais que podem servir de referência para os projetos
de software. Nesse capitulo, serão apresentados alguns desses modelos, suas devidas
estruturas e as vantagens e desvantagens na utilização de cada uma delas.

OBJETIVOS
•  O conceito de arquitetura de software;
•  A influência da arquitetura nos projetos de software;
•  Os tipos mais utilizados de arquitetura de software.

Conceito

Segundo Bass, Claments e Kazman[2003]:

A arquitetura de software de um programa ou sistema computacional é a estrutura


ou estruturas do sistema, que abrange os componentes de software, as propriedades
externamente visíveis desses componentes e as relações entre eles.

A Arquitetura de Software é definida como a organização fundamental de um


sistema na forma de componentes, seus relacionamentos com o ambiente, e os
princípios que conduzem seu design e evolução. Um projeto arquitetural envolve
a descrição formal dos elementos dos quais um sistema é construído, das intera-
ções entre os diversos elementos presentes, dos padrões de projeto que orientam
sua construção, e das limitações que os padrões naturalmente possuem.
Como em um projeto são produzidos diversos artefatos de software, nas suas
diversas fases, a Arquitetura de Software tem relação direta com a organização do

capítulo 4 • 96
relacionamento desses artefatos, de acordo com determinada visão do projeto,
como por exemplo:
•  Visão de Requisitos;
•  Visão do Usuário;
•  Visão de Implantação;
•  etc.

Todas essas visões propõem maneiras particulares de organização, que devem


manter-se em sinergia, pois apesar da natureza distinta dos artefatos produzidos
nas diversas visões, todos eles influenciam ou são influenciados, como pode ser
visto na figura 4.1, representando a matriz de rastreabilidade dos requisitos, que,
segundo o Guia PMBOK®, associa os requisitos às suas origens e os rastreia duran-
te todo o ciclo de vida do projeto.

IMPLEMENTO01

PROJ01

IMPLEMENTO02
ESPECIF02 PROJ02

ANALISE01 ESPECIF01 PROJ03

ESPECIF03

Figura 4.1 – Representação da dependência entre os artefatos de software e os requisi-


tos produzidos.

Uma maneira de abordar como a arquitetura influencia em um projeto de


software, está na observação da figura 4.2.
Design
x – interface entre componentes
banco de domínio
dados
xx

xx

xx

xx
servidor
web

Figura 4.2 – Representação do Design de um sistema (Silveira et al, 2011).

capítulo 4 • 97
O Design propõe uma estrutura que prevê os componentes necessários para a
implementação do software. A medida que o projeto avança, tanto a quantidade,
quanto a disposição desses componentes pode mudar de maneira significativa.
Na Arquitetura do Software, além de haver a preocupação com a disposição
desses componentes, deve ser analisado o comportamento da aplicação quando
esses elementos interagem entre si. Aspectos como performance e integridade
dos dados são indicadores que podem ser conquistados, quando aplicada uma
arquitetura madura e já testada, mas sem o acompanhamento adequado através
de atividades de SQA (Software Quality Assurance), a qualidade do projeto não
é conquistada sem a intervenção do Arquiteto de Software, papel esse que será
abordado no tópico a seguir.
Arquitetura

1
xx

xx

xx

Mudanças em xx
1 afetam 2
2

Figura 4.3 – Representação de um sistema, onde os "x" seriam artefatos de implementação


(Silveira et al, 2011).

Uma das motivações de projetar uma arquitetura adequada é a possibilidade


de aumento do tamanho e da complexidade dos softwares. Uma arquitetura bem
projetada pode trazer importantes benefícios, tais como, diminuição do tempo de
desenvolvimento, facilidade de manutenção e escalabilidade assegurada.

Arquiteto de software

Em resumo, o Arquiteto de Software é o profissional que promove a elabora-


ção, implantação e melhoria do modelo arquitetural do software. Larman[1997]
complementa dizendo que um arquiteto que não está a par da evolução do código
e do produto, não estar conectado com a realidade. Esse profissional tem a respon-
sabilidade de ter um olhar constante sobre o comportamento da arquitetura que

capítulo 4 • 98
ele ajudou a implantar no projeto. Mesmo com um histórico de implementações
bem-sucedidas, ao utilizar modelos comprovadamente eficientes, cada projeto
apresenta particularidades a medida que o software avança nas suas fases de pro-
dução, o que pode apresentar uma necessidade de mudanças no projeto.

Complexidade dos projetos de software

Segundo o famoso artigo No Silver Bullet – Essence and Accidents of Software


Engineering [1987], de Frederick Brooks, o autor frisa que não há uma tecnologia
capaz de resolver todos os problemas de um projeto de software. Ele faz uma ana-
logia bem-humorada entre o software e um lobisomem, alegando que esses seres se
transformam em algo pavoroso, de forma inesperada. A diferença é que para matar
um lobisomem, basta uma bala de prata. Para desenvolver um software, o cenário
é bem mais complexo. O autor ainda ressalta no artigo que num projeto de soft-
ware são encontrados problemas acidentais e essenciais. Os acidentais podem ser
acarretados por falhas de planejamento, escolhas tecnológicas equivocadas e falhas
humanas. Já os essenciais são aqueles inerentes a natureza complexa dos softwares.
Algumas características podem ser detectadas quando são analisados projetos de
software simples e complexos.

Projetos simples

•  Arquitetura não definida;


•  Podem ser desenvolvidos por processos Ad-hoc: sem relação com práticas já
consolidadas, e mesmo assim, pode ser bem-sucedido;
•  Podem ser construídos por uma ou poucas pessoas, sem a necessidade de
definição rígida de papéis;
•  Modelagem simples, sem a necessidade de muitos artefatos documentais;
•  Ferramentas simples.

Projetos complexos

•  Exigem arquiteturas formalizadas;


•  Trabalho em equipe com especialistas;
•  Modelagem abundante, que descreva com detalhes os artefatos a serem pro-
duzidos em cada fase do processo de desenvolvimento do software;

capítulo 4 • 99
•  Processos bem-definidos, com um nível de maturidade a ser considerado;
•  Conjunto de ferramentas complexo, tais como, ferramentas CASE, contro-
le de versão; ferramentas de integração contínua, IDEs etc.

A seguir, serão apresentados alguns modelos de arquitetura que podem ser


utilizados nos projetos de software.

Arquitetura monolítica

Foi bastante utilizada em projetos entre os anos de 1960-1980, voltados para


ambiente Mainframe, onde havia o predomínio do uso da linguagem Cobol
(COmmon Business Oriented Language). Esses softwares têm como característica o
processamento centralizado, com diversos terminais acessando a mesma Unidade
Central de Processamento (UCP ou CPU, em inglês). Esses terminais eram co-
nhecidos, vulgarmente, como "terminais burros", pois neles continham apenas
elementos de entrada e saída de dados, usados para interagir com a UCP.

Vantagens

•  Gerência da segurança facilitada e centralizada;


•  Manutenção das aplicações facilitada, por não ter arquitetura distribuída;
•  Facilidade de integração de sistemas.

Desvantagens

•  Processamento centralizado (escalabilidade vertical);


•  Alto custo (milhões de dólares/ano);
•  Arquiteturas de hardware, software e comunicação totalmente; proprietárias
levando à dependência do fornecedor;
•  Interface com o usuário limitada, baseada em console de terminal (shell);
•  Usuário sem autonomia.

Arquitetura multi-camadas

Consiste numa evolução do modelo cliente servidor introduzindo uma ou


mais camadas intermediárias de software. Cada camada fica responsável por parte

capítulo 4 • 100
da implementação do software, como se fossem elementos independentes, que se
comunicam mutuamente afim de manter a harmonia no processamento dos da-
dos. Em um modelo de 2 camadas, por exemplo, pode ser encontrada a seguinte
disposição dos componentes, segundo o diagrama a seguir:

Computador Servidor

<<interface>> <<TCP>> <<SGBD>>


App.exe MySQL

Figura 4.4  –  Representação de um sistema 2 camadas. Fonte: Autor.

Em um modelo de 3 camadas, há uma maior granularidade na distribuição


das responsabilidades, como pode ser visto no diagrama a seguir:

Computador Servidor Web

<<HTTP>>
Browser WepApp

<<TCP>>

Servidor BD

<<SSGBD>>
MySQL

Figura 4.5  –  Representação de um sistema 3 camadas. Fonte: Autor.

O modelo mais conhecido atualmente é o MVC, que se trata de um modelo


em 3 camadas, amplamente utilizado em projetos de software, seja por imple-
mentações personalizadas para cada projeto, seja por essa arquitetura que já está
implementada em diversos frameworks difundidos no mercado.

MVC

Criada pelo professor Trygve Reenskaug, da Universidade de Oslo, em 1978,


no famoso Centro de Pesquisas de Palo Alto da Xerox (PARC),a abordagem MVC
(Modelo/Visão/Controlador) define a lógica da implementação em camadas.

capítulo 4 • 101
mental
model
Controller computer
model
1
User *
Model
*
*
View
Tool

Figura 4.6  –  Representação da arquitetura MVC (REENSKAUG, 1979).

É composta por três tipos de objetos: Modelo, Visão e Controlador. O Modelo


é o objeto da aplicação, a Visão é a apresentação na tela e o Controlador é o que
define a maneira como a interface reage as entradas do usuário. A abordagem se-
para a Visão e os Modelos pelo estabelecimento de um protocolo do tipo inserção
/ notificação, aumentando a flexibilidade e reutilização.

Visão (View)

É constituída por artefatos de software, que proporcionam aos usuários a vi-


sualização dos dados e a apresentação dos elementos de interação que serão impu-
tados por esses usuários para processamento. As classes de fronteira (boundar y)
se encontram nessa camada.
Exemplos de camadas de apresentação: um sistema de menus baseados em
texto; uma página escrita em HTML ou XHTML com JavaScript apresentada em
um navegador de Internet; uma interface gráfica construída em algum ambiente
de programação (PyGTK, Swing do Java, Delphi etc.).

Controlador (Controller)

É composta de classes que implementam as regras do negócio no qual o siste-


ma está para ser implantado. Nessa camada, são efetuados os processamentos com
base nos dados armazenados ou nos dados de entrada, provenientes da camada
View. As validações de dados podem ser efetuadas, antes do seu processamento
principal. Como exemplo de implementações, as classes baseadas no padrão DAO
(Data Access Model), do catálogo Core J2EE, que contêm as operações de manipu-
lação do SGBD, conhecidas como CRUD.

capítulo 4 • 102
Modelo (Model)

Contém classes que se comunicam com outros sistemas para realizar tarefas
ou adquirir informações. Esta camada é implementada utilizando a tecnologia
de banco de dados, em que um SGBD executa em um ou mais nós de processa-
mento de alto desempenho. Como exemplo de implementações temos as Stored
Procedures presentes nos SGBDs (MySQL, Oracle SQL Server etc).

Vantagens

•  Permite acoplamento baixo entre as camadas, maior grau de manutenção e


reutilização de objetos;
•  Facilidade em novas implementações (ex.: incluir uma camada de apresen-
tação para utilização de ambiente web);
•  Mais adaptáveis a uma quantidade maior de usuários. Pode-se dividir a car-
ga de processamento do sistema entre as camadas de lógica da aplicação e acesso;
•  Quando bem projetada, oferece uma arquitetura independente de SGBD,
facilitando assim uma eventual migração de base de dados.

Desvantagens

•  Diminuir potencialmente o desempenho: a cada camada, as representa-


ções dos objetos sofrem modificações, e essas modificações levam tempo para se-
rem realizadas;
•  Dificuldade de implementação, na definição de quais classes irão compor
cada uma das camadas, e como comunicá-las entre si.

Arquitetura cliente-servidor

São aplicações desenvolvidas com dois macro módulos: o cliente e o servidor.


Esses módulos podem interagir entre si através de protocolos de rede conhecidos,
como os presentes na arquitetura TCP/IP ou protocolos privados, presentes em
redes locais. No módulo cliente, é oferecida uma interface para o usuário interagir,
seja através da entrada de dados ou da leitura das informações processadas. No
módulo servidor, encontra-se a aplicação implementada, contendo as regras de

capítulo 4 • 103
negócio das organizações. Na figura 4.7, há um desenho esquemático da interação
desses módulos:

Internet

Servidor
Clientes

Figura 4.7  –  Representação da arquitetura Cliente Servidor. Fonte: Wikipedia.

Metodologia de desenvolvimento

Em relação à programação, há uma grande quantidade de linguagens que im-


plementam os recursos necessários para se criar uma aplicação cliente servidor.
Java, PHP, C#, Delphi, são alguns exemplos que podem ser levados em considera-
ção em um projeto dessa natureza.
Essas linguagens trabalham com diversas bases de dados, sejam elas relacionais
(MySQL, SQL Server, Oracle, etc.), não relacionais (MongoDB) ou outras fontes
de dados em arquivo (texto, XML, JSON etc.).
Essa lista de tecnologias pode tanto ser usada para desenvolver projetos de
análise estruturada como de análise orientada a objetos.

Vantagens

•  Manutenção centralizada em um único local (Servidor);


•  Segurança dos dados armazenados. Os servidores podem controlar melhor
o acesso aos recursos. Podem garantir que apenas os clientes com permissão pos-
sam aceder e alterar os dados;
•  Como o armazenamento dos dados é centralizado, a sua atualização é refle-
tida automaticamente em todos os clientes;
•  Conta com inúmeras tecnologias desenvolvidas para garantir a segurança,
facilidade de interface do usuário e facilidade de uso;

capítulo 4 • 104
•  Os clientes podem ser acessados de diversas plataformas. Sejam elas regidas
por dispositivos (Celular, Tablet, PC etc), ou por sistemas operacionais (Linux,
Windows etc).

Desvantagens

•  Falta de comunicação direta entre clientes, sem sobrecarregar o servidor.


Clientes podem solicitar serviços, mas não podem oferecê-los para outros clientes;
•  Forte dependência de requisitos de qualidade (também conhecidos como
não-funcionais), tais como rede, computadores servidores robustos etc;
•  Ações de balanceamento de carga, que otimizem as requisições dos clientes
aos servidores, requerem equipe qualificada e equipamentos, o que encarece os
custos de implantação.

SOA

Service-Oriented Architecture ou Arquitetura Orientada a Serviço. É um con-


ceito de arquitetura,que tem como objetivo apresentar as funcionalidades imple-
mentadas pelas aplicações corporativas na forma de serviços. Esses serviços são
conectados a um componente conhecido como ESB (Enterprise Service Bus - bar-
ramento de serviços), que disponibiliza interfaces acessíveis através de webservices.
A arquitetura SOA é baseada nos princípios da computação distribuída e uti-
liza o paradigma Request/Reply (Requisição/Resposta), para estabelecer a comuni-
cação entre os sistemas clientes e os sistemas que implementam os serviços.
A comunicação entre Cliente e Serviço ocorre através uma padronização de
troca de informações. Tecnologias baseadas em XML como SOAP (Simple Object
Access Protocol) e WSDL (Web Services Description Language), desde o início dos
anos 2000, são consideradas padrão de interoperabilidade entre os sistemas.
Outros tipos de padrões, como REST(Representational State Transfer), baseado em
JSON (Java Script Object Notation), tem ganhado espaço como o aumento do de-
senvolvimento de aplicações móveis e baseadas em Cloud Computer (Computação
em Nuvem).

capítulo 4 • 105
Secure Policies
Discovery
Mechanism

pu
bli
d

sh
fin
Shield

Shield
Client interact Service
Secure
Identities Messaging Identities
Policies Policies
Trust Trust

Figura 4.8  –  Representação da arquitetura SOA, com elementos de segurança evidentes.


Fonte: W3C.

Vantagens

•  Abstração: Serviço totalmente abstraído da sua implementação, podendo


ser acessado por clientes desenvolvidos nas mais diversas linguagens e plataformas;
•  Flexibilidade: Isolando a estrutura de um serviço as mudanças são feitas
com maior facilidade;
•  Governança: É uma ferramenta poderosa para o gerenciamento dos proces-
samentos de negócio;
•  Integração: A integração com outros serviços, aplicativos e sistemas legados ;
•  Interoperabilidade: Disponibilizar serviços independentemente da plata-
forma e tecnologia;
•  Manutenibilidade: Com baixo acoplamento, facilita a manutenção
dos serviços;
•  Padronizado: É baseado no uso de padrões, seja ele SOAP ou JSON,
por exemplo;
•  Produtividade: Com o reuso, a equipe de desenvolvimento pode reutilizar
serviços em outros projetos, diminuindo o tempo de desenvolvimento;
•  Reutilização: O serviço pode ser reutilizado para outras aplicações.

capítulo 4 • 106
Desvantagens

•  Complexidade: Uma grande quantidade de serviços pode dificultar


o gerenciamento;
•  Performance: A performance depende da rede e do servidor onde o serviço
está publicado. Quanto mais requisitos de qualidade estiverem presentes na imple-
mentação, mais difícil é gerenciar a performance;
•  Robustez: Caso uma exceção acontecer, não há como reverter o processo. A
gestão de transações deve ser bem implementada;
•  Disponibilidade: Uma queda na rede ou no servidor deixa todos os servi-
ços indisponíveis;
•  Segurança: Os serviços estão disponíveis na rede, qualquer aplicativo pode
consumir esse serviço, os dados são trafegados pela rede podendo ser intercepta-
dos. Adoção de camadas de criptografia como SSL(Secure Socket Layer) podem
encarecer o projeto.

ATIVIDADES
01. É possível desenvolver um projeto de software sem arquitetura? Justifique.

02. Com a evolução tecnológica constante, o papel do arquiteto de software em um projeto


pode ficar ameaçado? Justifique.

03. Um projeto MVC pode ter mais de 3 camadas? Justifique.

04. É recomendável utilizar arquitetura SOA para comunicar sistemas distintos, porém, que
tenham sido desenvolvidos na mesma linguagem e plataforma? Justifique.

05. Após o conhecimento adquirido sobre as arquiteturas de software, é possível, em um


mesmo projeto, utilizar mais de uma arquitetura? Justifique

capítulo 4 • 107
REFERÊNCIAS BIBLIOGRÁFICAS
BASS, L., P. CLEMENTS, R. KAZMAN. Software Architecture in Practice. 2ª Edição. Addison-Wesley,
2003.
BROOKS, F.P.No Silver Bullet – Essence and Accidents of Software Engineering, IEEE, 1987.
FREEMAN, ELISABETH; FREEMAN, ERIC. Use a Cabeça! Padrões de Projetos (design Patterns).
2ª Edição. Estados Unidos da América: Alta Books, 2009.
GAMMA, ERICH et al. Design Patterns: Elements of Reusable Object-Oriented Software.
Addison-Wesley Professional. 1ª Edição. Estados Unidos da América: Addison-Wesley, 1995.
LARMAN, CRAIG. Utilizando UML e Padrões. 3ª Edição. Estados Unidos da América: Prentice Hall,
2007.
PMI. A Guide to the Project Management Body of Knowledge. 5° Edição, 2013.
REENSKAUG, TRYGVE. MODELS – VIEWS – CONTROLLERS. Estados Unidos da América: Xerox
PARC technical note, 1979.
SILVEIRA, PAULO et al. Introdução à Arquitetura e Design de Software – Uma Visão Sobre a
Plataforma Java. 1ª Edição. Brasil: Elsevier, 2011.

capítulo 4 • 108
5
Aplicações práticas
dos padrões GoF
Aplicações práticas dos padrões GoF
A vida cotidiana apresenta problemas que inspiram a academia a desenvolver
soluções que facilitem a vida da sociedade de um modo geral. Essa é uma das mis-
sões dos profissionais que trabalham com tecnologia da informação.
Nesse capítulo serão apresentadas soluções práticas do uso dos padrões de
projeto abordados no livro Design Patterns (Gamma et al, 1995), e que foram
exemplificadas no capítulo 2.
São códigos fonte, comentados previamente, que fornecerão a visão arquite-
tural das implementações, quanto ao uso dos padrões de projeto do catálogo GoF.

OBJETIVOS
•  Descrever problemas reais, presentes no cotidiano das organizações;
•  Codificar as soluções, de acordo com o catálogo de padrões abordado.

Considerações sobre os exemplos

Os códigos, que implementam as soluções propostas pelo catáogo de padrões


GoF, foram desenvolvidos em linguagem Java, na IDE Eclipse, que pode ser bai-
xada no link <http://www.eclipse.org/>. Esse ambiente é apenas de uma recomen-
dação, visto que há diversas soluções disponíveis no mercado.
Com relação às implementações, não foi utilizada qualquer biblioteca externa
do Core do Java, nem mesmo funções do pacote java.util, pois o propósito é su-
gestionar a arquitetura das soluções e não implementá-las na sua totalidade. Em
outras palavras, no escopo de implementação, na maioria dos casos, são encon-
tradas saídas para o console (System.out.println), que sugerem a codificação do
algoritmo em questão.

Abstract factory

Aplicação que seleciona qual a bliblioteca gráfica para a visualização da aplica-


ção (Motif, Qt, Win32 etc.), sem que o tema escolhido influencie na implementa-
ção do restante da aplicação, que pode ser conferido no programa a seguir:

capítulo 5 • 110
GuiFactory.java
package br.estacio.abstractFactory;
abstract class GuiFactory {
public static GuiFactory obterFactory() {
if(Config.obterGuiAtual() == Config.MotifGui) {
return new MotifGuiFactory();
} else {
return new QtGuiFactory();
}
}

public abstract Botao criarBotao();
public abstract Janela criarJanela();
}

MotifGuiFactory.java
package br.estacio.abstractFactory;
class MotifGuiFactory extends GuiFactory {
public Botao criarBotao() {
return new BotaoMotif();
}
}

QtGuiFactory.java
package br.estacio.abstractFactory;
class QtGuiFactory extends GuiFactory {
public Botao criarBotao() {
return new BotaoQt();
}
}

Botao.java
package br.estacio.abstractFactory;
abstract class Botao {
public abstract void desenhar();
}

capítulo 5 • 111
BotaoMotif.java
package br.estacio.abstractFactory;
class BotaoMotif extends Botao {
publicvoid desenhar(){
System.out.println("Eu sou um botao Motif!");
}
}

BotaoQt.java
package br.estacio.abstractFactory;
class BotaoQt extends Botao {
public void desenhar(){
System.out.println("Eu sou um botao Qt!");
}
}

Janela.java
package br.estacio.abstractFactory;
abstract class Janela {
public abstract void desenhar();
}

JanelaMotif.java
package br.estacio.abstractFactory;
class JanelaMotif extends Janela {
public void desenhar(){
System.out.println("Eu sou uma janela Motif!");
}
}

JanelaQt.java
package br.estacio.abstractFactory;
class JanelaQt extends Janela {
public void desenhar(){
System.out.println("Eu sou uma janela Qt!");
}
}

capítulo 5 • 112
Cliente.java
package br.estacio.abstractFactory;
public class Cliente {
public static void main(String[] args) {
GuiFactory factory = GuiFactory.obterFactory();
Botao botao = factory.criarBotao();
botao.desenhar();
}
}

Factory method

Implementar uma suíte de escritório, como o Microsoft Office. Através de


uma interface única, os diversos formatos de arquivo que a ferramenta contempla
poderão ser acessados e manipulados.

Aplicacao.java
package br.estacio.factoryMethod;
abstract class Aplicacao {
private Documento doc;
//Abstração do Factory Method
abstract Documento criaDocumento();
void novoDocumento() {
this.doc = this.criaDocumento();
}
void abrirDocumento() {
this.doc.abrir();
}
}

MinhaAplicacao.java
package br.estacio.factoryMethod;
class MinhaAplicacao extends Aplicacao {
@Override
Documento criaDocumento() {
return new MeuDocumento();

capítulo 5 • 113
}
}

Documento.java
package br.estacio.factoryMethod;
abstract class Documento {
void abrir() {
System.out.println("Documento:Abrir documento!");
}
void fechar() {
System.out.println("Documento:Fechar documento!");
}
void salvar() {
System.out.println("Documento:Salvar documento!");
}
}

MeuDocumento.java
package br.estacio.factoryMethod;
class MeuDocumento extends Documento {
private String word = "Word";
private String excel = "Excel";
String getWord() {
return this.word;
}
String getExcel() {
return this.excel;
}
}

Cliente.java
package br.estacio.factoryMethod;
public class Cliente {
public static void main(String[] args) {
MinhaAplicacao app = new MinhaAplicacao();

capítulo 5 • 114
Documento doc = app.criaDocumento();
doc.abrir();
MeuDocumento myDoc = new MeuDocumento();
System.out.println("Documento:"+myDoc.getWord());
}
}

Builder

Um sistema de conversão de texto em diversos formatos. Partindo de uma


mesma fonte de dados, diversos formatos de apresentação do texto podem ser
criados (TeX, ASCII, PDF etc). Segue adianteo algoritmo de implementação:

ConversorTexto.java
package br.estacio.builder;
abstract class ConversorTexto {
void converterCaractere(charc) {}
void converterParagrafo() {}
void converterFonte(Fonte f) {}
}

ConversorPDF.java
package br.estacio.builder;
class ConversorPDF extends ConversorTexto {
void converterCaractere(char c) {
System.out.println("Caractere PDF");
}
void converterParagrafo() {
System.out.println("Paragrafo PDF");
}
void converterFonte(Fonte f) {
System.out.println("Fonte PDF");
}
}

capítulo 5 • 115
ConversorTeX.java
package br.estacio.builder;
class ConversorTeX extends ConversorTexto {
void converterCaractere(char c) {
System.out.println("Caractere TeX");
}
void converterParagrafo() {
System.out.println("Paragrafo TeX");
}
void converterFonte(Fonte f) {
System.out.println("Fonte TeX");
}
}

ConversorASCII.java
package br.estacio.builder;
class ConversorASCII extends ConversorTexto {
void converterCaractere(char c) {
System.out.println("Caractere ASCII");
}
}

LeitorRTF.java
package br.estacio.builder;
class LeitorRTF {
private ConversorTexto conversor;
LeitorRTF(ConversorTexto c) {
this.conversor = c;
}

publicvoid lerRTF() {
List<Token>tokens = obterTokensDoTexto();
for (Token t : tokens) {
if (t.getTipo() == Token.Tipo.CARACTERE){
conversor.converterCaractere(t.getCaractere());
}

capítulo 5 • 116
if (t.getTipo() == Token.Tipo.PARAGRAFO) {
conversor.converterParagrafo();
}
if (t.getTipo() == Token.Tipo.FONTE) {
conversor.converterFonte(t.getFonte());
}
}
}
}

Cliente.java
package br.estacio.builder;
public class Cliente {
public static void main(String[] args) {
ConversorTexto conversor;
if (args[0].equals("pdf")) {
conversor = new ConversorPDF();
} else if (args[0].equals("tex")) {
conversor = new ConversorTeX();
} else {
conversor = new ConversorASCII();
}
LeitorRTF leitor = new LeitorRTF(conversor);
leitor.lerRTF();
}
}

Prototype

Nesse exemplo é mostrado uma hierarquia de classes representando documen-


tos de formato ASCII e PDF, que são criados através da classe Cliente. A partir
de duas instâncias prototípicas, ascii e pdf, o método criar Documento cria clones
de documentos de acordo com o tipo desejado. A tarefa de realizar a criação da
instância é implementada na classe Documento e herdada por suas classes filhas,
ASCII e PDF.

capítulo 5 • 117
DocumentoPrototype.java
abstract class DocumentoPrototype implements Cloneable {
protected DocumentoPrototype clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException ex) {
ex.printStackTrace();
}
return (DocumentoPrototype) clone;
}
}

ASCIIPrototype.java
package br.estacio.prototype;
class ASCIIPrototype extends DocumentoPrototype {

@Override
public String toString() {
return"Documento ASCII Criado!";
}
}

PDFPrototype.java
package br.estacio.prototype;
class ASCIIPrototype extends DocumentoPrototype {

@Override
public String toString() {
return"Documento ASCII Criado!";
}
}

capítulo 5 • 118
Cliente.jav
package br.estacio.prototype;
class Cliente {

public static void main(String[] args) {
DocumentoPrototype ascii = new ASCIIPrototype();
DocumentoPrototype pdf = new PDFPrototype();

System.out.println(ascii.clone());
System.out.println(pdf.clone());
}
}

Singleton

Uma implementação que forneça um número inteiro aleatório de 0 a 10, que


estará disponível enquanto o software estiver em execução. Uma única instância da
classe Singleton será criada, assim, quando houver a necessidade de se recuperar
esse valor.

Singleton.java
package br.estacio.singleton;

publicclass Singleton {
private static Singleton instance = new Singleton();
int valor;
private Singleton() {
this.valor = (int) (10 * Math.random());
}
public static synchronized Singleton getInstance() {
if (instance == null)
instance = new Singleton();
returninstance;
}
}

capítulo 5 • 119
Cliente.java
package br.estacio.singleton;
public class Cliente {
public static void main(String[] args) {
Singleton singleton = null;
singleton = Singleton.getInstance();

System.out.println("Classe Singleton instanciada,
inicializada com o valor:");
System.out.println(singleton.valor);
}
}

Adapter

Software de manipulação de imagens, onde elas podem ser carregadas nos for-
matos JPG ou PNG. Caso haja a necessidade de uma imagem PNG ser convertida
para o formato JPG, ou vice-versa, o padrão Adapter sugere uma estrutura onde
essa conversão ocorra de maneira intuitiva na aplicação.

IImage.java
package br.estacio.adapter;

public interface IImagem{


void carregarImagem(String nomeArquivo);
void desenharImagem(int posX, int posY, int largura, int altura);
}

JpgImagem.java
package br.estacio.adapter;
public class JpgImagem {
public void carregarImagemJpg(String arquivo) {
System.out.println("Imagem " + arquivo + " carregada.");
}

capítulo 5 • 120
public void desenharImagemJpg(int largura, int altura, int posX,
int posY) {
System.out.println("Imagem JPG desenhada");
System.out.println("Largura: "+largura+" Altura: "+altura);
System.out.println("X: "+posX+" Y: "+posY);
}
}

JpgImagemAdapter.java
package br.estacio.adapter;
public class JpgImagemAdapter extends JpgImagem implements IImagem
{
@Override
public void carregarImagem(String nomeArquivo) {
carregarImagemJpg(nomeArquivo);
}
@Override
public void desenharImagem(int posX, int posY, int largura, int
altura) {
desenharImagemJpg(largura, altura, posX, posY);
}
}

PngImagem.java
package br.estacio.adapter;
public class PngImagem {
public void carregarImagemPng(String arquivo) {
System.out.println("Imagem " + arquivo + " carregada.");
}
public void desenharImagemPng(int largura, int altura, int posX,
int posY) {
System.out.println("Imagem PNG desenhada");
System.out.println("Largura: "+largura+" Altura: "+altura);
System.out.println("X: "+posX+" Y: "+posY);
}
}

capítulo 5 • 121
PngImagemAdapter.java
package br.estacio.adapter;
public class PngImagemAdapter extends PngImagem implements IImagem
{
@Override
public void carregarImagem(String nomeArquivo) {
carregarImagemPng(nomeArquivo);
}
@Override
public void desenharImagem(int posX, int posY, int largura, int
altura) {
desenharImagemPng(largura, altura, posX, posY);
}

Cliente.java
package br.estacio.adapter;
public class Cliente {
public static void main(String[] args) {
IImagem imagem = new JpgImagemAdapter();
imagem.carregarImagem("imagem.png");
imagem.desenharImagem(1, -5, 10, 10);
System.out.println("==================================");
imagem = new PngImagemAdapter();
imagem.carregarImagem("imagem.png");
imagem.desenharImagem(3, 8, 10, 100);
}
}

Bridge

Será desenvolvido um programa multiplataformas, que funcione em diversos


sistemas operacionais como Windows, Linux, Mac etc. O programa poderá uti-
lizar diversas abstrações de janelas gráficas, como por exemplo, janela de diálogo,
janela de aviso, janela de erro etc. O padrão Bridge sugere um modelo de desaco-
plamento dos componentes muito eficiente. Tanto, novas abstrações como novas
plataformas, podem ser acomodadas pelo sistema sem grandes dificuldades, graças
a extensibilidade do padrão.

capítulo 5 • 122
Janela Abstrata.java
package br.estacio.bridge;
public abstract class JanelaAbstrata {
protected JanelaImplementada janela;
public JanelaAbstrata(JanelaImplementada j) {
janela = j;
}
public void desenharJanela(String titulo) {
janela.desenharJanela(titulo);
}
public void desenharBotao(String titulo) {
janela.desenharBotao(titulo);
}
public abstract void desenhar();
}

IJanela.java
package br.estacio.bridge;
public interface IJanela {
void desenharJanela(String titulo);
void desenharBotao(String titulo);
}

JanelaWindows.java
package br.estacio.bridge;
public class JanelaWindows implements IJanela {
@Override
public void desenharJanela(String titulo) {
System.out.println(titulo + ": Janela Windows");
}
@Override
public void desenharBotao(String titulo) {
System.out.println(titulo + ": Botão Windows");
}
}

capítulo 5 • 123
JanelaLinux.java
package br.estacio.bridge;
public class JanelaLinux implements IJanela {
@Override
public void desenharJanela(String titulo) {
System.out.println(titulo + ": Janela Linux");
}
@Override
public void desenharBotao(String titulo) {
System.out.println(titulo + ": Botão Linux");
}
}

JanelaAviso.java
package br.estacio.bridge;
public class JanelaAviso extends JanelaAbstrata {
public JanelaAviso(IJanela j) {
super(j);
}
@Override
public void desenhar() {
desenharJanela("Janela de Aviso");
desenharBotao("Ok");
}
}

JanelaDialogo.java
package br.estacio.bridge;
public class JanelaDialogo extends JanelaAbstrata {
public JanelaDialogo(IJanela j) {
super(j);
}
@Override
public void desenhar() {
desenharJanela("Janela de Diálogo");

capítulo 5 • 124
desenharBotao("Botão Sim");
desenharBotao("Botão Não");
desenharBotao("Botão Cancelar");
}
}

Cliente.java
package br.estacio.bridge;
public class Cliente {
public static void main(String[] args) {
JanelaAbstrata janela = new JanelaDialogo(new
JanelaLinux());
janela.desenhar();
janela = new JanelaAviso(new JanelaLinux());
janela.desenhar();
janela = new JanelaDialogo(new JanelaWindows());
janela.desenhar();
}
}

Composite

Um sistema de gerenciamento de arquivos, que seja possível criar arquivos


concretos (texto, imagem etc.) e arquivos pastas, que armazenem esses arquivos
concretos. Segundo a proposta do padrão Composite, os arquivos concretos do
exemplo são chamados de Folhas, pois não possuem filhos e os arquivos pasta são
chamados de Nós, pois possuem filhos e fornecem operações sobre esses filhos.

ArquivoComponent.java
package br.estacio.composite;
public abstract class ArquivoComponent {
String nomeArquivo;
public String getNomeArquivo() {
return this.nomeArquivo;
}

capítulo 5 • 125
public void printNomeArquivo() {
System.out.println(this.nomeArquivo);
}
}

ArquivoComposite.java
package br.estacio.composite;
import java.util.ArrayList;
public class ArquivoComposite extends ArquivoComponent {
protected ArrayList<ArquivoComponent>listaArquivos;
public ArquivoComposite(String nomeArquivo) {
this.nomeArquivo = nomeArquivo;
listaArquivos = new ArrayList<ArquivoComponent>();
}
@Override
public void printNomeArquivo() {
System.out.println(this.nomeArquivo);
for (ArquivoComponent arquivoTmp : listaArquivos) {
arquivoTmp.printNomeArquivo();
}
}
public void adicionar(ArquivoComponent novoArquivo) {
this.listaArquivos.add(novoArquivo);
}
public void remover(String nomeArquivo) throws Exception {
for (ArquivoComponent arquivoTmp : listaArquivos) {
if (arquivoTmp.getNomeArquivo() == nomeArquivo){
this.listaArquivos.remove(arquivoTmp);
return;
}
}
throw new Exception("Este arquivo não existe");
}
public ArquivoComponent getArquivo(String nomeArquivo) throws
Exception {

capítulo 5 • 126
for (ArquivoComponent arquivoTmp : listaArquivos) {
if (arquivoTmp.getNomeArquivo() == nomeArquivo){
returnarquivoTmp;
}
}
throw new Exception("Este arquivo não existe");
}
}

ArquivoVideo.java
package br.estacio.composite;
public class ArquivoVideo extends ArquivoComponent {
public ArquivoVideo(String nomeArquivo) {
this.nomeArquivo = nomeArquivo;
}
}

Cliente.java
package br.estacio.composite;
public class Cliente {
public static void main(String[] args) {
ArquivoComponent video01 = new ArquivoVideo("meu video.rmvb");
ArquivoComponent video02 = new ArquivoVideo("novo video.rmvb");
ArquivoComponent pastaVideos = new Arquivo
Composite("Meus Videos/");
((ArquivoComposite) pastaVideos).adicionar(video01);
((ArquivoComposite) pastaVideos).adicionar(video02);
pastaVideos.printNomeArquivo();
}
}

Decorator

Em um sistema de pedidos de um bar, será implementado um módulo de


montagem de coquetéis. Após a escolha da implementação do coquetel (Vodka,
por exemplo), são escolhidas as implementações dos decoradores (Suco Laranja,

capítulo 5 • 127
Suco Tomate etc.) que irão compor o coquetel. O padrão Decorator adiciona fun-
cionalidades ao objeto em tempo de execução. Ao contrário da herança, que aplica
funcionalidades a todos os objetos dela, o padrão permite aplicar funcionalidades
apenas a um objeto específico.

Coquetel.java
package br.estacio.decorator;
public abstract class Coquetel {
String nome;
double preco;
public String getNome() {
return nome;
}
public double getPreco() {
return preco;
}
}

CoquetelDecorator.java
package br.estacio.decorator;
public abstract class CoquetelDecorator extends Coquetel{
Coquetel coquetel;
public CoquetelDecorator(Coquetel oCoquetel) {
coquetel = oCoquetel;
}
@Override
public String getNome() {
return coquetel.getNome() + " + " + nome;
}
public double getPreco() {
return coquetel.getPreco() + preco;
}
}

capítulo 5 • 128
Vodka.java
package br.estacio.decorator;
public class Vodka extends Coquetel {
public Vodka() {
nome = "Vodka";
preco = 30.0;
}
}

SucoLaranja.java
package br.estacio.decorator;
public class SucoLaranja extends CoquetelDecorator {
public SucoLaranja(Coquetel oCoquetel) {
super(oCoquetel);
nome = "Suco de Laranja";
preco = 7.5;
}
}

Cliente
package br.estacio.decorator;
public class Cliente {
public static void main(String[] args) {
Coquetel coquetel = new Vodka();
System.out.println(coquetel.getNome() + ": "+
coquetel.getPreco());
coquetel = new SucoLaranja(coquetel);
System.out.println(coquetel.getNome() + ": "+
coquetel.getPreco());
}
}

capítulo 5 • 129
Façade

Um sistema de gestão de recursos humanos contém normalmente diversos


módulos ou subsistemas. Utilizando uma interface (Facade), o acesso a todas as
classes de negócio se torna facilitado, pois ele proporciona para o desenvolvedor
um acesso facilitado às implementações de negócio do sistema.

Facade.java
package br.estacio.facade;
public class Facade {
protected Funcionario funcionario;
protected Cargo cargo;
protected Setor setor;
public Facade() {
setor = new Setor();
cargo = new Cargo();
funcionario = new Funcionario();
}
public void getFuncionario() {
funcionario.getFuncionario();
}
public void getCargo() {
cargo.getCargo();
}

public void getSetor() {
setor.getSetor();
}
}

Funcionario.java
package br.estacio.facade;
public class Funcionario {
private String nome;
private int matricula;

capítulo 5 • 130
public Funcionario() {
this.nome = "Luiz Leão";
this.matricula = 123456;
}

public void getFuncionario() {
System.out.println("Nome: "+nome);
System.out.println("Matricula: "+matricula);
}
}

Cargo.java
package br.estacio.facade;
public class Cargo {
private String descricao;
private Double salario;
public Cargo() {
this.descricao = "Programador";
this.salario = 2500.0;
}

public void getCargo() {
System.out.println("Cargo: "+descricao);
System.out.println("Salario: R$ "+salario);
}
}

Setor.java
package br.estacio.facade;
public class Setor {
private String descricao;

public Setor() {
this.descricao = "CPD";
}

capítulo 5 • 131
public void getSetor() {
System.out.println("Setor: "+descricao);
}
}

Cliente.java
package br.estacio.facade;
public class Cliente {
public static void main(String[] args) {
Facade oFacade = new Facade();

System.out.println("====================");
oFacade.getFuncionario();
System.out.println("====================");
oFacade.getCargo();
System.out.println("====================");
oFacade.getSetor();
}
}

Flyweight

Na programação para jogos, são utilizados vários elementos distribuídos na


telado jogador e esses elementos consomem uma quantidade significativa de me-
mória. Usando a estrutura de compartilhamento do flyweight, o uso da memória
se torna mais eficiente. Através de uma fábrica (Factory), diversos objetos são cria-
dos para serem utilizados no programa. Em algumas situações, se houver mais de
um objeto de fábrica instanciado, pode ocorrer a criação excessiva de instâncias.
Nesse caso, um uso de uma fábrica, instanciada através do uso do padrão Singleton
pode amenizar essa situação.

ElementoFlyweight.java
package br.estacio.flyweight;
public abstract class ElementoFlyweight {
public abstract void desenharImagem(Ponto ponto);
}

capítulo 5 • 132
Elemento.java
package br.estacio.flyweight;
public class Elemento extends ElementoFlyweight {
protected Imagem imagem;
public Elemento(String nomeImagem) {
imagem = new Imagem(nomeImagem);
}
@Override
public void desenharImagem(Ponto ponto) {
imagem.desenharImagem();
System.out.println(" => Ponto (" + ponto.x + "," +ponto.y +
")");
}
}

FlyweightFactory.java
package br.estacio.flyweight;
import java.util.ArrayList;
public class FlyweightFactory {
protected ArrayList<ElementoFlyweight>flyweights;
public FlyweightFactory() {
flyweights = new ArrayList<ElementoFlyweight>();
flyweights.add(new Elemento("jogador.png"));
flyweights.add(new Elemento("inimigo01.png"));
flyweights.add(new Elemento("inimigo02.png"));
flyweights.add(new Elemento("inimigo03.png"));
flyweights.add(new Elemento("obstaculo01.png"));
flyweights.add(new Elemento("obstaculo02.png"));
}
public ElementoFlyweight getFlyweight(EnumElementos
componente) {
switch (componente) {
case JOGADOR: return flyweights.get(0);
case INIMIGO01: return flyweights.get(1);
case INIMIGO02: return flyweights.get(2);
case INIMIGO03: return flyweights.get(3);

capítulo 5 • 133
case OBSTACULO01: return flyweights.get(4);
default: return flyweights.get(5);
}
}
}

Imagem.java
package br.estacio.flyweight;
public class Imagem {
protected String nomeImagem;
public Imagem(String imagem) {
nomeImagem = imagem;
}
public void desenharImagem() {
System.out.print(nomeImagem + desenharPontinhos() + "
Criado");
}

public String desenharPontinhos() {
String pontos = "";
int tamanho = 22 - nomeImagem.length();
for(int i=0; i<tamanho; i++) {
pontos += ".";
}
return pontos;
}
}

Ponto.java
package br.estacio.flyweight;
public class Ponto {
public int x, y;
public Ponto(int x, int y) {
this.x = x;
this.y = y;
}
}

capítulo 5 • 134
Cliente.java
package br.estacio.flyweight;
public class Cliente {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
System.out.println("==============================");
System.out.println("Renderizando o Cenário do Jogo");
System.out.println("==============================");
factory
.getFlyweight(EnumElementos.OBSTACULO01)
.desenharImagem(new Ponto(0, 0));
factory
.getFlyweight(EnumElementos.JOGADOR)
.desenharImagem(new Ponto(10, 10));
factory
.getFlyweight(EnumElementos.INIMIGO01)
.desenharImagem(new Ponto(100, 10));
factory
.getFlyweight(EnumElementos.INIMIGO01)
.desenharImagem(new Ponto(120, 10));
factory
.getFlyweight(EnumElementos.INIMIGO01)
.desenharImagem(new Ponto(140, 10));
factory
.getFlyweight(EnumElementos.OBSTACULO02)
.desenharImagem(new Ponto(5, 33));
factory
.getFlyweight(EnumElementos.INIMIGO02)
.desenharImagem(new Ponto(60, 10));
factory
.getFlyweight(EnumElementos.INIMIGO02)
.desenharImagem(new Ponto(50, 10));
factory
.getFlyweight(EnumElementos.INIMIGO03)
.desenharImagem(new Ponto(170, 10));
}
}

capítulo 5 • 135
Proxy

Em um sistema bancário, determinadas informações só podem ser acessadas


por pessoas com permissões para tal. Utilizando os conceitos do padrão Proxy,o
sistema terá uma classe que contém as informações bancárias (Banco) e uma classe
extendida que adiciona a camada de segurança necessária para controlar o aces-
so (Banco Proxy).A principal vantagem de utilizar o Proxy é que, ao utilizar um
substituto, pode ser implementado desde operações otimizadas até proteção do
acesso ao objeto. Porém, como o papel de um proxy possui variâncias, é necessário
conhecer bem seu comportamento para decidir quando utilizá-lo ou não.

Banco.java
package br.estacio.proxy;
public class Banco {
private int totalClientes;
private double saldoClientes;
public Banco() {
totalClientes = (int) (Math.random() * 100);
saldoClientes = (double) (Math.random() * 100000);
}
public String getTotalClientes() {
return new String("Total de clientes: " + totalClientes);
}
public String getSaldoClientes() {
return new String("Saldo Total dos Clientes: R$ " +
saldoClientes);
}
}

BancoProxy.java
package br.estacio.proxy;
public class BancoProxy extends Banco {
protected String usuario, senha;
public BancoProxy(String usuario, String senha) {
this.usuario = usuario;
this.senha = senha;
}

capítulo 5 • 136
@Override
public String getTotalClientes() {
if (checarPermissao()) {
return super.getTotalClientes();
}
return null;
}
@Override
public String getSaldoClientes() {
if (checarPermissao()) {
return super.getSaldoClientes();
}
return null;
}
private boolean checarPermissao() {
return (usuario == "gerente"&&senha == "gerente") ? true :
false;
}
}

Cliente.java
package br.estacio.proxy;
public class Cliente {
public static void main(String[] args) {
System.out.println("Acesso do Hacker");
Banco banco = new BancoProxy("Hacker", "1234");
System.out.println(banco.getTotalClientes());
System.out.println(banco.getSaldoClientes());
System.out.println("================================");
System.out.println("Acesso do Gerente");
banco = new BancoProxy("gerente", "gerente");
System.out.println(banco.getTotalClientes());
System.out.println(banco.getSaldoClientes());
}
}

capítulo 5 • 137
Chain of responsibility

Um site de comércio eletrônico disponibiliza diversas formas de pagamento


para os seus clientes. Ao modelar uma forma de execução do pagamento, é neces-
sário selecionar uma entre diversas formas de pagamento disponíveis. A primeira
ideia que surge é utilizar uma estrutura de decisão para verificar, dado um parâ-
metro, qual aforma de pagamento a ser utilizada na transação, porém será imple-
mentada a estrutura proposta pelo Chain of Responsibility. Esse padrão fornece
uma maneira de tomar decisões com um fraco acoplamento. Será observado que a
estrutura de cadeia não possui qualquer informação sobre as classes que compõem
a cadeia, da mesma forma, uma classe da cadeia não tem nenhuma noção sobre o
formato da estrutura ou sobre elementos nela inseridos.

Pagamento Chain.java
package br.estacio.chain;
public abstract class PagamentoChain {
protected PagamentoChain proximo;
protected EnumPagamentos idBanco;
public PagamentoChain(EnumPagamentos banco) {
proximo = null;
idBanco = banco;
}
public void setProximo(PagamentoChain forma) {
if (proximo == null) {
proximo = forma;
} else {
proximo.setProximo(forma);
}
}
public void efetuarPagamento(EnumPagamentos banco) throws
Exception {
if (verificarPagamento(banco)) {
efetuarPagamento();
} else {
if (proximo == null) {

capítulo 5 • 138
throw new Exception(banco + ": .......Forma de Pagamento
Não Cadastrada");
}
proximo.efetuarPagamento(banco);
}
}
private boolean verificarPagamento(EnumPagamentos banco) {
if (idBanco == banco) {
return true;
}
return false;
}

protected abstract void efetuarPagamento();
}

EnumPagamentos.java
package br.estacio.chain;
public enum EnumPagamentos {
CARTAOA, CARTAOB, BOLETO, CARTAOC
}

Boleto.java
package br.estacio.chain;
public class Boleto extends PagamentoChain {
public Boleto() {
super(EnumPagamentos.BOLETO);
}
@Override
protected void efetuarPagamento() {
System.out.println("Pagamento efetuado: Boleto");
}
}

capítulo 5 • 139
CartaoA.java
package br.estacio.chain;
public class CartaoA extends PagamentoChain {
public CartaoA() {
super(EnumPagamentos.CARTAOA);
}
@Override
protected void efetuarPagamento() {
System.out.println("Pagamento efetuado: Cartão A");
}
}

CartaoB.java
package br.estacio.chain;
public class CartaoB extends PagamentoChain {
public CartaoB() {
super(EnumPagamentos.CARTAOB);
}
@Override
protected void efetuarPagamento() {
System.out.println("Pagamento efetuado: Cartão B");
}
}

Cliente.java
package br.estacio.chain;
public class Cliente {
public static void main(String[] args) {
PagamentoChain bancos = new CartaoA();
bancos.setProximo(new CartaoB());
bancos.setProximo(new Boleto());

try {
bancos.efetuarPagamento(EnumPagamentos.BOLETO);
bancos.efetuarPagameto(EnumPagamentos.CARTAOA);
bancos.efetuarPagameto(EnumPagamentos.CARTAOB);

capítulo 5 • 140
bancos.efetuarPagameto(EnumPagamentos.CARTAOC);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}

Command

Uma loja que vende produtos e oferece várias formas de pagamento. Ao exe-
cutar uma compra, o sistema registra o seu valor e, de acordo com a forma de
pagamento selecionada, por exemplo, cartão de crédito, emite o valor total da
compra para o cartão de crédito do cliente, exibindo a quantidade de parcelas.
Outras formas de pagamento, como boleto bancário, teriam outras informações
relacionadas a compra, e assim por diante. Para este caso, vamos supor as seguintes
classes para simplificar o exemplo: Loja e Compra.
O padrão Command utiliza apenas da herança para agrupar classes e obrigar
que todas tenham uma mesma interface em comum. Inicialmente o padrão pode
ser confundido com o padrão Template Method, pois ambos utilizam a herança
para unificar a interface de várias classes.
A diferença reside no fato do padrão Command não utilizar o conceito de
um algoritmo que será executado. Já no padrão Template Method, as subclasses
concretas definem apenas algumas operações, sem alterar o esqueleto de execução.
O Command não oferece uma maneira de execução de suas subclasses, apenas
garante que todas executem determinada requisição.

PagamentoCommand.java
package br.estacio.command;
public interface PagamentoCommand {
void processarCompra(Compra compra);
}

Compra.java
package br.estacio.command;
public class Compra {
private static int CONT;

capítulo 5 • 141
protected int numNotaFiscal;
protected String nomeLoja;
protected double valorTotal;
public Compra(String nomeLoja) {
this.nomeLoja = nomeLoja;
numNotaFiscal = ++CONT;
}
public void setValor(double valor) {
this.valorTotal = valor;
}
public String getInfoNota() {
StringBuffer linha = new StringBuffer();
linha.append("| Nota fiscal nº: " + numNotaFiscal+"\n");
linha.append("| Loja: "+ nomeLoja+"\n");
linha.append("| Valor: " + valorTotal+"\n");
return linha.toString();
}
}

Loja.java

package br.estacio.command;
public class Loja {
protected String nome;
public Loja(String nome) {
this.nome = nome;
}
public void executarCompra(double valor, PagamentoCommand
formaPagamento) {
Compra compra = new Compra(nome);
compra.setValor(valor);
formaPagamento.processarCompra(compra);
}
}

capítulo 5 • 142
PagamentoBoleto.java
package br.estacio.command;
public class PagamentoBoleto implements PagamentoCommand {
@Override
public void processarCompra(Compra compra) {
System.out.println(compra.getInfoNota() + "| Forma de
Pagamento: ........ Boleto");
}
}

PagamentoCartãoCredito.java
package br.estacio.command;
public class PagamentoCartaoCredito implements PagamentoCommand {
int qtdParcelas;

PagamentoCartaoCredito(int qtdParcelas) {
this.qtdParcelas = qtdParcelas;
}

@Override
public void processarCompra(Compra compra) {
System.out.println(compra.getInfoNota() + "| Forma de
Pagamento: ........ Cartao de Credito");
System.out.println("| Quantidade de Parcelas: ....
"+qtdParcelas);
}
}

PagamentoCartãoDebito.java
package br.estacio.command;
public class PagamentoCartaoDebito implements PagamentoCommand {
@Override
public void processarCompra(Compra compra) {
System.out.println(compra.getInfoNota() + "| Forma de
Pagamento: ........ Cartao de Debito");
}
}

capítulo 5 • 143
Cliente.java
package br.estacio.command;
public class Cliente {
public static void main(String[] args) {
Loja baratao = new Loja("Baratão");
baratao.executarCompra(999.00, new PagamentoCartao
Credito(2));
System.out.println("==============================");
baratao.executarCompra(49.00, new PagamentoBoleto());
System.out.println("==============================");
baratao.executarCompra(99.00, new PagamentoCartao
Debito());
System.out.println("==============================");

Loja joaoEletro = new Loja("João Eletro");
joaoEletro.executarCompra(19.00, new PagamentoCartao
Credito(3));
}
}

Interpreter

Um software para a interpretação de números, que estão representados por


algarismos romanos, para algarismos arábicos (a representação numérica ampla-
mente conhecida por todos). Percorrer a String e procurar cada um dos possíveis
casos não é a melhor solução, pois dificultaria bastante a manutenção do código.
O padrão Interpreter sugere a criação de uma gramática, para a identificação de
padrões que direcione a conversão dos formatos numéricos. Para simplificar, serão
tratados apenas números de quatro dígitos. Iniciando a definição da gramática,
um número romano é composto por caracteres que representam números de qua-
tro, três, dois ou um dígito.

NumRomanoInterpreter.java
package br.estacio.interpreter;
public abstract class NumRomanoInterpreter {
public void interpretar(Contexto contexto) {

capítulo 5 • 144
if (contexto.getInput().length() == 0) {
return;
}
// 4 e 9 tem duascasas
if (contexto.getInput().startsWith(nove())) {
addValorOutput(contexto, 9);
useDuasCasasInput(contexto);
} else if(contexto.getInput().startsWith(quatro())) {
addValorOutput(contexto, 4);
useDuasCasasInput(contexto);
} else if (contexto.getInput().startsWith(cinco())) {
addValorOutput(contexto, 5);
useUmaCasaInput(contexto);
}
// valoresquese repetem: III, CCC, MMM
while (contexto.getInput().startsWith(um())) {
addValorOutput(contexto, 1);
useUmaCasaInput(contexto);
}
}
private void useUmaCasaInput(Contexto contexto) {
contexto.setInput(contexto.getInput().substring(1));
}
private void useDuasCasasInput(Contexto contexto) {
contexto.setInput(contexto.getInput().substring(2));
}
private void addValorOutput(Contexto contexto, int numero){
contexto.setOutput(contexto.getOutput() + (numero *
multiplicador()));
}
public abstract String um();
public abstract String quatro();
public abstract String cinco();
public abstract String nove();
public abstract int multiplicador();
}

capítulo 5 • 145
Contexto.java
package br.estacio.interpreter;
public class Contexto {
protected String input;
protected int output;
public Contexto(String input) {
this.input = input;
}
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
public int getOutput() {
return output;
}
public void setOutput(int output) {
this.output = output;
}
}

UnidadeRomano.java
package br.estacio.interpreter;
public class UnidadeRomano extends NumRomanoInterpreter {
@Override
public String um() {
return"I";
}
@Override
public String quatro() {
return"IV";
}
@Override
public String cinco() {
return"V";
}

capítulo 5 • 146
@Override
public String nove() {
return"IX";
}
@Override
public int multiplicador() {
return 1;
}
}

DezenaRomano.java
package br.estacio.interpreter;
public class DezenaRomano extends NumRomanoInterpreter {
@Override
public String um() {
return "X";
}
@Override
public String quatro() {
return "XL";
}
@Override
public String cinco() {
return "L";
}
@Override
public String nove() {
return "XC";
}
@Override
public int multiplicador() {
return 10;
}
}

capítulo 5 • 147
CentenaRomano.java
package br.estacio.interpreter;
public class CentenaRomano extends NumRomanoInterpreter {
@Override
public String um() {
return "C";
}
@Override
public String quatro() {
return "CD";
}
@Override
public String cinco() {
return "D";
}
@Override
public String nove() {
return "CM";
}
@Override
public int multiplicador() {
return 100;
}
}

MilharRomano.java
package br.estacio.interpreter;
public class MilharRomano extends NumRomanoInterpreter {
@Override
public String um() {
return "M";
}
@Override
public String quatro() {
return " ";
}

capítulo 5 • 148
@Override
public String cinco() {
return " ";
}
@Override
public String nove() {
return " ";
}
@Override
public int multiplicador() {
return 1000;
}
}

Cliente.java
package br.estacio.interpreter;
import java.util.ArrayList;
public class Cliente {
public static void main(String[] args) {
ArrayList<NumRomanoInterpreter>aInterpreter = new Array
List<NumRomanoInterpreter>();
aInterpreter.add(new MilharRomano());
aInterpreter.add(new CentenaRomano());
aInterpreter.add(new DezenaRomano());
aInterpreter.add(new UnidadeRomano());
String valorRomano = "MMMDLVIII";
Contexto contexto = new Contexto(valorRomano);
for (NumRomanoInterpreter numRomanoInterpreter : aInterpreter)
{
numRomanoInterpreter.interpretar(contexto);
}
System.out.println(valorRomano + " = " + Integer.
toString(contexto.getOutput()));
}
}

capítulo 5 • 149
Iterator

Um sistema de controle da programação de uma empresa de TV a cabo, onde


cada programador implementa os canais de determinada categoria usando um tipo
de coleção diferente (ArrayList, List, Vector etc.). O padrão Iterator é uma solução
onde pode ser padronizada a ação de percurso dessas listas, facilitando a visuali-
zação dessas informações, mesmo quando implementadas em formatos distintos.

IteradorCanais.java
package br.estacio.iterator;
import java.util.ArrayList;
public class IteradorCanais {
ArrayList<Canal>lista;
int contador;
protected IteradorCanais(ArrayList<Canal>lista) {
this.lista = lista;
contador = 0;
}
public void first() {
contador = 0;
}
public void proximoCanal() {
contador++;
}
public void voltarCanal() {
34stacio34 --;
}
public 34stacio isDone() {
return 34stacio34 == lista.size();
}
private Canal currentItem() {
if (isDone()) {
contador = lista.size() – 1;
} else if (contador< 0) {
contador = 0;
}

capítulo 5 • 150
return lista.get(34stacio34);
}
public String getNomeCanal() {
return currentItem().nome;
}

public int getNumeroCanal() {
return currentItem().numero;
}
}

CanaisAgregado.java
package br.estacio.iterator;

import java.util.ArrayList;

public abstract class CanaisAgregado {


protected ArrayList<Canal>canais;
public CanaisAgregado() {
canais = new ArrayList<Canal>();
}
public int count() {
return canais.size();
}
public IteradorCanais criarIterator() {
return new IteradorCanais(canais);
}
}

CanaisNoticia.java
package br.estacio.iterator;
public class CanaisNoticia extends CanaisAgregado {
public CanaisNoticia() {
canais.add(new Canal("Blogo News", 11));
canais.add(new Canal("Bland Noticias", 3));
canais.add(new Canal("Gazeta Belenense", 21));

capítulo 5 • 151
canais.add(new Canal("Valarejo Alerta", 41));
}
}

Canal.java
package br.estacio.iterator;
public class Canal {
String nome;
int numero;
public Canal(String nome, int numero) {
this.nome = nome;
this.numero = numero;
}
}

Cliente.java
package br.estacio.iterator;
public class Cliente {
public static void main(String[] args) {
CanaisNoticia canais = new CanaisNoticia();
System.out.println("==========================");
System.out.println("Iterando com for:”);
System.out.println("==========================");
for (IteradorCanais it = canais.criarIterator(); !it.
isDone(); it.
proximoCanal()) {
System.out.println(it.getNumeroCanal() + "–" + it.get
NomeCanal());
}
System.out.println("==========================");
System.out.println("Iterando manualmente: ");
System.out.println("==========================");
IteradorCanais it = canais.criarIterator();

System.out.println(it.getNumeroCanal() + "–" + it.get
NomeCanal());

capítulo 5 • 152
it.proximoCanal();
System.out.println(it.getNumeroCanal() + "–" + it.get
NomeCanal());
it.proximoCanal();
System.out.println(it.getNumeroCanal() + "–" + it.get
NomeCanal());
it.proximoCanal();
System.out.println(it.getNumeroCanal() + "–" + it.get
NomeCanal());
System.out.println("\nIterando além dos limites:");
it.proximoCanal();
System.out.println(it.getNumeroCanal() + "–" + it.get
NomeCanal());
it.first();
it.voltarCanal();
System.out.println(it.getNumeroCanal() + "–" + it.get
NomeCanal());
}
}

Mediator

Desenvolver um aplicativo de troca de mensagem, entre dispositivos móveis


de diversas plataformas (IOS, Android, Windows Phone etc.). Porém, como cada
plataforma tem uma implementação própria do seu serviço de troca de mensa-
gem, será utilizado o padrão Mediator para estabelecer um barramento de comu-
nicação entre esses dispositivos distintos. O padrão Mediator tem como objetivo
diminuir a complexidade de relacionamentos entre objetos, garantindo assim uma
maior independência entre eles, facilitando a manutenibilidade e a introdução de
novos tipos de objetos ao relacionamento.

Mediator.java
package br.estacio.mediator;
public interface Mediator {
void enviar(String mensagem, Colleague colleague);
}

capítulo 5 • 153
MensagemMediator.java
package br.estacio.mediator;
import java.util.ArrayList;
public class MensagemMediator implements Mediator {
protected ArrayList<Colleague>contatos;
public MensagemMediator() {
contatos = new ArrayList<Colleague>();
}
public void adicionarColleague(Colleague colleague) {
contatos.add(colleague);
}
@Override
public void enviar(String mensagem, Colleague colleague) {
for (Colleague contato : contatos) {
if (contato != colleague) {
definirProtocolo(contato);
contato.receberMensagem(mensagem);
}
}
}
private void definirProtocolo(Colleague contato) {
if (contato instanceof IOSColleague) {
System.out.println("Dispositivo:..... iOS");
} else if (contato instanceof AndroidColleague) {
System.out.println("Dispositivo:..... Android");
} else if (contato instanceof WindowsPhoneColleague) {
System.out.println("Dispositivo:..... Windows Phone");
}
}
}

Colleague.java
package br.estacio.mediator;
public abstract class Colleague {
protected Mediator mediator;
public Colleague(Mediator m) {

capítulo 5 • 154
mediator = m;
}
public void enviarMensagem(String mensagem) {
mediator.enviar(mensagem, this);
}
publicabstractvoid receberMensagem(String mensagem);
}

AndroidColleague.java
package br.estacio.mediator;
public class AndroidColleague extends Colleague {
public AndroidColleague(Mediator m) {
super(m);
}
@Override
public void receberMensagem(String mensagem) {
System.out.println("Android recebeu: " + mensagem);
}
}

IOSColleague.java
package br.estacio.mediator;
public class IOSColleague extends Colleague {
public IOSColleague(Mediator m) {
super(m);
}
@Override
public void receberMensagem(String mensagem) {
System.out.println("iOs recebeu: " + mensagem);
}
}

WindowsPhoneColleague.java
package br.estacio.mediator;
public class WindowsPhoneColleague extends Colleague {
public WindowsPhoneColleague(Mediator m) {

capítulo 5 • 155
super(m);
}
@Override
public void receberMensagem(String mensagem) {
System.out.println("Windows Phone recebeu: " + mensagem);
}
}

Cliente.java
package br.estacio.mediator;
public class Cliente {
public static void main(String[] args) {
MensagemMediator mediador = new MensagemMediator();

AndroidColleague android = new AndroidColleague(mediador);
IOSColleague ios = new IOSColleague(mediador);
WindowsPhoneColleague 39stacio = new Windows
PhoneColleague(mediador);

mediador.addColleague(android);
mediador.addColleague(ios);
mediador.addColleague(symbian);

symbian.enviarMensagem("Oi, eu sou um Symbian!");
System.out.println("================================");
android.enviarMensagem("Oi Windows Phone! Eu sou um Android!");
System.out.println("================================");
ios.enviarMensagem("Olá todos, sou um iOs!");
}
}

Memento

Desenvolver um editor de texto, com recurso de recuperação de estados an-


teriores ao atual (undo). Para isso será utilizado o padrão Memento, onde serão
armazenados os diversos estados da produção de um texto. O padrão Memento
oferece uma forma simplificada de salvar estados internos de um objeto. Basta

capítulo 5 • 156
salvar todas as informações necessárias em Memento e, quando necessário, recu-
perá-las. Ele transfere a responsabilidade de fornecer maneiras de acessar o estado
para o objeto Memento, deixando o Originator (nesse exemplo, a classe Texto)
livre desta responsabilidade.

TextoMemento.java
package br.estacio.memento;
public class TextoMemento {
protected String estadoTexto;
public TextoMemento(String texto) {
estadoTexto = texto;
}
public String getTextoSalvo() {
returnestadoTexto;
}
}

TextoCareTaker.java
package br.estacio.memento;
import java.util.ArrayList;
public class TextoCareTaker {
protected ArrayList<TextoMemento>estados;
public TextoCareTaker() {
estados = new ArrayList<TextoMemento>();
}
public void addMemento(TextoMemento memento) {
estados.add(memento);
}
public TextoMemento getUltimoEstadoSalvo() {
if (estados.size() <= 0) {
returnnew TextoMemento("");
}
TextoMemento estadoSalvo = estados.get(estados.size() – 1);
estados.remove(estados.size() – 1);
return estadoSalvo;
}
}

capítulo 5 • 157
Texto.java
package br.estacio.memento;
public class Texto {
protected String texto;
TextoCareTaker caretaker;
public Texto() {
caretaker = new TextoCareTaker();
texto = new String();
}
public void addTexto(String novoTexto) {
caretaker.addMemento(new TextoMemento(texto));
texto += novoTexto+"\n";
}
public void undoTexto() {
texto = caretaker.getUltimoEstadoSalvo().getTextoSalvo();
}
public void showTexto() {
System.out.println(texto);
}
}

Cliente.java
package br.estacio.memento;
public class Cliente {
public static void main(String[] args) {
Texto texto = new Texto();
texto.addTexto("Texto da linha 1");
texto.addTexto("Texto da linha 2");
texto.addTexto("Texto da linha 1");
texto.showTexto();
texto.undoTexto();
texto.showTexto();
texto.undoTexto();
texto.showTexto();
texto.undoTexto();
texto.showTexto();

capítulo 5 • 158
texto.undoTexto();
texto.showTexto();
}
}

Observer

Em um sistema de informações gerenciais, há diversas maneiras de se visuali-


zar os seus dados (gráfico, planilha etc.). A arquitetura desses dados deve ser cons-
truída de tal modo que qualquer alteração no conjunto de dados compartilhados
provoque alterações encadeadas em todas as formas de representação, garantindo
assim, que uma visão nunca tenha dados invalidados. Logo, o padrão Observer
apresenta uma solução que garante a integridade dos dados em todas as suas for-
mas de visualização. O padrão Observer oferece uma forma de compartilhar um
recurso, como um broadcast, onde todos os observers cadastrados em um subject
serão notificados, caso haja alguma mudança.

DadosObserver.java
package br.estacio.observer;
public abstract class DadosObserver {
protected DadosSubject dados;
public DadosObserver(DadosSubject dados) {
this.dados = dados;
}
public abstract void update();
}

Dados.java
package br.estacio.observer;
public class Dados {
int valorA, valorB, valorC;
public Dados(int a, int b, int c) {
valorA = a;
valorB = b;
valorC = c;
}
}

capítulo 5 • 159
DadosSubject.java
package br.estacio.observer;
import java.util.ArrayList;
public class DadosSubject {
protected ArrayList<DadosObserver>observers;
protected Dados dados;
public DadosSubject() {
observers = new ArrayList<DadosObserver>();
}
public void attach(DadosObserver observer) {
observers.add(observer);
}
public void detach(intindice) {
observers.remove(indice);
}
public void setState(Dados dados) {
this.dados = dados;
notifyObservers();
}
private void notifyObservers() {
for (DadosObserver observer : observers) {
observer.update();
}
}
public Dados getState() {
return dados;
}
}

Barra Observer.java
package br.estacio.observer;
public class BarraObserver extends DadosObserver {
public BarraObserver(DadosSubject dados) {
super(dados);
}

capítulo 5 • 160
@Override
public void update() {
String barraA = "", barraB = "", barraC = "";
for (inti = 0; i<dados.getState().valorA; i++)
barraA += '=';
for (inti = 0; i<dados.getState().valorB; i++)
barraB += '=';
for (inti = 0; i<dados.getState().valorC; i++)
barraC += '=';
System.out.println("Barras:");
System.out.println("Valor A: " + barraA);
System.out.println("Valor B: " + barraB);
System.out.println("Valor A: " + barraC);
}
}

PercentualObserver.java
package br.estacio.observer;
import java.text.DecimalFormat;
public class PercentualObserver extends DadosObserver {
public PercentualObserver(DadosSubject dados) {
super(dados);
}
@Override
public void update() {
int totalValores = dados.getState().valorA + dados.getState().
valorB + dados.getState().valorC;
DecimalFormat format = new DecimalFormat("#.##");
String porcentagemA = format.format((double) dados.getState().
valorA / totalValores);
String porcentagemB = format.format((double) dados.getState().
valorB / totalValores);
String porcentagemC = format.format((double) dados.getState().
valorC / totalValores);
System.out.println("Porcentagem:");
System.out.println("Valor A: " + porcentagemA + "%");

capítulo 5 • 161
System.out.println("Valor B: " + porcentagemB + "%");
System.out.println("Valor C: " + porcentagemC + "%");
}
}

TabelaObserver.java
package br.estacio.observer;
public class TabelaObserver extends DadosObserver {
public TabelaObserver(DadosSubject dados) {
super(dados);
}
@Override
public void update() {
System.out.println("Tabela:");
System.out.println("Valor A: "+ dados.getState().valorA);
System.out.println("Valor B: "+ dados.getState().valorB);
System.out.println("Valor C: "+ dados.getState().valorC);
}
}

Cliente.java
package br.estacio.observer;
public class Cliente {
public static void main(String[] args) {
DadosSubject dados = new DadosSubject();
dados.attach(new TabelaObserver(dados));
dados.attach(new BarraObserver(dados));
dados.attach(new PercentualObserver(dados));
dados.setState(new Dados(7, 3, 1));
dados.setState(new Dados(2, 3, 1));
}
}

capítulo 5 • 162
State

No jogo Mario Bros, a troca de estados do personagem do jogo é bastante


comum. Por exemplo, ao pegar uma flor de fogo o Mario pode crescer, se estiver
pequeno, e ficar com a habilidade de soltar bolas de fogo entre outras situações.
O State é um padrão que possibilita a gestão dos estados de formas bastante
escalável. Caso surja a necessidade de se criar um novo estado, basta se criar uma
classe que implemente a interface State (nesse exemplo, seria a classe MarioState).

MarioState.java
package br.estacio.state;
public interface MarioState {
MarioState pegarCogumelo();
MarioState pegarFlor();
MarioState pegarPena();
MarioState sofrerDano();
}

Mario.java
package br.estacio.state;
public class Mario {
protected MarioState estado;

public Mario() {
estado = new MarioPequeno();
}
public void pegarCogumelo() {
estado = estado.pegarCogumelo();
}
public void pegarFlor() {
estado = estado.pegarFlor();
}
public void pegarPena() {
estado = estado.pegarPena();
}

capítulo 5 • 163
public void levarDano() {
estado = estado.sofrerDano();
}
}

Mario Pequeno.java
package br.estacio.state;
public class MarioPequeno implements MarioState {
@Override
public MarioState pegarCogumelo() {
System.out.println("Mario grande");
return new MarioGrande();
}
@Override
public MarioState pegarFlor() {
System.out.println("Mario grande com fogo");
return new MarioFogo();
}
@Override
public MarioState pegarPena() {
System.out.println("Mario grande com capa");
return new MarioCapa();
}
@Override
public MarioState sofrerDano() {
System.out.println("Mario morto");
return new MarioMorto();
}
}

MarioGrande.java
package br.estacio.state;
public class MarioGrande implements MarioState {
@Override
public MarioState pegarCogumelo() {

capítulo 5 • 164
System.out.println("Mario ganhou 1000 pontos");
return this;
}
@Override
public MarioState pegarFlor() {
System.out.println("Mario com fogo");
return new MarioFogo();
}
@Override
public MarioState pegarPena() {
System.out.println("Mario com capa");
return new MarioCapa();
}
@Override
public MarioState sofrerDano() {
System.out.println("Mario pequeno");
return new MarioPequeno();
}
}

MarioFogo.java
package br.estacio.state;
public class MarioFogo implements MarioState {
@Override
public MarioState pegarCogumelo() {
System.out.println("Mario ganhou 1000 pontos");
return this;
}
@Override
public MarioState pegarFlor() {
System.out.println("Mario ganhou 1000 pontos");
return this;
}

capítulo 5 • 165
@Override
public MarioState pegarPena() {
System.out.println("Mario com capa");
return new MarioCapa();
}
@Override
public MarioState sofrerDano() {
System.out.println("Mario grande");
return new MarioGrande();
}
}

MarioPena.java
package br.estacio.state;
public class MarioGrande implements MarioState {
@Override
public MarioState pegarCogumelo() {
System.out.println("Mario ganhou 1000 pontos");
return this;
}
@Override
public MarioState pegarFlor() {
System.out.println("Mario com fogo");
return new MarioFogo();
}
@Override
public MarioState pegarPena() {
System.out.println("Mario com capa");
return new MarioCapa();
}
@Override
public MarioState sofrerDano() {
System.out.println("Mario pequeno");
return new MarioPequeno();
}
}

capítulo 5 • 166
MarioMorto.java
package br.estacio.state;
public class MarioMorto implements MarioState {
@Override
public MarioState pegarCogumelo() {
return null;
}
@Override
public MarioState pegarFlor() {
return null;
}
@Override
public MarioState pegarPena() {
return null;
}
@Override
public MarioState sofrerDano() {
return null;
}
}

Cliente.java
package br.estacio.state;
public class Cliente {
public static void main(String[] args) {
Mario mario = new Mario();
mario.pegarCogumelo();
mario.pegarPena();
mario.levarDano();
mario.pegarFlor();
mario.pegarFlor();
mario.levarDano();
mario.levarDano();
mario.pegarPena();
mario.levarDano();
mario.levarDano();

capítulo 5 • 167
mario.levarDano();
}
}

Strategy

Um sistema de cálculo do desconto de impostos a serem pagos, de acordo com


cargo assumido pelos funcionários. De acordo com o cargo, o algoritmo que cal-
cula os impostos a serem pagos será diferente (Desconto de 10%-15% do salário,
desconto de 15%-20% etc.). Para implementarmos essa solução, usando o padrão
Strategy, serão criadas classes que implementarão uma mesma interface, porém,
cada classe conterá o algoritmo que resolva a especificidade de desconto.
O padrão Strategy, além de encapsular os algoritmos da mesma família, tam-
bém permite a reutilização do código. No exemplo, a regra para cálculo do im-
posto do Desenvolvedor e do DBA são as mesmas, ou seja, não será necessário
escrever código extra.

StrategyCalculoImposto.java
package br.estacio.strategy;
interface StrategyCalculoImposto {
double calcularSalarioImposto(Funcionario oFuncionario);
}

CalculoImposto10_15.java
package br.estacio.strategy;
public class CalculoImposto10_15 implements StrategyCalculoImposto
{
@Override
public double calcularSalarioImposto(Funcionario oFuncionario)
{
if (oFuncionario.getSalarioBase() > 2000) {
return oFuncionario.getSalarioBase() * 0.85;
}
return oFuncionario.getSalarioBase() * 0.9;
}
}

capítulo 5 • 168
CalculoImposto15_20.java
package br.estacio.strategy;
public class CalculoImposto15_20 implements StrategyCalculoImposto
{
@Override
public double calcularSalarioImposto(Funcionario oFuncionario)
{
if (oFuncionario.getSalarioBase() > 3500) {
return oFuncionario.getSalarioBase() * 0.8;
}
return oFuncionario.getSalarioBase() * 0.85;
}
}

Funcionario.java
package br.estacio.strategy;
public class Funcionario {
public static final int DESENVOLVEDOR = 1;
public static final int GERENTE = 2;
public static final int DBA = 3;
protected double salarioBase;
protected int cargo;
protected String descricaoCargo;
protected StrategyCalculoImposto strategyCalculo;
public Funcionario(int cargo, double salarioBase) {
this.salarioBase = salarioBase;
switch (cargo) {
case DESENVOLVEDOR:
strategyCalculo = new CalculoImposto10_15();
descricaoCargo = "DESENVOLVEDOR";
break;
case DBA:
strategyCalculo = new CalculoImposto10_15();
descricaoCargo = "DBA";
break;

capítulo 5 • 169
case GERENTE:
strategyCalculo = new CalculoImposto15_20();
descricaoCargo = "GERENTE";
break;
default:
throw new RuntimeException("Cargo não encontrado");
}
}
public double calcularSalarioImposto() {
return strategyCalculo.calcularSalarioImposto(this);
}
public double getSalarioBase() {
return salarioBase;
}

public String getDescricaoCargo() {
return descricaoCargo;
}
}

Cliente.java
package br.estacio.strategy;
public class Cliente {
public static void main(String[] args) {
try{

Funcionario oFuncionario1 = new Funcionario(Funcionario.DESENVOL
VEDOR, 2100);
System.out.println("Cargo: " + oFuncionario1.getDescricaoCargo());
System.out.println("Salario Bruto: " + oFuncionario1.
getSalarioBase());
System.out.println("Salario Liquido: " + oFuncionario1.calcularSalario
Imposto());

System.out.println("=====================================");

capítulo 5 • 170
Funcionario oFuncionario2 = new Funcionario(Funcionario.DBA, 1700);
System.out.println("Cargo: " + oFuncionario2.getDescricaoCargo());
System.out.println("Salario Bruto: " +
oFuncionario2.getSalarioBase());
System.out.println("Salario Liquido: " + oFuncionario2.calcularSalario
Imposto());
S y s t e m . o u t . p r i n t l n ( " = = = = = = = = = = = = = = = = = = = = = =
====================");
Funcionario oFuncionario3 = new Funcionario(Funcionario.GERENTE,
10000);
System.out.println("Cargo: " + oFuncionario3.getDescricaoCargo());
System.out.println("Salario Bruto: " +
oFuncionario3.getSalarioBase());
System.out.println("Salario Liquido: " + oFuncionario3.calcularSalario
Imposto());

System.out.println("==================================");
} catch(RuntimeException e) {
System.out.println("Erro: "+e.getMessage());
}
}
}

Template Method

Em um player de música, que oferece várias maneiras de ordenar a playlist de


reprodução (nome da música, nome do Autor, ano etc.). Uma ideia seria utilizar
o padrão Strategy e implementar uma classe que define o método de reprodu-
ção para cada tipo de reprodução da playlist. Esta seria uma solução viável, pois
manteríamos a flexibilidade para implementar novos modos de reprodução de
maneira bem simples. No entanto, o algoritmo para reprodução de uma playlist
é o mesmo, independente de qual modo está sendo utilizado. A única diferença
é a criação da playlist, que leva em consideração um dos atributos da música para
a ordenação. Para suprir esta dificuldade, é recomendado o uso padrão Template
Method, que tem como vantagem a facilidade na alteração do código principal
do software.

capítulo 5 • 171
OrdenarTemplate.java
package br.estacio.templateMethod;
import java.util.ArrayList;
public abstract class OrdenarTemplate {
public abstract boolean isPrimeiro(Musica musica1,
Musica musica2);
protected ArrayList<Musica>
ordenarMusica(ArrayList<Musica>lista) {
ArrayList<Musica>aListaMusica = new ArrayList<Musica>();
for (Musica musicaMP3 : lista) {
aListaMusica.add(musicaMP3);
}
for (inti = 0; i<aListaMusica.size(); i++) {
for (intj = i; j<aListaMusica.size(); j++) {
if (!isPrimeiro(aListaMusica.get(i), aListaMusica.
get(j))) {
Musica temp = aListaMusica.get(j);
aListaMusica.set(j, aListaMusica.get(i));
aListaMusica.set(i, temp);
}
}
}
returna ListaMusica;
}
}

EnumModoReproducao.java
package br.estacio.templateMethod;
public enum EnumModoReproducao {
NOME, AUTOR, ANO, ESTRELA
}

capítulo 5 • 172
OrdenarAno.java
package br.estacio.templateMethod;
public class OrdenarAno extends OrdenarTemplate {
@Override
public boolean isPrimeiro(Musica musica1, Musica musica2) {
if (musica1.ano.compareToIgnoreCase(musica2.ano) <= 0) {
return true;
}
return false;
}
}

OrdenarAutor.java
package br.estacio.templateMethod;
public class OrdenarAutor extends OrdenarTemplate {
@Override
public boolean isPrimeiro(Musica musica1, Musica musica2) {
if (musica1.autor.compareToIgnoreCase(musica2.autor) <= 0) {
return true;
}
return false;
}
}

OrdenarEstrela.java
package br.estacio.templateMethod;
public class OrdenarEstrela extends OrdenarTemplate {
@Override
public boolean isPrimeiro(Musica musica1, Musica musica2) {
if (musica1.estrelas>musica2.estrelas) {
return true;
}
return false;
}
}

capítulo 5 • 173
OrdenarNome.java
package br.estacio.templateMethod;
public class OrdenarNome extends OrdenarTemplate {
@Override
public boolean isPrimeiro(Musica musica1, Musica musica2) {
if (musica1.nome.compareToIgnoreCase(musica2.nome) <= 0) {
return true;
}
return false;
}
}

Musica.java
package br.estacio.templateMethod;
public class Musica {
String nome;
String autor;
String ano;
int estrelas;
public Musica(String nome, String autor, String ano, int estrela)
{
this.nome = nome;
this.autor = autor;
this.ano = ano;
this.estrelas = estrela;
}
}

Cliente.java
package br.estacio.templateMethod;
public class Cliente {
public static void main(String[] args) {
Playlist playList = new Playlist(EnumModoReproducao.NOME);
playList.addMusica("Qualquer Coisa", "Caetano Veloso",
"1975", 5);

capítulo 5 • 174
playList.addMusica("Realce", "Gilberto Gil", "1979", 4);
playList.addMusica("Sob Medida", "Chico Buarque", "1995", 3);
playList.addMusica("Faceira", "Gal Costa", "1980", 2);
playList.addMusica("Tenha Calma", "Maria Bethânia", "1989",
1);
System.out.println("| ------------------------ |");
System.out.println("| Lista por Nome de Musica |");
System.out.println("| ------------------------ |");
playList.showLista();
System.out.println("| ------------------------ |");
System.out.println("| Lista por Autor |");
System.out.println("| ------------------------ |");
playList.setModoDeReproducao(EnumModoReproducaoAUTOR);
playList.showLista();
System.out.println("| ------------------------ |");
System.out.println("| Lista por Ano |");
System.out.println("| ------------------------ |");
playList.setModoDeReproducao(EnumModoReproducao.ANO);
playList.showLista();
System.out.println("| ------------------------ |");
System.out.println("| Lista por Estrela |");
System.out.println("| ------------------------ |");
playList.setModoDeReproducao(EnumModoReproducao.ESTRELA);
playList.showLista();
}
}

Visitor

Um sistema que possua a necessidade de apresentar seus dados, provenientes


de uma árvore binária. O padrão Visitor é indicado quando há a necessidade de
realizar diversas operações sobre um conjunto de dados, cujas operações sofrem
mudanças constantemente. Um outro exemplo é a construção de compiladores,
onde existem vários tipos de nós da árvore de sintaxe, e o conjunto de operações
é indeterminado.

capítulo 5 • 175
Arvore Visitor.java
package br.estacio.visitor;
public interface ArvoreVisitor {
void visitar(No no);
}

ArvoreBinaria.java
package br.estacio.visitor;
public class ArvoreBinaria {
No raiz;
int qtdElementos;
public ArvoreBinaria(int chaveRaiz) {
raiz = new No(chaveRaiz);
qtdElementos = 0;
}
public void addNo(int chave) {
addNo(chave, raiz);
}

public void remover(int chave){
}

public void buscar(int chave){

}
private void addNo(int chave, No no) {
if (no.getChave() == chave)
return;
if (chave>no.getChave()) {
if (no.getDireito() == null) {
no.setDireito(new No(chave));
qtdElementos++;
return;
}
addNo(chave, no.getDireito());
} else {

capítulo 5 • 176
if (no.getEsquerdo() == null) {
no.setEsquerdo(new No(chave));
qtdElementos++;
return;
}
addNo(chave, no.getEsquerdo());
}
}
public void aceitarVisitante(ArvoreVisitor visitor) {
visitor.visitar(raiz);
}
}

No.java
package br.estacio.visitor;
public class No {
protected int chave;
No esquerdo, direito;
public No(intchave) {
this.chave = chave;
esquerdo = null;
direito = null;
}
public String toString() {
return String.valueOf(chave);
}
public int getChave() {
returnchave;
}
public No getEsquerdo() {
returnesquerdo;
}
public void setEsquerdo(No esquerdo) {
this.esquerdo = esquerdo;
}

capítulo 5 • 177
public No getDireito() {
return direito;
}
public void setDireito(No direito) {
this.direito = direito;
}
}

ExibirIdentadoVisitor.java
package br.estacio.visitor;
public class ExibirIndentadoVisitor implements ArvoreVisitor {
@Override
public void visitar(No no) {
if (no == null) {
return;
}
System.out.println(no);
visitar(no.getEsquerdo(), 1);
visitar(no.getDireito(), 1);
}
private void visitar(No no, int qtdEspacos) {
if (no == null) {
return;
}
for (inti = 0; i<qtdEspacos; i++) {
System.out.print("-");
}
System.out.println(no);
visitar(no.getEsquerdo(), qtdEspacos + 1);
visitar(no.getDireito(), qtdEspacos + 1);
}
}

ExibirInOrderVisitor.java
package br.estacio.visitor;
public class ExibirInOrderVisitor implements ArvoreVisitor {

capítulo 5 • 178
@Override
public void visitar(No no) {
if (no == null)
return;
this.visitar(no.getEsquerdo());
System.out.println(no);
this.visitar(no.getDireito());
}
}

Exibir Post Order Visitor.java


package br.estacio.visitor;
public class ExibirPostOrderVisitor implements ArvoreVisitor {
@Override
public void visitar(No no) {
if (no == null)
return;
visitar(no.getEsquerdo());
visitar(no.getDireito());
System.out.println(no);
}
}

ExibirPreOrderVisitor.java
package br.estacio.visitor;
public class ExibirPreOrderVisitor implements ArvoreVisitor {
@Override
public void visitar(No no) {
if (no == null)
return;
System.out.println(no);
visitar(no.getEsquerdo());
visitar(no.getDireito());
}
}

capítulo 5 • 179
ATIVIDADES
01. No exemplo do padrão Façade, foi criada uma interface única de acesso às classes de
sistema. Para facilitar a manutenção do sistema, caso haja uma grande quantidade de clas-
ses e métodos, qual seria uma boa prática para a criação dessa interface?

02. Em um sistema bancário, uma conta corrente pode assumir diversas situações (saldo
positivo, saldo negativo, bloqueado etc.), e as operações na conta sofrerão modificações, de
acordo com o status em que ela se encontra. De acordo com as implementações anteriores,
qual o padrão de projeto recomendado para resolver esse problema? Justifique.

03. De acordo com a sugestão de implementação do padrão Command apresentada, des-


creva um problema que seja passível de resolução, utilizando esse padrão.

REFERÊNCIAS BIBLIOGRÁFICAS
GAMMA, ERICH et al. Design Patterns: Elements of Reusable Object-Oriented Software.
Addison-Wesley Professional. 1ª Edição. Estados Unidos da América: Addison-Wesley, 1995.
LARMAN, CRAIG. Utilizando UML e Padrões. 3ª Edição. Estados Unidos da América: Prentice Hall,
2007.
FREEMAN,ELISABETH; FREEMAN,ERIC.Use a Cabeça! Padrões de Projetos (design Patterns).
2ª Edição. Estados Unidos da América: Alta Books, 2009.

GABARITO
Capítulo 1

01. Problemas rotineiros, quando devidamente mitigados, podem sugerir uma resolução pa-
dronizada para cada ocorrência. Os padrões de projeto são uma forma de detalhar como
esses problemas podem ser resolvidos.

02. Porque muitas dessas soluções podem sofrer variações, de acordo com aspectos de
técnicos de implementação como linguagem, ambiente de desenvolvimento etc., logo, o im-
portante é transmitir o conceito da solução, e não o meio de construí-la.

capítulo 5 • 180
03. Um exemplo de padrão para e-commerce pode ser a elaboração de um esquema de
carrinho de compras. Usado a estrutura dos padrões, podemos descrever a solução da se-
guinte forma:
•  Nome: Carrinho de Compras;
•  Problema: Encontrar um meio de registrar os produtos, antes de armazenar no banco
de dados;
•  Solução: Criar uma variável de sessão, que armazene uma coleção de objetos produtos,
que possa ser alimentada sempre que o cliente requerer;
•  Consequência: Verificar o tempo de vida de variável de sessão. Caso não seja devidamen-
te gerenciada, ela pode expirar e o cliente ficará sem os itens do seu carrinho de compras
para finalizar a operação.

Capítulo 2

01. Definir formas de gerenciar e combinar diferentes comportamentos e responsabilidades


de classes e objetos de uma aplicação.

02. Facilitar o design do sistema identificando maneiras de realizar o relacionamento entre


as entidades, deixando o desenvolvedor livre desta preocupação.

03. Abstrair o processo de criação de objetos, ou seja, a sua instanciação. Desta maneira
o sistema não precisa se preocupar com questões sobre, como o objeto é criado, como é
composto, qual a sua representação real.

04. Decorator

05. Singleton e Observer

Capítulo  4

01. Na verdade, não. Independente da forma de desenvolvimento, haverá a disposição dos


componentes independem para o funcionamento do software. A escolha de arquitetura con-
solidada promove uma melhor organização desses componentes, mas a não escolha não
significa ausência de um modelo arquitetural. Ele vai existir, porém, não formalizado.

capítulo 5 • 181
02. O Arquiteto é um profissional que deve, além de conhecer diversos modelos de arqui-
tetura, tem que estar atualizado sobre as mudanças que a computação sofre com o passar
do tempo. Com a evolução das ferramentas e das maneiras de se construir um software, as
responsabilidades desse profissional podem sofrer várias mudanças, mas ainda é pré-maturo
prever a extinção desse papel.

03. A separação de três camadas do modelo MVC deve ser respeitado conceitualmente,
porém, em termos de implementação, naturalmente podem ser inseridas mais camadas, para
atender a alguma particularidade da implementação, como por exemplo, uma camada de
autenticação, porém, a estrutura de separação da visão, do controlador e do modelo têm que
ser respeitada.

04. É recomendável, para que seja mantido o baixo acoplamento entre os sistemas. Cada
sistema possui particularidades que podem variar, mesmo que eles tenham sido desenvolvi-
dos em ambientes idênticos.

05. Sim. Um exemplo seria uma aplicação feito na arquitetura Cliente-Servidor, implemen-
tando MVC.

Capítulo  5

01. Uma prática comum é a de criar várias classes Façade, que contenham os métodos de
um subsistema ou das classes individualmente, dependendo da granularidade de elementos
a ser agrupada.

02. Padrão State. Ele permite que um objeto altere o seu comportamento quando o seu
estado interno muda, e o objeto passará a impressão de ter mudado de classe. O Stateen
capsula os estados em classes separadas e delega as tarefas para o objeto que representa
o estado atual.

03. Em um jogo, onde os botões de um joystick podem assumir diferentes ações, dependen-
do do jogo, por exemplo, em um jogo de futebol, o botão A recebe o comando “Bola Alta()”,
quando o jogador estiver com a bola, ou o comando “Carrinho()”, quando o jogador estiver
sem a bola.

capítulo 5 • 182
ANOTAÇÕES

capítulo 5 • 183
ANOTAÇÕES

capítulo 5 • 184

Você também pode gostar