Vous êtes sur la page 1sur 110

Padrões de Desenho de Software

Texto de apoio à disciplina de:


Programação Orientada por Objectos
2º Ano – Engenharia Informática
3º Ano – Engenharia Electrónica e de Computadores

Instituto Politécnico de Setúbal – Escola Superior de Tecnologia

Docente: Anacleto Correia


11/10/2005
IPS-ESTSetúbal Padrões de Desenho

Índice
Nota Prévia ................................................................................................................... 3
Historial........................................................................................................................ 4
Padrões de Desenho GoF .............................................................................................. 5
Padrões de Criação ..................................................................8
Abstract Factory (Fábrica Abstracta) ..................................................................... 8
Builder (Fabricante) ............................................................................................ 14
Factory Method (Método-Fábrica)....................................................................... 19
Prototype (Protótipo)........................................................................................... 25
Singleton (Solitário) ............................................................................................ 31
Padrões Estruturais ................................................................ 34
Adapter (Adaptador) ........................................................................................... 34
Bridge (Ponte)..................................................................................................... 39
Composite (Composto)........................................................................................ 43
Decorator (Decorador) ........................................................................................ 47
Façade (Fachada) ................................................................................................ 51
Flyweight (Peso-Leve) ........................................................................................ 54
Proxy (Procurador).............................................................................................. 59
Padrões de Comportamento .................................................... 63
Chain of Responsibility (Cadeia de Responsabilidade) ........................................ 63
Command (Comando) ......................................................................................... 67
Interpreter (Interpretador).................................................................................... 71
Iterator (Iterador)................................................................................................. 75
Mediator (Mediador) ........................................................................................... 79
Memento (Memória) ........................................................................................... 83
Observer (Observador) ........................................................................................ 87
State (Estado)...................................................................................................... 92
Strategy (Estratégia)............................................................................................ 98
Template Method (Método-Modelo) ................................................................. 101
Visitor (Visitante) ............................................................................................. 104
Relações entre Padrões GoF .................................................. 109
Referências ............................................................................................................... 110

Anacleto Correia 2
IPS-ESTSetúbal Padrões de Desenho

Nota Prévia
O conteúdo deste documento resulta de compilação e tratamento de
elementos provenientes de diversas fontes, as quais se encontram
listadas nas “Referências”.

Anacleto Correia 3
IPS-ESTSetúbal Padrões de Desenho

Historial
Christopher Alexander descreve 250 padrões de desenho de
arquitectura, no livro “A Pattern Language: Towns, Buildings,
Construction”. A descrição de um padrão de desenho consiste na
apresentação e discussão de um determinado problema e na
descrição de uma solução que sirva para diversos problemas que
possam ser encontrados e que se encaixem no mesmo padrão.

De igual modo, no desenho de software, estudar padrões de desenho


é importante, porque:

· Permite aprender com a experiência de outros;


· Utilizar soluções amplamente testadas;
· Utilizar uma linguagem comum entre os designers e
programadores. Melhora-se assim a comunicação entre a
equipa e a documentação dos sistemas;
· Utilizar boas práticas no desenvolvimento de software orientado
a objectos, e obtém-se produtos de melhor qualidade.

Anacleto Correia 4
IPS-ESTSetúbal Padrões de Desenho

Padrões de Desenho GoF


Erich Gamma, John Vlissides, Ralph Jonhson e Richard Helm,
conhecidos como “The Gang of Four” (GoF), inspiraram-se no livro de
Alexander e escreveram “Design Patterns: Elements of Reusable
Object-Oriented Software”, onde descreveram 23 padrões de
desenho de software orientado por objectos.

Os 23 padrões GoF são geralmente agrupados nas três seguintes


categorias:

· Padrões de Criação (Creational): Abstract Factory, Builder,


Factory Method, Prototype, Singleton.
· Padrões de Estrutura (Structural): Adapter, Bridge,
Composite, Decorator, Facade, Flyweight, Proxy.
· Padrões de Comportamento (Behavioural): Chain of
Responsibility, Command, Interpreter, Iterator, Mediator,
Memento, Observer, State, Strategy, Template Method, Visitor.

Anacleto Correia 5
IPS-ESTSetúbal Padrões de Desenho

Objectivo
De Criação Estrutural Comportamental
Classe Factory Method Adapter Interpreter
Template Method

 Objecto Abstract Factory Adapter Chain of Responsability


m Builder Bridge Command
b Prototype Composite Iterator
i Singleton Decorator Mediator
t Facade Memento
o Flyweigth
Observer
State
Proxy
Strategy
Visitor

Os padrões referidos, podem também ser divididos, segundo a


classificação de Steven Metsker no seu livro Design Patterns Java
Workbook, em cinco categorias:

· Padrões de Interface: Adapter, Bridge, Composite e Façade;


· Padrões de Responsabilidade: Chain of Responsibility,
Flyweight, Mediator, Observer, Proxy e Singleton;
· Padrões de Construção: Abstract Factory, Builder, Factory
Method, Memento e Prototype;
· Padrões de Operação: Command, Interpreter, State,
Strategy e Template Method;
· Padrões de Extensão: Decorator, Iterator e Visitor.

É a primeira das categorias referidas, que servirá de base à


introdução aos padrões de desenho que em seguida se fará.

Anacleto Correia 6
IPS-ESTSetúbal Padrões de Desenho

No quadro seguinte resume-se a funcionalidade intrínseca a cada um


dos padrões de desenho.

Padrões Criação Têm a ver com a instanciação de objectos.


Fornece uma interface para criar e instanciar famílias
Abstract Factory de objectos relacionados, sem especificar classes
concretas
Builder Separa a construção do objecto da sua representação
Factory Method Cria uma instância de várias classes derivadas
Uma instância completamente inicializada, para ser
Prototype
copiada ou clonada
Singleton Uma classe da qual só pode existir uma única instância
Padrões Estruturais Têm a ver com a composição de classes ou objectos
Adapter Concilia interfaces de diferentes classes
Separa a interface de um objecto da sua
Bridge
implementação
Uma estrutura em árvore de objectos simples e
Composite
compostos
Decorator Adiciona dinamicamente responsabilidades a objectos
Facade Uma única classe representa um subsistema completo
Uma instância de pequena granularidade para partilha
Flyweight
eficiente
Proxy Um objecto que representa outro objecto
Padrões Comportamento
Caracterizam formas de interacção entre classes e
objectos
Uma forma de enviar um pedido entre uma cadeia de
Chain of Resp.
objectos.
Command Encapsula um comando num objecto
Uma forma de incluir elementos da linguagem num
Interpreter
programa.
Iterator Acesso sequencial a elementos de uma colecção.
Mediator Define comunicação simplificada entre classes
Memento Captura e restaura o estado interno de um objecto
Uma forma de notificar alterações num número de
Observer
classes.
Altera o comportamento de um objecto quando o seu
State
estado muda.
Strategy Encapsula um algoritmo numa classe
Difere os passos concretos de um algoritmo para uma
Template Method
subclasse
Define uma nova operação para uma classe sem
Visitor
alterações

Anacleto Correia 7
IPS-ESTSetúbal Padrões de Desenho

Padrões de Criação

São padrões que têm a ver com a instanciação de objectos.

Abstract Factory (Fábrica Abstracta)

Fornece uma interface para criar famílias de objectos relacionados ou


interdependentes sem especificar as suas classes concretas.

A fábrica abstracta é semelhante ao método-fábrica. Existe uma


classe conhecida pelo cliente que é a “Fábrica Abstracta”. Esta fábrica
é capaz de produzir alguns objectos e possui diversas
implementações, cada uma capaz de construir instâncias de
implementações diferentes dos objectos que a fábrica é capaz de
produzir. Dependendo da situação, o cliente irá obter uma instância
da fábrica de um determinado tipo, de maneira que passará a
construir objectos daquele tipo pela fábrica.

Participantes:

As classes e/ou objectos que participam no padrão são:

· AbstractFactory
o Declara uma interface para operações de criação de
produtos abstractos
· ConcreteFactory
o Implementa as operações de criação de objectos de
produtos concretos
· AbstractProduct
o Declara uma interface para um objecto do tipo produto
· Product
o Define um objecto do tipo produto a ser criado pela
correspondente fábrica concreta
o implementa a interface AbstractProduct
· Client
o Utiliza as interfaces declaradas pelas classes
AbstractFactory e AbstractProduct

Anacleto Correia 8
IPS-ESTSetúbal Padrões de Desenho

Anacleto Correia 9
IPS-ESTSetúbal Padrões de Desenho

Exemplo do padrão no mundo real

Este padrão é encontrado nos equipamentos de estampagem de


folhas de metal, utilizados na produção automóvel. O equipamento de
estampagem é uma fábrica abstracta que cria as peças da carroçaria
do automóvel. O mesmo equipamento é utilizado para estampar o
capo, pára-lamas e portas (dianteira e traseira, esquerda e direita),
tampa do porta-malas, etc., de diferentes modelos de automóveis. A
troca dos moldes de estampagem é efectuada através de plataformas
deslizantes. As classes concretas produzidas pelo equipamento
podem ser trocadas em apenas três minutos.

Anacleto Correia 10
IPS-ESTSetúbal Padrões de Desenho

Exemplo

// Abstract Factory pattern


/// classe de teste do Jogo
public class Jogo
{
public static void main(String[] args)
{
// Criar e executar o Mundo Animal de Africa
FabricaContinente africa = new FabricaAfrica();
MundoAnimal mundo = new MundoAnimal( africa );
mundo.executaCadeiaAlimentar();
// Criar e executar o Mundo Animal da America
FabricaContinente america = new FabricaAmerica();
mundo = new MundoAnimal( america );
mundo.executaCadeiaAlimentar();
}
}
// "AbstractFactory"
abstract class FabricaContinente
{
// Métodos
abstract public Herbivoro criarHerbivoro();
abstract public Carnivoro criarCarnivoro();
}
// "ConcreteFactory1"
class FabricaAfrica extends FabricaContinente
{
// Métodos
public Herbivoro criarHerbivoro()
{

Anacleto Correia 11
IPS-ESTSetúbal Padrões de Desenho

return new Antilope();


}
public Carnivoro criarCarnivoro()
{
return new Leao();
}
}
// "ConcreteFactory2"
class FabricaAmerica extends FabricaContinente
{
// Métodos
public Herbivoro criarHerbivoro()
{
return new Bisonte();
}

public Carnivoro criarCarnivoro()


{
return new Lobo();
}
}
// "AbstractProductA"
abstract class Herbivoro
{
abstract public String toString();
abstract public void pasta();
}
// "AbstractProductB"
abstract class Carnivoro
{
// Métodos
abstract public void devora( Herbivoro h );
abstract public String toString();
}
// "ProductA1"
class Antilope extends Herbivoro
{
public String toString()
{
return "Antilope";
}
public void pasta()
{
System.out.println( "Antilope pasta na savana");
}
}
// "ProductB1"
class Leao extends Carnivoro
{
// Métodos
public void devora( Herbivoro h )
{
// caça Antilope
System.out.println( this + " caça o " + h );
}
public String toString()
{
return "Leao";
}
}
// "ProductA2"

Anacleto Correia 12
IPS-ESTSetúbal Padrões de Desenho

class Bisonte extends Herbivoro


{
public String toString()
{
return "Bisonte";
}
public void pasta()
{
System.out.println( "Bisonte pasta na pradaria");
}

}
// "ProductB2"
class Lobo extends Carnivoro
{
// Métodos
public void devora( Herbivoro h )
{
// caça Bisonte
System.out.println( this + " caça o " + h );
}
public String toString()
{
return "Lobo";
}

}
// "Client"
class MundoAnimal
{
// Variáveis de Instância
private Herbivoro herbivoro;
private Carnivoro carnivoro;

// Constructores
public MundoAnimal( FabricaContinente fabrica )
{
carnivoro = fabrica.criarCarnivoro();
herbivoro = fabrica.criarHerbivoro();
}

// Métodos
public void executaCadeiaAlimentar()
{
herbivoro.pasta();
carnivoro.devora( herbivoro );
}
}
// Antilope pasta na savana
// Leao caça o Antilope
// Bisonte pasta na pradaria
// Lobo caça o Bisonte

Anacleto Correia 13
IPS-ESTSetúbal Padrões de Desenho

Builder (Fabricante)

Separa a construção de um objecto complexo da sua representação


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

O padrão builder consiste em delegar numa classe específica,


designada por Director, a tarefa de construir objectos complexos e
similares entre si. O cliente especifica ao director o que quer construir
e este último irá providenciar a construção, chamando o construtor
adequado para cada situação. A utilização deste padrão é
conveniente quando o processo de construção do objecto é complexo
e passível de futura alteração, de forma que o cliente não tenha de
saber como funciona, para não ficar acoplado.

Participantes:

As classes e/ou objectos que participam no padrão são:

· Builder
o Especifica uma interface abstracta de criação de
componentes de um objecto de Product
· ConcreteBuilder
o Constrói e monta as componentes de Product,
implementado a interface Builder
o Define e mantém referência da representação (instância
de Product) que cria
o Fornece uma interface para obtenção do produto criado
· Director
o Constrói um objecto utilizando a interface Builder
· Product
o Representa o objecto complexo a ser construído.
ConcreteBuilder constrói a representação interna do
produto e define o processo pelo qual é montado.
o Inclui classes que definem as componentes constituintes,
incluindo as interfaces para montagem dos componentes
no produto acabado

Anacleto Correia 14
IPS-ESTSetúbal Padrões de Desenho

Exemplo do padrão no mundo real

Este padrão é utilizado em restaurantes fast food para servir as refeições de


crianças. Estas refeições consistem tipicamente num artigo principal, um
acompanhamento, uma bebida, e um brinquedo (e.g., hamburger, batatas
fritas, cola e carro de brinquedo). De notar que pode haver variação no
conteúdo de uma refeição de criança, mas o processo de construção dessa
refeição, é sempre o mesmo. Quer o cliente requisite um hamburger,
cheeseburger, ou pernas de galinha, o processo é o mesmo. O empregado de
balcão solicita ao pessoal do empacotamento que junte um artigo principal, o
acompanhamento e brinquedo. Estes artigos são colocados de seguida num
saco. A bebida é colocada num copo junto do saco. Este processo é
semelhante em qualquer restaurante do género.

Anacleto Correia 15
IPS-ESTSetúbal Padrões de Desenho

Exemplo

import java.util.Collections;
import java.util.HashMap;
// padrão Builder -- exemplo real

// “Client”
public class MainApp
{
public static void main(String[] args)
{
// Cria um concessionario com construtores de veiculos
Concessionario concessionario = new Concessionario();
VeiculoBuilder b1 = new ScooterBuilder();
VeiculoBuilder b2 = new AutomovelBuilder();
VeiculoBuilder b3 = new MotocicloBuilder();
// Constrói e mostra veiculos
concessionario.Construct(b1);
b1.getVeiculo().mostrar();
concessionario.Construct(b2);
b2.getVeiculo().mostrar();
concessionario.Construct(b3);
b3.getVeiculo().mostrar();
}
}
// "Director"
class Concessionario
{
// Builder utiliza um conjunto complexo de passos
public void Construct(VeiculoBuilder veiculoBuilder)

Anacleto Correia 16
IPS-ESTSetúbal Padrões de Desenho

{
veiculoBuilder.ConstroiEstrutura();
veiculoBuilder.ConstroiMotor();
veiculoBuilder.ConstroiRodas();
veiculoBuilder.ConstroiPortas();
}
}
// "Builder"
abstract class VeiculoBuilder
{
protected Veiculo veiculo;
public Veiculo getVeiculo()
{
return veiculo;
}
public abstract void ConstroiEstrutura();
public abstract void ConstroiMotor();
public abstract void ConstroiRodas();
public abstract void ConstroiPortas();
}
// "ConcreteBuilder1"
class MotocicloBuilder extends VeiculoBuilder
{
public void ConstroiEstrutura()
{
veiculo = new Veiculo("Motociclo");
veiculo.setCaracteristica("estrutura","Estrutura Motociclo");
}
public void ConstroiMotor()
{
veiculo.setCaracteristica("motor","500 cc");
}
public void ConstroiRodas()
{
veiculo.setCaracteristica("rodas","2");
}
public void ConstroiPortas()
{
veiculo.setCaracteristica("portas","0");
}
}
// "ConcreteBuilder2"
class AutomovelBuilder extends VeiculoBuilder
{
public void ConstroiEstrutura()
{
veiculo = new Veiculo("Automovel");
veiculo.setCaracteristica("estrutura","Estrutura Automovel");
}
public void ConstroiMotor()
{
veiculo.setCaracteristica("motor","2500 cc");
}
public void ConstroiRodas()
{
veiculo.setCaracteristica("rodas","4");
}
public void ConstroiPortas()
{
veiculo.setCaracteristica("portas","4");
}

Anacleto Correia 17
IPS-ESTSetúbal Padrões de Desenho

}
// "ConcreteBuilder3"
class ScooterBuilder extends VeiculoBuilder
{
public void ConstroiEstrutura()
{
veiculo = new Veiculo("Scooter");
veiculo.setCaracteristica("estrutura","Estrutura Scooter");
}
public void ConstroiMotor()
{
veiculo.setCaracteristica("motor","50 cc");
}
public void ConstroiRodas()
{
veiculo.setCaracteristica("rodas","2");
}
public void ConstroiPortas()
{
veiculo.setCaracteristica("portas","0");
}
}
// "Product"
class Veiculo
{
private String tipo;
private HashMap componentes = new HashMap();
// Constructor do Veiculo
public Veiculo(String tipo)
{
this.tipo = tipo;
}
public Object getCaracteristica(String key){
return componentes.get(new Integer(key.hashCode()));
}
public void setCaracteristica(String key, String value){
componentes.put(new Integer(key.hashCode()),value);
}
public void mostrar()
{
System.out.println("\n---------------------------");
System.out.println(" Tipo Veiculo: "+ tipo);
System.out.println(" Estrutura : " + componentes.get(new
Integer("estrutura".hashCode())));
System.out.println(" Motor : " + componentes.get(new
Integer("motor".hashCode())) );
System.out.println(" #Rodas: " + componentes.get(new
Integer("rodas".hashCode())) );
System.out.println(" #Portas : " + componentes.get(new
Integer("portas".hashCode())) );
}
}

Anacleto Correia 18
IPS-ESTSetúbal Padrões de Desenho

Factory Method (Método-Fábrica)

Define uma interface para criar um objecto, mas deixa as subclasses


decidirem que classe instanciar. O padrão Factory Method deixa uma
classe transferir a responsabilidade de instanciação para as suas
subclasses.

O método-fábrica também desacopla o cliente dos construtores dos


objectos que deseja criar. Cria-se uma classe abstracta Fábrica e uma
subclasse para cada objecto que se pretende criar. O cliente só
conhece a classe Fábrica e requisita-lhe objectos, tendo por base um
parâmetro discriminador.

Participantes:

As classes e/ou objectos que participam no padrão são:

· Product
o Define uma interface de objectos que o método-fábrica
cria
· ConcreteProduct
o Implementa a interface do Product
· Creator
o Declara o método-fábrica, que retorna um objecto do tipo
Product. Creator pode também definir uma
implementação por omissão do método-fábrica, que
retorna por omissão um objecto ConcreteProduct.
o Pode evocar o método-fábrica para criar o objecto
Product.
· ConcreteCreator
o Sobre-escreve o método-fábrica para retornar uma
instância de ConcreteProduct.

Anacleto Correia 19
IPS-ESTSetúbal Padrões de Desenho

Exemplo do padrão no mundo real

Os fabricantes de brinquedos de plástico processam moldes de plástico e


injectam partículas de plástico em moldes das formas desejadas. A classe do
brinquedo (carro, cavalo, super herói, etc.) é determinada pelo molde.

Exemplo

// ------------------------ Factory Method


// Define uma interface para a criação de objectos, mas deixa as
// subclasses decidirem que classe instanciam.
// "Product"
abstract class Product {}
// "ConcreteProductA"
class ConcreteProductA extends Product {}
// "ConcreteProductB"
class ConcreteProductB extends Product {}
// "Creator"
abstract class Creator
{
// Methods
abstract public Product FactoryMethod();
}
// "ConcreteCreatorA"
class ConcreteCreatorA extends Creator
{
// Methods
public Product FactoryMethod()
{
return new ConcreteProductA();
}
}
// "ConcreteCreatorB"
class ConcreteCreatorB extends Creator
{

Anacleto Correia 20
IPS-ESTSetúbal Padrões de Desenho

// Methods
public Product FactoryMethod()
{
return new ConcreteProductB();
}
}
// "Client"
public class Factory_Estrutural {
// Main method
public static void main (String[] args) {
// Create director and builders
Creator ca = new ConcreteCreatorA();
Product pa = ca.FactoryMethod();
System.out.println ("Created " + pa);

Creator cb = new ConcreteCreatorB();


Product pb = cb.FactoryMethod();
System.out.println ("Created " + pb);
}
}

Anacleto Correia 21
IPS-ESTSetúbal Padrões de Desenho

Exemplo – flexibilidade de criação de documentos diferentes

// As classes Report e Resume, subclasses de Document (classe base)


// são especializações da classe Document. O FactoryMethod é evocado
// no método construtor da classe base.
// Product
abstract class Page {abstract class Page {
public String toString() {
return this.getClass().getName();
}
}
// ConcreteProduct
class SkillsPage
extends Page {}
// ConcreteProduct
class EducationPage
extends Page {}
// ConcreteProduct
class ExperiencePage
extends Page {}
// ConcreteProduct
class IntroductionPage
extends Page {}

Anacleto Correia 22
IPS-ESTSetúbal Padrões de Desenho

// ConcreteProduct
class SummaryPage
extends Page {}
// ConcreteProduct
class BibliographyPage
extends Page {}
// ConcreteProduct
class ResultsPage
extends Page {}
// ConcreteProduct
class ConclusionPage
extends Page {}
// Creator
abstract class Document{
protected ArrayList pages = new ArrayList();
public Document() {
this.CreatePages();
}
public ArrayList getPages() {
return pages;
}
public String toString() {
return this.getClass().getName();
}
abstract public void CreatePages();
}
// ConcreteCreator
class Resume extends Document{
public void CreatePages() {
pages.add (new SkillsPage());
pages.add (new EducationPage());
pages.add (new ExperiencePage());
}
}
// ConcreteCreator
class Report extends Document
{
public void CreatePages()
{
pages.add (new IntroductionPage());
pages.add (new ResultsPage());
pages.add (new ConclusionPage());
pages.add (new SummaryPage());
pages.add (new BibliographyPage());
}
}
// Client
class Test {
public static void main( String[] args )
{
Document[] docs = new Document[ 2 ];
docs[0] = new Resume(); //Cria ConcreteCraetorA
docs[1] = new Report();// Cria ConcreteCreator B
for( int i=0; i<docs.length;i++)
{
System.out.println( "\n" + docs[i] + " ------- " );
ArrayList p =((Document)docs[i]).getPages();
for( int j=0; j<p.size();j++)
{
System.out.println(" " + p.get(j));
}

Anacleto Correia 23
IPS-ESTSetúbal Padrões de Desenho

}
}
}
/*
Resume -------
SkillsPage
EducationPage
ExperiencePage

Report -------
IntroductionPage
ResultsPage
ConclusionPage
SummaryPage
BibliographyPage
*/

Anacleto Correia 24
IPS-ESTSetúbal Padrões de Desenho

Prototype (Protótipo)

Especifica os tipos de objectos a criar usando uma instância-protótipo


e cria novos objectos copiando este protótipo.

O padrão Prototype consiste pois em construir objectos a partir de


outros já existentes, aproveitando suas características. O método
clone() da classe Object em Java é um exemplo deste padrão.

Participantes:

As classes e/ou objectos que participam no padrão são:

· Prototype
o Declara uma interface para se autoclonar
· ConcretePrototype
o Implementa uma interface para se autoclonar
· Client
o Cria um novo objecto pedindo ao protótipo para se
autoclonar

Exemplo

// Prototype pattern
import java.lang.Cloneable;
// Client test application
public class ClientApp
{
public static void main(String[] args)
{
// Create first instance and clone it
Prototype p1 = new ConcretePrototype1("I");

Anacleto Correia 25
IPS-ESTSetúbal Padrões de Desenho

ConcretePrototype1 c1 = (ConcretePrototype1) p1.clone();


System.out.println("clone: "+ c1.getId());
// Create second instances and clone it
Prototype p2 = new ConcretePrototype2("II");
ConcretePrototype2 c2 = (ConcretePrototype2) p2.clone();
System.out.println("clone: "+ c2.getId());
}
}
// "Prototype"
abstract class Prototype implements Cloneable
{
private String id;
// Constructor
public Prototype(String id)
{
this.id = id;
}
// Property
public String getId()
{
return id;
}
public abstract Object clone();
}
// "ConcretePrototype1"
class ConcretePrototype1 extends Prototype
{
// Constructor
public ConcretePrototype1(String id)
{
super(id);
}
public Object clone()
{
// Shallow copy
return new ConcretePrototype1(getId());
}
}
// "ConcretePrototype2"
class ConcretePrototype2 extends Prototype
{
// Constructor
public ConcretePrototype2(String id)
{
super(id);
}
public Object clone()
{
// Shallow copy
return new ConcretePrototype2(getId());
}
}
// clone: I
// clone: II

Exemplo do padrão no mundo real

Os protótipos de novos produtos são frequentemente construídos


antes da produção definitiva. Nesse caso o protótipo é passivo, e não
participa na sua própria cópia.

Anacleto Correia 26
IPS-ESTSetúbal Padrões de Desenho

A divisão mitótica de uma célula, tendo por resultado células


idênticas, é um exemplo de um protótipo que tem um papel activo na
cópia de si próprio, demonstrando assim o padrão protótipo. Quando
uma célula se divide, resultam duas células com idêntico genoma. Por
outras palavras, a célula clona-se a si própria.

Exemplo

/*
Demonstração do padrão Prototype em que as novas cores
são criadas a partir de cópia das pré-existentes do mesmo tipo
*/
// Padrão Prototype -- examplo real
import java.util.Collections;
import java.lang.Cloneable;
import java.util.HashMap;

// ClientApp - applicação de teste


public class ClientApp {
public static void main(String[] args) {
GestorCor gestorCor = new GestorCor();
// inicializar com cores standard
gestorCor.setCaracteristica("vermelha", new Cor(255, 0, 0));
gestorCor.setCaracteristica("verde", new Cor(0, 255, 0));
gestorCor.setCaracteristica("azul", new Cor(0, 0, 255));
// adicionar cores personalizadas
gestorCor.setCaracteristica("zangada", new Cor(255, 54, 0));
gestorCor.setCaracteristica("paz", new Cor(128, 211, 128));
gestorCor.setCaracteristica("chama", new Cor(211, 34, 20));
Cor cor;
// selecção de cores
String name = "vermelha";
cor = (Cor) gestorCor.getCaracteristica(new
Integer(name.hashCode())).clone();
name = "paz";
cor = (Cor) gestorCor.getCaracteristica(new
Integer(name.hashCode())).clone();
name = "chama";
cor = (Cor) gestorCor.getCaracteristica(new
Integer(name.hashCode())).clone();
}

Anacleto Correia 27
IPS-ESTSetúbal Padrões de Desenho

// "Prototype"
abstract class CorPrototype
implements Cloneable {
public abstract Object clone();
}

// "ConcretePrototype"
class Cor
extends CorPrototype {
private int vermelha;
private int verde;
private int azul;
// Constructor
public Cor(int vermelha, int verde, int azul) {
this.vermelha = vermelha;
this.verde = verde;
this.azul = azul;
}

// Criação de uma cópia superficial


public Object clone() {
System.out.println(
"Clonagem cor RGB: " + vermelha + ", " + verde + ", " + azul);
return (CorPrototype)new Cor(vermelha, verde, azul);
}
}

// Prototype manager
class GestorCor {
private HashMap componentes = new HashMap();
HashMap cors = new HashMap();
public CorPrototype getCaracteristica(Integer name) {
return (CorPrototype) cors.get(new Integer(name.hashCode()));
}

public void setCaracteristica(String name, Cor value) {


cors.put(new Integer(name.hashCode()), value);
}
}
// Clonagem cor RGB: 255, 0, 0
// Clonagem cor RGB: 128, 211, 128
// Clonagem cor RGB: 211, 34, 20

Anacleto Correia 28
IPS-ESTSetúbal Padrões de Desenho

Exemplo

public class TestPrototype {


public static void main(String[] args)
{
System.out.println(
"Criando uma Prototype Factory com uma ColherSopa e uma GarfoSalada");
PrototypeFactory prototypeFactory =
new PrototypeFactory(new ColherSopa(),
new GarfoSalada());
AbstractColher colher = prototypeFactory.makeColher();
AbstractGarfo garfo = prototypeFactory.makeGarfo();
System.out.println("Obtendo o nome da Colher e Garfo:");
System.out.println("Colher: " + colher.getColherName() + ", Garfo: " +
garfo.getGarfoName());
System.out.println(" ");
System.out.println(
"Criando uma Prototype Factory com uma SaladaColher e uma GarfoSalada");
prototypeFactory = new PrototypeFactory(new SaladaColher(),
new GarfoSalada());
colher = prototypeFactory.makeColher();
garfo = prototypeFactory.makeGarfo();
System.out.println("Obtendo o nome da Colher e do Garfo:");
System.out.println(
"Colher: " + colher.getColherName() + ", Garfo: " +
garfo.getGarfoName());
}
}
// PrototypeFactory.java
// a Factory for Prototypes
class PrototypeFactory
{
AbstractColher prototypeColher;
AbstractGarfo prototypeGarfo;
public PrototypeFactory(AbstractColher colher, AbstractGarfo garfo) {
prototypeColher = colher;
prototypeGarfo = garfo;
}
public AbstractColher makeColher() {
return (AbstractColher)prototypeColher.clone();
}
public AbstractGarfo makeGarfo() {
return (AbstractGarfo)prototypeGarfo.clone();
}
}
// AbstractColher.java
// One of Two Prototypes
abstract class AbstractColher implements Cloneable
{

Anacleto Correia 29
IPS-ESTSetúbal Padrões de Desenho

String colherName;
public void setColherName(String colherName) {
this.colherName = colherName;
}
public String getColherName() {return this.colherName;}
public Object clone()
{
Object object = null;
try {
object = super.clone();
} catch (CloneNotSupportedException exception) {
System.err.println("AbstractColher is not Cloneable");
}
return object;
}
}
// AbstractGarfo.java
// Two of Two Prototypes
abstract class AbstractGarfo implements Cloneable
{
String garfoName;
public void setGarfoName(String garfoName) {this.garfoName = garfoName;}
public String getGarfoName() {return this.garfoName;}
public Object clone()
{
Object object = null;
try {
object = super.clone();
} catch (CloneNotSupportedException exception) {
System.err.println("AbstractGarfo is not Cloneable");
}
return object;
}
}
// ColherSopa.java
// One of Two Concrete Prototypes extending the AbstractColher Prototype
class ColherSopa extends AbstractColher
{
public ColherSopa()
{
setColherName("Colher Sopa");
}
}
// SaladaColher.java
// Two of Two Concrete Prototypes extending the AbstractColher Prototype
class SaladaColher extends AbstractColher
{
public SaladaColher()
{
setColherName("Colher Salada");
}
}
// GarfoSalada.java
// The Concrete Prototype extending the AbstractGarfo Prototype
class GarfoSalada extends AbstractGarfo
{
public GarfoSalada()
{
setGarfoName("Garfo Salada");
}
}

// Criando uma Prototype Factory com uma ColherSopa e uma GarfoSalada


// Obtendo o nome da Colher e Garfo:
// Colher: Colher Sopa, Garfo: Garfo Salada
//
//
// Criando uma Prototype Factory com uma SaladaColher e uma GarfoSalada
// Obtendo o nome da Colher e do Garfo:
// Colher: Colher Salada, Garfo: Garfo Salada

Anacleto Correia 30
IPS-ESTSetúbal Padrões de Desenho

Singleton (Solitário)

Garante que uma classe tenha uma única instância e fornece um


ponto global de acesso à instância.

Este padrão visa garantir que uma classe possui apenas uma
instância. Assim, um singleton possui construtor privado (ou
protegido) e um método obterInstancia(), que deve ser
sincronizado para que duas threads não o executem ao mesmo
tempo. Quando executado pela primeira vez, cria a instância e grava-
a numa propriedade static da classe, retornando esta mesma
propriedade sempre que requsitada pelo obterInstancia().

Participantes:

As classes e/ou objectos que participam no padrão são:

· Singleton
o Define o método de classe Instance (ou
obterInstancia()) que permite aos clientes acederem à
sua única instância.
o Responsável por criar e manter a sua única instância.

Exemplo do padrão no mundo real

O mandato do Presidente da República de um país é um Singleton. A


Constituição do país especifica a forma de eleição, o fim do mandato
e a forma de sucessão. Como consequência, só pode existir um único
Presidente em actividade em determinado momento.
Independentemente da identidade da pessoa que ocupa a
presidência, o título de “Presidente da República” é um ponto de
acesso que identifica qualquer pessoa que exerça o mandato.

Anacleto Correia 31
IPS-ESTSetúbal Padrões de Desenho

Exemplo

// ---------------------------- Padrão Singleton


// classe declarada como final impede seu uso através de herança
// "Singleton"
public final class SingletonImpl {
// campo estático armazena a única instância desta classe
private static SingletonImpl instance = null;
// construtores devem ser privados para impedir uso externo
private SingletonImpl() {
// campos da classe podem ser
// normalmente inicializados
}
// retorna o único objeto que pode ser instanciado
public static SingletonImpl getInstance() {
if (instance==null) {
// instanciação ocorre apenas se um objecto for solicitado
instance = new SingletonImpl();
}
return instance;
}
}
// "Cliente"
public class UsoDoSingletonImpl {
// ............
SingletonImpl obj;
// ............
obj = SingletonImpl.getInstance();
// ............
}

Exemplo de criação de uma instância global usando o Padrão Singleton

// Singleton para variáveis globais de uma aplicação


public final class GlobalVars {
// campo estático para referência única
private static GlobalVars instance = null;
// campos da classe
public int inteiro;
public float real;

// construtor privados
private GlobalVars() {
// campos da classe podem ser
// normalmente inicializados
inteiro = 0;
real = -1;
}

Anacleto Correia 32
IPS-ESTSetúbal Padrões de Desenho

public static GlobalVars getInstance() {


if (instance==null) {
// instanciação ocorre apenas se um objecto for solicitado
instance = new GlobalVars();
}
return instance;
}
}

public class TestaGlobalVars {


private GlobalVars globais;

public TestaGlobalVars() {
globais = GlobalVars.getInstance();
}

public void imprimeVariaveis(GlobalVars gv) {


System.out.println("inteiro = " + gv.inteiro);
System.out.println("real = " + gv.real);
}

public void ajustaVariaveis(GlobalVars gv, int i, float r) {


gv.inteiro = i;
gv.real = r;
}
public void teste() {
System.out.println("1/a Instancia");
imprimeVariaveis(globais);
ajustaVariaveis(globais, -50, 30.69f);
imprimeVariaveis(globais);
// tentativa de criar outra instância
GlobalVars duplicata = GlobalVars.getInstance();
System.out.println("Duplicado");
imprimeVariaveis(duplicado);
ajustaVariaveis(duplicata, 20, -10.85f);
imprimeVariaveis(duplicado);
System.out.println("1/a Instancia");
imprimeVariaveis(globais);
}
public static void main(String a[]) {
new TestaGlobalVars().teste();
}
}
/*
1/a Instancia
inteiro = 0
real = -1.0
inteiro = -50
real = 30.69

Duplicado
inteiro = -50
real = 30.69
inteiro = 20
real = -10.85
1/a Instancia
inteiro = 20
real = -10.85
*/

Anacleto Correia 33
IPS-ESTSetúbal Padrões de Desenho

Padrões Estruturais

Padrões que têm a ver com a composição de classes ou objectos

Adapter (Adaptador)

Converte a interface de uma classe numa outra interface que os


clientes possam utilizar. O padrão Adapter permite que classes
trabalhem em conjunto apesar de possuírem à partida interfaces
incompatíveis.

Em diversas ocasiões, temos uma classe X que utiliza através do seu


método x() uma outra classe Y. No entanto, na interface da classe Y,
o método possui o nome y(). Para que as duas classes comuniquem,
é necessário que haja um adaptador entre elas. Assim, cria-se a
classe Adapter que implementa a interface esperada por X, evocando
o método apropriado de Y.

Dois exemplos clássicos de adapter existem no API J2SE. No pacote


java.awt.event existem vários adaptadores (MouseAdapter,
KeyAdapter, …). As classes-envolventes (wrapper classes: Integer,
Long, …) também são adaptadoras, pois adaptam os tipos primitivos
para os casos em que só são aceites objectos (nas colecções, por
exemplo).

Participantes:

As classes e/ou objectos que participam no padrão são:

· Target
o Define o domínio específico da interface que Client irá
utilizar.
· Adapter
o Adapta a interface de Adaptee à interface de Target.
· Adaptee
o Define uma interface existente que necessita de ser
adaptada.
· Client
o Colabora com objectos que estejam conforme a interface
de Target.

Anacleto Correia 34
IPS-ESTSetúbal Padrões de Desenho

Exemplo do padrão no mundo real

As chaves de encaixes são um exemplo de adaptador. Um encaixe


pode ser adaptado a uma cremalheira, desde que o tamanho das
guias seja o mesmo. As guias típicas, nos EUA, têm dimensões de
1/2" e 1/4". Naturalmente que uma guia de uma cremalheira de 1/2",
não se ajusta a uma guia de encaixe de 1/4", a não ser que seja
utilizado um adaptador. Um adaptador de 1/2" para 1/4" tem uma
fêmea de 1/2" para se ajustar às guias de 1/2" da cremalheira e um
macho de 1/4" para se ajustar aos guias do encaixe de 1/4".

Anacleto Correia 35
IPS-ESTSetúbal Padrões de Desenho

Exemplo

// Padrão Adapter

// "Target"
class Composto {
protected String nome;
protected float pontoEbulicao;
protected float pontoLiquidificacao;
protected double pesoMolecular;
protected String formulaMolecular;

// Constructor
public Composto(String nome) {
this.nome = nome;
}

public void mostrar() {


System.out.println("\nComposto: " + nome + " ------ ");
}
}

// "Adapter"
class CompostoRico
extends Composto {
private BaseBadosQuimica banco;
// Constructor
public CompostoRico(String nome) {
super(nome);
}

public void mostrar() {


// Adaptee
banco = new BaseBadosQuimica();
pontoEbulicao = banco.getPontoCritico(nome, "E");
pontoLiquidificacao = banco.getPontoCritico(nome, "L");
pesoMolecular = banco.getPesoMolecular(nome);
formulaMolecular = banco.getEstruturaMolecular(nome);

super.mostrar();

Anacleto Correia 36
IPS-ESTSetúbal Padrões de Desenho

System.out.println(" Formula: " + formulaMolecular);


System.out.println(" Peso : " + pesoMolecular);
System.out.println(" Ponto de Liquidificação: " + pontoLiquidificacao);
System.out.println(" Ponto de Ebulição: " + pontoEbulicao);
}
}

// "Adaptee"
class BaseBadosQuimica {
// A Base de Dados com 'API legada'
public float getPontoCritico(String compound, String point) {
float temperatura = 0.0F;
// Ponto de Liquidificação
if (point.compareTo("L") == 0) {
switch (compound.toLowerCase().charAt(1)) {
case 'g':
temperatura = 0.0F;
break; // "água"
case 'e':
temperatura = 5.5F;
break; //"benzina"
case 'l':
temperatura = -114.1F;
break; //"alcool"
}
}
// Ponto de ebulição
else {
switch (compound.toLowerCase().charAt(1)) {
case 'g':
temperatura = 100.0F;
break; // "água"
case 'e':
temperatura = 80.1F;
break; //"benzina"
case 'l':
temperatura = 78.3F;
break; //"alcool"
}
}
return temperatura;
}

public String getEstruturaMolecular(String compound) {


String structure = "";

switch (compound.toLowerCase().charAt(1)) {
case 'g':
structure = "H20";
break; // "água"
case 'e':
structure = "C6H6";
break; //"benzina"
case 'l':
structure = "C2H6O2";
break; //"alcool"
}
return structure;
}

public double getPesoMolecular(String compound) {


double peso = 0.0;
switch (compound.toLowerCase().charAt(1)) {
case 'g':
peso = 18.015;
break; // "água"
case 'e':
peso = 78.1134;
break; //"benzina"
case 'l':
peso = 46.0688;
break; //"alcool"
}
return peso;
}
}

Anacleto Correia 37
IPS-ESTSetúbal Padrões de Desenho

// aplicação Client
class AdapterPat {
public static void main(String[] args) {
// componente químico não adaptado
Composto coisa = new Composto("Desconhecido");
coisa.mostrar();

// componente químico adaptado


Composto agua = new CompostoRico("Agua");
agua.mostrar();

Composto benzina = new CompostoRico("Benzina");


benzina.mostrar();

Composto alcool = new CompostoRico("Alcool");


alcool.mostrar();
}
}
/*
Composto: Desconhecido ------
Composto: Agua ------
Formula: H20
Peso : 18.015
Ponto de Liquidificação: 0.0
Ponto de Ebulição: 100.0
Composto: Benzina ------
Formula: C6H6
Peso : 78.1134
Ponto de Liquidificação: 5.5
Ponto de Ebulição: 80.1
Composto: Alcool ------
Formula: C2H6O2
Peso : 46.0688
Ponto de Liquidificação: -114.1
Ponto de Ebulição: 78.3
*/

Anacleto Correia 38
IPS-ESTSetúbal Padrões de Desenho

Bridge (Ponte)

Desacopla uma abstracção da sua implementação de forma que as


duas possam variar de forma independente.

Por exemplo, num sistema académico se tiver uma interface


Estudante e duas classes que efectuem a sua implementação: uma
para armazenamento dos dados em XML e outra através de
serialização em ficheiro. Em princípio não teríamos problemas com
esta abordagem. Mas se pretender criar subinterfaces de Estudante:
EstudanteBacharelato, EstudanteMestrado, EstudanteDoutoramento,
etc. então não se teria apenas duas implementações, mas seis!
Separando o modelo da persistência e criando uma ponte (bridge)
entre eles, o problema ficaria resolvido.

Função das classes participantes:

· Abstraction - Define a interface da abstracção e mantém uma


referência para um objecto do tipo Implementor;
· RefinedAbstraction – Estende a interface definida por
Abstraction;
· Implementor – Define a interface para as classes que
realizam a implementação; não necessita de corresponder
exactamente à interface de Abstraction;
· ConcreteImplementor – Implementa a interface de
Implementor.

Anacleto Correia 39
IPS-ESTSetúbal Padrões de Desenho

Exemplo do padrão no mundo real

Um interruptor doméstico, utilizado para controlo da iluminação,


ventilação, etc., é um exemplo de Bridge. O objectivo de um
interruptor é o de ligar ou desligar um dispositivo. O interruptor
concreto pode ser implementado como um interruptor de duas
posições, de deslocamento, de brilho, etc.

Exemplo

// --------------- Bridge
// Separar a abstracção da sua implementação para que cada uma
// possa variar independentemente.

//Abstraction
class Pilha {
protected ImplementacaoPilha pi;
public Pilha(String s) {
if (s.equals("java")) {
pi = new PilhaJava();
}
else {
pi = new PilhaArray();
}
}
public Pilha() {
this("java");
}
public void push(int in) {
pi.push(new Integer(in));
}
public int pop() {
return ( (Integer) pi.pop()).intValue();
}
public boolean isEmpty() {
return pi.empty();
}
}
//RefinedAbstraction
class PilhaCrescente
extends Pilha {
private int _totalRejeitados = 0;
public PilhaCrescente() {

Anacleto Correia 40
IPS-ESTSetúbal Padrões de Desenho

super("java");
}
public PilhaCrescente(String s) {
super(s);
}
public int reportaRejeitados() {
return _totalRejeitados;
}
public void push(int in) {
if (!pi.empty() && in > ( (Integer) pi.peek()).intValue()) {
_totalRejeitados++;
}
else {
pi.push(new Integer(in));
}
}
}
// Implementor
interface ImplementacaoPilha {
Object push(Object o);
Object peek();
boolean empty();
Object pop();
}
// ConcreteImplementorA
class PilhaJava extends java.util.Stack implements
ImplementacaoPilha{}
// ConcreteImplementorB
class PilhaArray implements ImplementacaoPilha {
private Object[] _items = new Object[20];
private int _total = -1;
public Object push(Object o) { return _items[++_total] = o; }
public Object peek() { return _items[_total]; }
public Object pop() {return _items[_total--]; }
public boolean empty() { return _total == -1;}
}
// Client
class TestBridge {
public static void main(String[] args) {
Pilha[] pilhas = {
new Pilha("java"), new PilhaCrescente("java"),
new Pilha("meu"),new PilhaCrescente("meu") };
for (int i = 0, num; i < 20; i++) {
num = (int) (Math.random() * 1000) % 40;
for (int j = 0; j < pilhas.length; j++) {
pilhas[j].push(num);
}
}
for (int i = 0, num; i < pilhas.length; i++) {
while (!pilhas[i].isEmpty()) {
System.out.print(pilhas[i].pop() + " ");
}
System.out.println();
}
System.out.println("O total de elementos rejeitados é "
+ ( (PilhaCrescente) pilhas[1]).reportaRejeitados());
}
}
/*
29 28 38 8 0 31 3 15 18 5 15 34 23 3 35 38 19 32 4 21
0 3 3 4 21

Anacleto Correia 41
IPS-ESTSetúbal Padrões de Desenho

29 28 38 8 0 31 3 15 18 5 15 34 23 3 35 38 19 32 4 21
0 3 3 4 21
O total de elementos rejeitados é 15
*/

Anacleto Correia 42
IPS-ESTSetúbal Padrões de Desenho

Composite (Composto)

Compõe objectos em estruturas de árvore para representar


hierarquias parte-todo. O padrão Composite permite que os clientes
tratem objectos individuais e composições de objectos de forma
uniforme.

Consiste em agrupar componentes em conjuntos e considerar o


conjunto como se fosse também um componente com a mesma
interface, dispondo todos em árvore para tratamento uniforme. Por
exemplo, supondo que existem vários componentes C que possuem
um método c() que se pretende executar. Pode-se agrupar todos
estes componentes num único objecto Composite, que implementa a
interface do componente C, com o método c() em Composite, a
evocar o método c() de todos os componentes aí agrupados. Como
Composite implementa a mesma interface de C, pode-se agrupar
composições dentro de composições, formando uma árvore.

Participantes:

As classes e/ou objectos que participam no padrão são:

· Component
o Declara a interface para objectos na composição.
o Implementa conforme apropriado, o comportamento por
omissão da interface comum a todas as classes.
o Declara uma interface para acesso e gestão dos
componentes filhos.
o (opcional) define uma interface para acesso ao
componente pai, na estrutura recursiva, e implementa-a
caso apropriado.
· Leaf
o Representa os objectos folha na composição. Uma folha
não tem filhos.
o Define o comportamento dos objectos primitivos na
composição.
· Composite
o Define o comportamento dos componentes que têm
filhos.
o Armazena os componentes filhos.
o Implementa os métodos relacionados com os filhos, na
interface Component.
· Client
o Manipula objectos na composição através da interface
Component.

Anacleto Correia 43
IPS-ESTSetúbal Padrões de Desenho

Anacleto Correia 44
IPS-ESTSetúbal Padrões de Desenho

Exemplo do padrão no mundo real

As expressões aritméticas são Composites (compostos). Uma


expressão aritmética consiste num operando, um operador (+ - * /) e
outro operando. O operando pode ser um número, ou uma expressão
aritmética. Assim, ambas expressões 5 + 6 e (1 + 4) + (5 * 2) são
válidas.

Exemplo

//COMPOSITE
interface Componente {void escreve();}
class Folha implements Componente {
private int _valor;
public Folha(int val) { _valor = val; }
public void escreve() {
System.out.print(_valor + " ");
}
}
abstract class No implements Componente {
private Componente[] _filhos = new Componente[9];
private int _total = 0;
private int _valor;
public No(int val) { _valor = val; }
public void add(Componente c) { _filhos[_total++] = c; }
public void escreve() {
System.out.print(_valor + " ");
for (int i=0; i < _total; i++) {
_filhos[i].escreve();
}
}
}
class Linha extends No {
public Linha(int val) { super(val); }
public void escreve() {
System.out.print("Linha");
super.escreve();
}

Anacleto Correia 45
IPS-ESTSetúbal Padrões de Desenho

}
class Coluna extends No {
public Coluna(int val) { super(val); }
public void escreve() {
System.out.print("Coluna");
super.escreve();
}
}
public class CompostoDemo {
public static void main(String[] args) {
Componente primei = new Linha(1);
Componente segund = new Coluna(2);
Componente tercei = new Coluna(3);
Componente quarto = new Linha(4);
Componente quinto = new Linha(5);
primei.add(segund);
primei.add(tercei);
tercei.add(quarto);
tercei.add(quinto);
primei.add(new Folha(6));
segund.add(new Folha(7));
tercei.add(new Folha(8));
quarto.add(new Folha(9));
quinto.add(new Folha(10));
primei.escreve();
}
}
// Linha1 Coluna2 7 Coluna3 Linha4 9 Linha5 10 8 6

Anacleto Correia 46
IPS-ESTSetúbal Padrões de Desenho

Decorator (Decorador)

Adiciona responsabilidades de forma dinâmica a um objecto. Os


Decoradores fornecem uma alternativa flexível à herança para
estender funcionalidades.

O padrão Decorator permite assim, estender as funcionalidades de


uma classe em tempo de execução. A ideia é que uma classe possui
um determinado método que retorna um resultado, mas podemos-lhe
acoplar várias outras classes, que irão realizar certo processamento
antes ou depois da operação, retornando o valor apenas no final.

Dois exemplos: filtros do API Servlet e interceptores do Xwork. No


segundo caso, quando se acede uma acção do Xwork pode-se
configurar uma série de interceptores que irão interceptar a acção
antes e depois da sua execução. Os interceptores estendem a
funcionalidade da acção e, através de configuração XML, podemos
dizer que interceptores executar ou não, sem necessidade de
recompilar o código.

Participantes:

As classes e/ou objectos que participam no padrão são:

· Component
o Define a interface dos objectos que podem ter
responsabilidades adicionadas de forma dinâmica.
· ConcreteComponent
o Define um objecto ao qual mais responsabilidades podem
ser adicionadas.
· Decorator
o Mantém a referência para um objecto Component e
define a interface que está conforme a interface
Component.
· ConcreteDecorator
o Adiciona responsabilidades ao componente.

Anacleto Correia 47
IPS-ESTSetúbal Padrões de Desenho

Anacleto Correia 48
IPS-ESTSetúbal Padrões de Desenho

Exemplo do padrão no mundo real

Embora as telas se possam pendurar na parede com ou sem moldura,


esta são frequentemente utilizada, e é a moldura do quadro que
realmente é pendurada à parede. Antes de serem penduradas, as
telas podem ser encaixilhadas e emolduradas. A tela, o caixilho e a
moldura dão forma a um único componente visual.

Exemplo

// Padrão Decorator

// Componente
interface Componente { void desenha(); }
// ConcreteComponent
class Texto implements Componente {
private int comprimento, altura;
public Texto(int c, int a) {
comprimento = c; altura = a;
}
public void desenha() {
System.out.println("Texto: " + comprimento +
", " + altura);
}
}
// Decorator
abstract class Decorador implements Componente {
private Componente obj;
public Decorador(Objecto o) { obj = o; }
public void desenha() { obj.desenha(); }
}
// ConcreteDecoratorA
class Borda extends Decorador {
public Borda(Componente o) { super(o); }
public void desenha() {

Anacleto Correia 49
IPS-ESTSetúbal Padrões de Desenho

super.desenha();
System.out.println(" DecoraçãoBorda");
}
}
// ConcreteDecoratorB
class Cerca extends Decorador {
public Cerca(Componente o) { super(o); }
public void desenha() {
super.desenha();
System.out.println(" DecoraçãoCerca");
}
}
// Client
public class DecoratorPattern {
public static void main(String[] args) {
Componente obj = new Borda(
new Borda(
new Cerca(
new Texto(80,24))));
obj.desenha();
}
}
// Texto: 80, 24
// DecoraçãoCerca
// DecoraçãoBorda
// DecoraçãoBorda

Anacleto Correia 50
IPS-ESTSetúbal Padrões de Desenho

Façade (Fachada)

Fornece uma interface unificada de alto-nível, para acesso a um


conjunto de funcionalidades de um subsistema. O padrão Façade ao
definir a interface de alto nível torna o subsistema mais fácil de usar.

Façade é uma classe que agrupa na sua interface acessos a métodos


que são executados por diversas outras classes, servindo de fachada
a esses métodos, de forma que, as demais classes não necessitem de
ser conhecidas pelos objectos clientes. Uma versão especial de
Façade na qual todos os métodos são estáticos é designada por
Utility. É frequente utilizar este padrão em classes de aplicações.
Dependendo do caso de utilização que for ser implementado, cria-se
uma classe para cada caso de utilização, uma para o pacote (que é
fachada das classes que implementam os casos) e uma para toda a
aplicação, que fornece acesso às classes dos pacotes. Esta última é
armazenada em sessão para que o utilizador tenha acesso, através
dela, a todas as funcionalidades do sistema.

Participantes:

As classes e/ou objectos que participam no padrão são:

· Facade
o Sabe que classes do subsistema são responsáveis pelo
pedido.
o Delega os pedidos do cliente nos objectos apropriados do
subsistema.
· classes do Subsistema
o Implementam as funcionalidades do subsistema.
o Levam a cabo o trabalho atribuído ao objecto Facade.
o Não têm conhecimento do objecto Facade e não guardam
dele qualquer referência.

Anacleto Correia 51
IPS-ESTSetúbal Padrões de Desenho

Exemplo do padrão no mundo real

Os clientes encontram uma fachada (front-office) ao encomendar a


partir de um catálogo. O cliente telefona para um número e fala com
um funcionário de atendimento ao público. O funcionário age como
uma fachada, fornecendo uma interface dos serviços de recepção de
encomendas, de cobrança e expedição de encomendas.

Exemplo

// aplicação Cliente
public class patFacade {
public static void main(String[] args) {
// Facade
PedidoEmprestimo pedidoEmprestimo = new PedidoEmprestimo();
// Avalia se o cliente é eligível para obtenção de empréstimo
Cliente cliente = new Cliente("Ana Silva");
boolean eligivel = pedidoEmprestimo.ehEligivel(cliente, 125000);

System.out.println("\nEmpréstimo de " + cliente.getName() +


" foi " + (eligivel ? "Aprovado" :
"Rejeitado"));
}
}
// "Subsistema ClassA"
class Banco {
public boolean temPoupancasSuficientes(Cliente c, int amount) {
System.out.println("Verificar poupanças de " + c.getName());
return true;
}
}
// "Subsistema ClassB"
class Credito {
public boolean temCredito(Cliente c) {
System.out.println("Verificar credito de " + c.getName());
return true;
}
}
// "Subsistema ClassC"
class Emprestimo {
public boolean naoTemEmprestimos(Cliente c) {

Anacleto Correia 52
IPS-ESTSetúbal Padrões de Desenho

System.out.println("Verificar empréstimos de " + c.getName());


return true;
}
}
class Cliente {
private String name;
// Construtor
public Cliente(String name) {
this.name = name;
}

// propriedade
public String getName() {
return name;
}
}
// "Facade"
class PedidoEmprestimo {
private Banco banco = new Banco();
private Emprestimo emprestimo = new Emprestimo();
private Credito credito = new Credito();

public boolean ehEligivel(Cliente cust, int amount) {


System.out.println("\n" + cust.getName() + " pretende " + amount +
" de emprestimo\n");
boolean eligivel = true;
// Verifica posição financeira do cliente
if (!banco.temPoupancasSuficientes(cust, amount)) {
eligivel = false;
}
else if (!emprestimo.naoTemEmprestimos(cust)) {
eligivel = false;
}
else if (!credito.temCredito(cust)) {
eligivel = false;
}
return eligivel;
}
}
/*
Ana Silva pretende 125000 de emprestimo
Verificar poupanças de Ana Silva
Verificar empréstimos de Ana Silva
Verificar credito de Ana Silva
Empréstimo de Ana Silva foi Aprovado
*/

Anacleto Correia 53
IPS-ESTSetúbal Padrões de Desenho

Flyweight (Peso-Leve)

Utiliza a partilha para dar suporte eficiente à utilização de um grande


número de objectos de pequena granularidade.

Flyweight é útil no caso de se ter um pequeno conjunto de objectos


imutáveis que são utilizados muitas vezes em diferentes locais do
sistema. Em vez de permitir que inúmeras instâncias destes objectos
sejam construídas indiscriminadamente, forma-se uma pool de
objectos que serão utilizados nas diversas situações em que são
necessários. É importante, no entanto, garantir que os objectos são
realmente imutáveis, ou o padrão não poderá ser aplicado.

Participantes:

As classes e/ou objectos que participam no padrão são:

· Flyweight
o Declara uma interface através da qual os flyweights
podem receber e actuar em estado extrínseco.
· ConcreteFlyweight
o Implementa a interface Flyweight e adiciona
armazenamento para o estado intrínseco, caso haja. Um
objecto ConcreteFlyweight deve ser partilhado. Qualquer
estado que armazene deve ser intrínseco, isto é, deve ser
independente do contexto do objecto ConcreteFlyweight.
· UnsharedConcreteFlyweight
o Nem todas as subclasses Flyweight têm que ser
partilhadas. A interface Flyweight permite a partilha, mas
não a obriga. É comum que os objectos
UnsharedConcreteFlyweight tenham objectos
ConcreteFlyweight como filhos num determinado nível da
estrutura do objecto flyweight.
· FlyweightFactory
o Cria e gere objectos flyweight
o Assegura que os flyweights são apropriadamente
partilhados. Quando um cliente solicita um flyweight, os
objectos FlyweightFactory fornecem uma instância
existente ou criam uma, caso não exista.
· Client
o Mantém referência(s) para flyweight(s).
o Calcula ou armazena o estado extrínseco do(s)
flyweight(s).

Anacleto Correia 54
IPS-ESTSetúbal Padrões de Desenho

Anacleto Correia 55
IPS-ESTSetúbal Padrões de Desenho

Exemplo do padrão no mundo real

A rede de telefone pública comutada é um exemplo de um flyweight.


Há diversos recursos tais como geradores de tom, geradores de
toque e receptores de dígitos que são partilhados por todos os
subscritores. Um subscritor não tem ideia de quantos recursos estão
na pool quando levanta o auscultador para fazer uma chamada. O
que lhe interessa apenas é obter o tom, se os dígitos seleccionados
são recebidos e a chamada é efectuada.

Exemplo

// Padrão Flyweight
import java.util.HashMap;

// aplicação Cliente
public class patFlyweigh {
public static void main(String[] args) {
// Construir um documento com texto
String documento = "AAZZBBZB";
char[] chars = documento.toCharArray();
CaracterFabrica f = new CaracterFabrica();
// estado extrínseco
int apontadorTamanho = 10;
// Para cada carácter usar um objecto flyweight
for (int i = 0; i < chars.length; i++) {
apontadorTamanho++;
Caracter caracter = f.getCaracter(chars[i]);
caracter.mostrar(apontadorTamanho);
}
}
}

// "FlyweightFactory"
class CaracterFabrica {
private HashMap characters = new HashMap();
public Caracter getCaracter(char key) {
// Uses "lazy initialization"
Caracter caracter = (Caracter) characters.get(new Character(key));
if (caracter == null) {
switch (key) {
case 'A':
caracter = new CaracterA();

Anacleto Correia 56
IPS-ESTSetúbal Padrões de Desenho

break;
case 'B':
caracter = new CaracterB();
break;
//...
case 'Z':
caracter = new CaracterZ();
break;
}
characters.put(new Character(key), caracter);
}
return caracter;
}
}

// "Flyweight"
abstract class Caracter {
protected char simbolo;
protected int comprimento;
protected int altura;
protected int ascendente;
protected int descendente;
protected int apontadorTamanho;

public abstract void mostrar(int apontadorTamanho);


}

// "ConcreteFlyweight"
class CaracterA
extends Caracter {
// Constructor
public CaracterA() {
this.simbolo = 'A';
this.altura = 100;
this.comprimento = 120;
this.ascendente = 70;
this.descendente = 0;
}

public void mostrar(int apontadorTamanho) {


this.apontadorTamanho = apontadorTamanho;
System.out.println(this.simbolo +
" (Tamanho " + this.apontadorTamanho + ")");
}
}

// "ConcreteFlyweight"
class CaracterB
extends Caracter {
// Constructor
public CaracterB() {
this.simbolo = 'B';
this.altura = 100;
this.comprimento = 140;
this.ascendente = 72;
this.descendente = 0;
}

public void mostrar(int apontadorTamanho) {


this.apontadorTamanho = apontadorTamanho;
System.out.println(this.simbolo +

Anacleto Correia 57
IPS-ESTSetúbal Padrões de Desenho

" (Tamanho " + this.apontadorTamanho + ")");


}
}

// ... C, D, E, etc.
// "ConcreteFlyweight"
class CaracterZ
extends Caracter {
// Construtor
public CaracterZ() {
this.simbolo = 'Z';
this.altura = 100;
this.comprimento = 100;
this.ascendente = 68;
this.descendente = 0;
}

public void mostrar(int apontadorTamanho) {


this.apontadorTamanho = apontadorTamanho;
System.out.println(this.simbolo +
" (Tamanho " + this.apontadorTamanho + ")");
}
}
/*
A (Tamanho 11)
A (Tamanho 12)
Z (Tamanho 13)
Z (Tamanho 14)
B (Tamanho 15)
B (Tamanho 16)
Z (Tamanho 17)
B (Tamanho 18)
*/

Anacleto Correia 58
IPS-ESTSetúbal Padrões de Desenho

Proxy (Procurador)

Fornece um objecto procurador ou representante de outro objecto,


para controlar o acesso a este mesmo objecto.

A Proxy é um intermediário entre um cliente C e um objecto O, que


implementa a mesma interface de O e adiciona outros controles e/ou
optimizações necessárias. O proxy deve representar o objecto da
forma o mais transparente possível. De preferência de modo a que o
cliente não saiba que está interagir com um representante em vez do
objecto original.

Um exemplo são as proxies utilizados para persistência. Supondo que


se tem um objecto que possui para além da propriedade “nome”,
outras propriedades que têm de se obter de uma base de dados
(campos blob, memo, text, etc.). Supondo, também, que se pretende
obter 10.000 instâncias para apresentar uma tabela ao utilizador.
Não é necessário obter logo as propriedades complexas, bastando
criar uma proxy que obtenha apenas o nome e evoque o objecto real
(obtendo as propriedades mais complexas) apenas nas situações em
que são realmente necessárias.

Participantes:

As classes e/ou objectos que participam no padrão são:

· Proxy
o Mantém a referência que permite à proxy aceder ao
objecto real. A Proxy pode-se referir ao Subject se as
interfaces de RealSubject e Subject forem as mesmas.
o Fornece uma interface idêntica a Subject para que a
proxy possa ser substituída pelo objecto real.
o Controla o acesso ao objecto real podendo ser
responsável pela sua criação e remoção.
o Outras responsabilidades dependem do tipo de proxy:
§ Proxies remotas são responsáveis por codificar o
pedido e os seus argumentos, e enviá-lo codificado
ao objecto real, num diferente espaço de endereço.
§ Proxies virtuais podem fazer cache de informação
adicional acerca do objecto real, para que se possa
adiar o acesso ao mesmo.
§ Proxies de protecção verificam se quem a evoca
tem privilégios de acesso necessários para efectuar
o pedido.
· Subject

Anacleto Correia 59
IPS-ESTSetúbal Padrões de Desenho

o Define a interface comum a RealSubject e Proxy de forma


que a Proxy possa ser usada em qualquer local onde
RealSubject seja esperado.
· RealSubject
o Define o objecto real que a proxy representa.

Anacleto Correia 60
IPS-ESTSetúbal Padrões de Desenho

Exemplo do padrão no mundo real

Uma letra ou um cheque de uma pessoa é uma proxy de um depósito


à ordem num banco do qual seja cliente. Um cheque pode ser
utilizado em vez de dinheiro para efectuar compras e em última
instância aceder ao depósito à ordem na conta do cliente.

Exemplo

// Padrão Proxy
// aplicação Cliente
public class PatProxy {
public static void main(String[] args) {
// Cria proxy math
IMath p = new MathProxy();

// Fazer os cálculos
System.out.println("4 + 2 = " + p.adicionar(4, 2));
System.out.println("4 - 2 = " + p.subtrair(4, 2));
System.out.println("4 * 2 = " + p.multiplicar(4, 2));
System.out.println("4 / 2 = " + p.dividir(4, 2));

}
}

// "Subject"
interface IMath {
double adicionar(double x, double y);
double subtrair(double x, double y);
double multiplicar(double x, double y);
double dividir(double x, double y);
}

// "RealSubject"
class Math
implements IMath {
public double adicionar(double x, double y) {
return x + y;
}

public double subtrair(double x, double y) {

Anacleto Correia 61
IPS-ESTSetúbal Padrões de Desenho

return x - y;
}

public double multiplicar(double x, double y) {


return x * y;
}

public double dividir(double x, double y) {


return x / y;
}
}

// "Objecto Proxy "


class MathProxy
implements IMath {
Math math;
public MathProxy() {
math = new Math();
}

public double adicionar(double x, double y) {


return (x+y);
}

public double subtrair(double x, double y) {


return (x-y);
}

public double multiplicar(double x, double y) {


// implementada à custa de RealSubject
return math.multiplicar(x, y);
}

public double dividir(double x, double y) {


// implementada à custa de RealSubject
return math.dividir(x, y);
}
}
/*
4 + 2 = 6.0
4 - 2 = 2.0
4 * 2 = 8.0
4 / 2 = 2.0
*/

Anacleto Correia 62
IPS-ESTSetúbal Padrões de Desenho

Padrões de Comportamento

Padrões que caracterizam formas de interacção entre classes e


objectos.

Chain of Responsibility (Cadeia de Responsabilidade)

Evita acoplar o objecto que envia um pedido ao receptor do mesmo,


dando oportunidade a vários objectos de tratarem esse pedido. Os
objectos receptores são encadeados e o pedido é passado na cadeia
até que um objecto o trate.

É montada uma cadeia de objectos que podem servir um


determinado pedido. Em vez de acoplar o cliente a um objecto
específico para a execução de um determinado método, o pedido é
enviado à cadeia. O pedido vai passando pelos objectos até encontrar
o mais adequado para satisfazê-lo, ou então, cada objecto realiza
uma parte do serviço e passa o pedido ao objecto seguinte na cadeia.
Um exemplo dessa última versão são os interceptores do Xwork: ao
executar uma acção, o pedido passa antes pelos interceptores, que
realizam um tipo de serviço antes da acção executar.

Participantes:

As classes e/ou objectos que participam no padrão são:

· Handler
o Define uma interface para tratar os pedidos
o (opcional) implementa a ligação ao sucessor
· ConcreteHandler
o Trata os pedidos pelos quais é responsável
o Pode aceder ao seu sucessor
o Se o ConcreteHandler pode tratar o pedido, trata-o; caso
contrário envia-o ao seu sucessor
· Client
o Inicia o pedido a um objecto ConcreteHandler na cadeia

Anacleto Correia 63
IPS-ESTSetúbal Padrões de Desenho

Exemplo do padrão no mundo real

As máquinas de separação de moedas utilizadas pelos bancos são um


exemplo de Cadeia de Responsabilidades. Em vez de possuírem
ranhuras diferentes por cada tipo de moeda, existe apenas uma.
Quando a moeda cai, é encaminhada para um receptáculo adequado
à dimensão da moeda, através de mecanismos internos apropriados.

Exemplo

// Padrão Chain of Responsibility


// aplicação Cliente
public class PatChResp {
public static void main(String[] args) {
// Definir a Cadeia de Responsabilidades
Aprovador pedro = new Director("Pedro");
Aprovador afonso = new VicePresidente("Afonso");
Aprovador maria = new Presidente("Maria");
pedro.setSucessor(afonso);
afonso.setSucessor(maria);

// Gera e processa os pedidos de compra


Compra p = new Compra(2034, 350.00, "Para Stock");
pedro.processaPedido(p);
p = new Compra(2035, 32590.10, "Projecto X");
pedro.processaPedido(p);
p = new Compra(2036, 122100.00, "Projecto Y");
pedro.processaPedido(p);
}
}

Anacleto Correia 64
IPS-ESTSetúbal Padrões de Desenho

// "Handler"
abstract class Aprovador {
private String nome;
protected Aprovador sucessor;

public Aprovador(String nome) {


this.nome = nome;
}

public void setSucessor(Aprovador sucessor) {


this.sucessor = sucessor;
}

public String getNome() {


return nome;
}

public abstract void processaPedido(Compra compra);


}

// "ConcreteHandler"
class Director
extends Aprovador {
public Director(String nome) {
super(nome);
}

public void processaPedido(Compra compra) {


if (compra.getValor() < 10000.0) {
System.out.println(this.getClass().getName() + " " + this.getNome()
+ " aprovou pedido nº" +
compra.getNumero());
}
else if (sucessor != null) {
sucessor.processaPedido(compra);
}
}
}

// "ConcreteHandler"
class VicePresidente
extends Aprovador {
public VicePresidente(String nome) {
super(nome);
}

public void processaPedido(Compra compra) {


if (compra.getValor() < 25000.0) {
System.out.println(this.getClass().getName() + " " + this.getNome() +
" aprovou pedido nº" +
compra.getNumero());
}
else if (sucessor != null) {
sucessor.processaPedido(compra);
}
}
}

// "ConcreteHandler"
class Presidente
extends Aprovador {
public Presidente(String nome) {
super(nome);
}

public void processaPedido(Compra compra) {


if (compra.getValor() < 100000.0) {
System.out.println(this.getClass().getName() + " " + this.getNome() +
" aprovou pedido nº" +
compra.getNumero());
}
else {
System.out.println(
"Pedido nº" + compra.getNumero() +
" requer uma reunião com o Presidente!");
}
}

Anacleto Correia 65
IPS-ESTSetúbal Padrões de Desenho

// Detalhe do Pedido
class Compra {
private int numero;
private double valor;
private String finalidade;

// Construtor
public Compra(int numero, double valor, String finalidade) {
this.numero = numero;
this.valor = valor;
this.finalidade = finalidade;
}

// Propriedades
public double getValor() {
return valor;
}

public void setValor(double valor) {


this.valor = valor;
}

public String getFinalidade() {


return finalidade;
}

public void setFinalidade(String finalidade) {


this.finalidade = finalidade;
}

int getNumero() {
return numero;
}

public void setNumero(int numero) {


this.numero = numero;
}

}
/*
Director Pedro aprovou pedido nº2034
Presidente Maria aprovou pedido nº2035
Pedido nº2036 requer uma reunião com o Presidente!
*/

Anacleto Correia 66
IPS-ESTSetúbal Padrões de Desenho

Command (Comando)

O padrão Command encapsula um pedido num objecto, permitindo:


parametrizar clientes com pedidos diferentes, enfileirar pedidos, fazer
log de pedidos e dar suporte a operações de undo.

Por outras palavras, quando se pretender realizar uma acção X, em


vez de evocar o método executarX() de uma classe enviando-lhe
parâmetros, cria-se o objecto AccaoX e configura-se as suas
propriedades com os valores dos parâmetros daquele comando.

Este padrão é a base do Xwork. No Xwork configura-se as acções


(comandos) às quais o sistema irá responder e a classe que
implementa o comando. Quando se acede a determinada acção, é
automaticamente: criada uma nova instância da classe da acção;
atribuídos os parâmetros que foram enviados; executada a acção; e
retornado um valor como resultado.

Participantes:

As classes e/ou objectos que participam no padrão são:

· Command
o Declara uma interface para execução de uma operação
· ConcreteCommand
o Define uma ligação entre o objecto Receiver e uma acção
o Implementa o método Execute, evocando as
correspondentes operações no Receiver
· Client
o Cria um objecto ConcreteCommand e estabelece o seu
receptor
· Invoker
o Pede ao comando para tomar conta do pedido
· Receiver
o Sabe como executar as operações associadas à realização
do pedido.

Anacleto Correia 67
IPS-ESTSetúbal Padrões de Desenho

Exemplo do padrão no mundo real

O “bilhete de pedido” de uma refeição é um exemplo do padrão


Command. O empregado de mesa recebe o pedido de um cliente e
encapsula-o registando no bilhete. O pedido é então colocado na fila
das refeições a preparar. De notar que o bloco de notas, utilizado em
diferentes refeições, não depende do menu, por isso, pode suportar
pedidos de diferentes refeições.

Exemplo

// Padrão Command: transforma um pedido num objecto


import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;

// Command
interface Command {
void executar();
}

// Receiver
// Sabe como executar as operações associadas a um pedido.
// Qualquer classe pode actuar como Receiver.

Anacleto Correia 68
IPS-ESTSetúbal Padrões de Desenho

class ReceptorOla {
public void action() {
System.out.print("\nSou o Receiver de Ola!");
}
}
// Receiver
class ReceptorMundo {
public void faz() {
System.out.print("\nSou o Receiver de Mundo!");
}
}
// Receiver
class ReceptorEuSou {
public void diz() {
System.out.print("\nSou o Receiver de EuSou!");
}
}

// ConcreteCommand
//Define a ligação entre uma acção e o objecto receptor
//Invoca as operações correspondentes no receptor
class Ola
implements Command {
ReceptorOla r;
public Ola(ReceptorOla r) {
this.r = r;
}

public void executar() {


r.action();
}
}
// ConcreteCommand
class Mundo
implements Command {
ReceptorMundo r;
public Mundo(ReceptorMundo r) {
this.r = r;
}

public void executar() {


r.faz();
}
}
// ConcreteCommand
class EuSou
implements Command {
ReceptorEuSou r;
public EuSou(ReceptorEuSou r) {
this.r = r;
}

public void executar() {


r.diz();
}
}

// Invoker
// Pede a Command para tratar o seu pedido
class Macro {
private List pedidos = new ArrayList();

Anacleto Correia 69
IPS-ESTSetúbal Padrões de Desenho

public void add(Command c) {


pedidos.add(c);
}

public void correr() {


Iterator it = pedidos.iterator();
while (it.hasNext())
( (Command) it.next()).executar();
}
}

// Client
// Cria o ConcreteCommand e define o seu receptor
public class CommandPattern {
private static Macro macro = new Macro();
public static void teste() {
macro.add(new Ola(new ReceptorOla()));
macro.add(new Mundo(new ReceptorMundo()));
macro.add(new EuSou(new ReceptorEuSou()));
macro.correr();
}

public static void main(String args[]) {


teste();
}
}
/*

Sou o Receiver de Ola!


Sou o Receiver de Mundo!
Sou o Receiver de EuSou!
*/

Anacleto Correia 70
IPS-ESTSetúbal Padrões de Desenho

Interpreter (Interpretador)

Dada uma linguagem, define uma representação da sua gramática e


um interpretador que utiliza a representação da gramática para
interpretar frases dessa linguagem.

Interpreter consiste pois em criar uma linguagem de representação


de operações, construir um interpretador para essa linguagem e
permitir que sejam enviados comandos escritos na linguagem
especificada, que serão transformados em operações. Apesar de ser
uma solução mais complexa, pois envolve a criação de um
interpretador de comandos, dá maior flexibilidade ao utilizador, que
pode alterar as funcionalidades do sistema através da linguagem de
comandos, sem precisar de recompilar as classes.

Participantes:

As classes e/ou objectos que participam no padrão são:

· AbstractExpression
o Declara uma interface para execução de um método.
· TerminalExpression
o Implementa o método Interpret associado com símbolos
terminais da gramática.
o É necessária uma instância para cada símbolo terminal na
frase.
· NonterminalExpression
o É necessária uma destas classes para cada regra R ::=
R1R2...Rn da gramática.
o Mantém variáveis de instância do tipo AbstractExpression
para cada um dos símbolos R1 a Rn.
o Implementa o método Interpret para símbolos não
terminais na gramática. Interpret tipicamente evoca-se a
si próprio recursivamente nas variáveis representando R1
a Rn.
· Context
o Contém informação que é global ao Interpreter.
· Client
o Constrói (ou é dada) uma árvore de sintaxe abstracta
representando uma frase particular na linguagem que a
gramática define. A árvore de sintaxe abstracta é
montada de instâncias das classes NonterminalExpression
e TerminalExpression.
o Evoca o método Interpret.

Anacleto Correia 71
IPS-ESTSetúbal Padrões de Desenho

Exemplo do padrão no mundo real

Os músicos são exemplos de intérpretes. O compasso e duração de


uma nota pode ser representada em notação musical. Essa notação é
a linguagem da música. Os músicos que tocam música a partir de
uma pauta, podem reproduzir o compasso e a duração originais, das
notas representadas.

Anacleto Correia 72
IPS-ESTSetúbal Padrões de Desenho

Exemplo

// Padrão Interpret

import java.util.ArrayList;
// aplicação Cliente
public class PatInterp {
public static void main(String[] args) {
String romano = "MCMXXVIII";
Contexto contexto = new Contexto(romano);
// Construir a 'parse tree'
ArrayList arvore = new ArrayList();
arvore.add(new ExpressaoMilhares());
arvore.add(new ExpressaoCentenas());
arvore.add(new ExpressaoDezenas());
arvore.add(new ExpressaoUnidades());
// Interpret
for (int i=0;i<arvore.size();i++ )
{
Expressao exp= (Expressao) arvore.get(i);
exp.interpretar(contexto);
}
System.out.println(romano + " = " + contexto.getOutput());
}
}
// "Context"
class Contexto
{
private String input;
private int output;
// Construtor
public Contexto(String input)
{
this.input = input;
}
// Propriedades
public String getInput() {
return input;
}
public void setInput(String value) {
input = value;
}
public int getOutput() {
return output;
}

public void setOutput(int value) {


output = value;
}
}

// "AbstractExpression"
abstract class Expressao
{
public void interpretar(Contexto contexto)
{
if (contexto.getInput().length() == 0)
return;

if (contexto.getInput().startsWith(nove()))
{
contexto.setOutput( contexto.getOutput()+ (9 * multiplicador()));
contexto.setInput( contexto.getInput().substring(2));
}
else if (contexto.getInput().startsWith(quatro()))
{
contexto.setOutput(contexto.getOutput()+ (4 * multiplicador()));
contexto.setInput( contexto.getInput().substring(2));
}
else if (contexto.getInput().startsWith(cinco()))
{
contexto.setOutput(contexto.getOutput()+ (5 * multiplicador()));
contexto.setInput( contexto.getInput().substring(1));
}

while (contexto.getInput().startsWith(um()))

Anacleto Correia 73
IPS-ESTSetúbal Padrões de Desenho

{
contexto.setOutput(contexto.getOutput()+ (1 * multiplicador()));
contexto.setInput( contexto.getInput().substring(1));
}
}
public abstract String um();
public abstract String quatro();
public abstract String cinco();
public abstract String nove();
public abstract int multiplicador();
}
// Verificação dos Milhares para o numero romano M
// "TerminalExpression"
class ExpressaoMilhares extends Expressao
{
public String um() { return "M"; }
public String quatro(){ return " "; }
public String cinco(){ return " "; }
public String nove(){ return " "; }
public int multiplicador() { return 1000; }
}
// Verificação das centenas C, CD, D or CM
// "TerminalExpression"
class ExpressaoCentenas extends Expressao
{
public String um() { return "C"; }
public String quatro(){ return "CD"; }
public String cinco(){ return "D"; }
public String nove(){ return "CM"; }
public int multiplicador() { return 100; }
}
// Verificação das dezenas para X, XL, L and XC
// "TerminalExpression"
class ExpressaoDezenas extends Expressao
{
public String um() { return "X"; }
public String quatro(){ return "XL"; }
public String cinco(){ return "L"; }
public String nove(){ return "XC"; }
public int multiplicador() { return 10; }
}
// Verificação das unidades para I, II, III, IV, V, VI, VI, VII, VIII, IX
// "TerminalExpression"
class ExpressaoUnidades extends Expressao
{
public String um() { return "I"; }
public String quatro(){ return "IV"; }
public String cinco(){ return "V"; }
public String nove(){ return "IX"; }
public int multiplicador() { return 1; }
}
/*
MCMXXVIII = 1928
*/

Anacleto Correia 74
IPS-ESTSetúbal Padrões de Desenho

Iterator (Iterador)

Fornece um meio de aceder aos elementos de uma colecção de


objectos, de forma sequencial e sem expor sua representação
interna. A interface java.util.Iterator é um exemplo do padrão
Iterator.

Participantes:

As classes e/ou objectos que participam no padrão são:

· Iterator
o Define uma interface para acesso sequencial aos
elementos de uma estrutura ou agregado.
· ConcreteIterator
o Implementa a interface Iterator.
o Mantém registo da posição corrente durante a passagem
pelo agregado de elementos.
· Aggregate
o Define uma interface para criação do objecto Iterator.
· ConcreteAggregate
o Implementa a criação da interface Iterator para retornar
uma instância de ConcreteIterator.

Exemplo do padrão no mundo real

Nos televisores os botões do comando remoto, “anterior” e


“seguinte”, são utilizados para percorrer os canais. Quando o

Anacleto Correia 75
IPS-ESTSetúbal Padrões de Desenho

espectador navega através desses botões pelos canais, o número do


canal não é importante, apenas o programa visualizado. Se o
programa visualizado na altura, não tiver interesse, o espectador
pode passar ao canal seguinte, através do botão “seguinte”, sem
saber o seu número para o qual vai.

Exemplo

// Padrão Iterator
// Iterator
interface Iterator {
boolean hasNext();

Object next();
}

// Agregate
interface Pilha {
//Métodos selectores
public int tamanho();

public boolean estaVazia();

public Object topo();

//Métodos de actualização
public void empilha(Object elemento);

public Object desempilha();


}

// ConcreteAggregate
class PilhaArray
implements Pilha {
// ConcreteIterator
private class PilhaArrayIterator
implements Iterator {
int pos = -1;
public boolean hasNext() {
return (pos < topo);
}

Anacleto Correia 76
IPS-ESTSetúbal Padrões de Desenho

public Object next() {


return p[++pos];
}
}

public static final int CAPACIDADE = 1000;


private int capacidade;
private Object p[];
private int topo = -1;
public PilhaArray() {
this(CAPACIDADE);
}

public PilhaArray(int cap) {


capacidade = cap;
p = new Object[capacidade];
}

public int tamanho() {


return (topo + 1);
}

public boolean estaVazia() {


return (topo < 0);
}

public void empilha(Object obj) {


if (tamanho() != capacidade)
p[++topo] = obj;
}

public Object topo() {


if (!estaVazia())
return p[topo];
return null;
}

public Object desempilha() {


if (!estaVazia()) {
Object elem = p[topo];
p[topo--] = null;
return elem;
}
return null;
}

public Iterator getIterator() {


return new PilhaArrayIterator();
}
}

//Cliente
public class PatIterator {
public static void main(String args[]) {
Iterator ai, ai2;
PilhaArray s;
s = new PilhaArray();
for (int i = 0; i < 5; i++)
s.empilha(new Integer(i));
ai = s.getIterator();

Anacleto Correia 77
IPS-ESTSetúbal Padrões de Desenho

while (ai.hasNext()) {
System.out.println("\n1 Iterador: " + ai.next());
System.out.print(" 2 Iterador:");
for (ai2 = s.getIterator(); ai2.hasNext(); )
System.out.print(" " + ai2.next());
}
}
}
/*
1 Iterador: 0
2 Iterador: 0 1 2 3 4
1 Iterador: 1
2 Iterador: 0 1 2 3 4
1 Iterador: 2
2 Iterador: 0 1 2 3 4
1 Iterador: 3
2 Iterador: 0 1 2 3 4
1 Iterador: 4
2 Iterador: 0 1 2 3 4
*/

Anacleto Correia 78
IPS-ESTSetúbal Padrões de Desenho

Mediator (Mediador)

Define um objecto que encapsula a forma como um conjunto de


objectos interage. O padrão Mediator promove uma relação fraca
entre objectos evitando que se referenciem explicitamente uns aos
outros, permitindo assim, que suas interacções variem
independentemente.

Quando diversos objectos precisam de interagir entre si, cria-se


geralmente uma associação entre eles, o que nem sempre é
desejado. Para diminuir o acoplamento desta associação, pode-se
criar uma classe mediadora, que encapsula a comunicação entre os
objectos. O mediador conhece todos mas estes apenas conhecem o
mediador.

Participantes:

As classes e/ou objectos que participam no padrão são:

· Mediator
o Define uma interface para comunicação com objectos
Colleague
· ConcreteMediator
o Implementa comportamento cooperativo coordenando
objectos Colleague
o Conhece e mantém os objectos ConcreteColleague
· ConcreteColleague
o Cada classe ConcreteColleague conhece o seu objecto
Mediator
o Cada colega comunica com o mediator, quando de outra
forma comunicaria com outro objecto ConcreteColleague

Anacleto Correia 79
IPS-ESTSetúbal Padrões de Desenho

Exemplo do padrão no mundo real

A torre de controlo de um aeroporto demonstra este padrão. Os


pilotos dos aviões que se aproximam ou partem, comunicam com a
torre, em vez de comunicarem entre si. As restrições de quem pode
aterrar ou levantar voo são definidas pela torre. É importante notar,
que a torre não controla todo o voo de um avião. A sua função é
apenas estabelecer as restrições relativas ao acesso e largada do
aeroporto.

Exemplo

// Padrão Mediator
import java.util.HashMap;

// Aplicação Cliente
public class PatMediator {
public static void main(String[] args) {
// Criação de sala de conversação
SalaConversacao salaConversacao = new SalaConversacao();

// Criação e registo de participantes


Participante George = new Beatle("George");
Participante Paul = new Beatle("Paul");
Participante Ringo = new Beatle("Ringo");
Participante John = new Beatle("John");
Participante Yoko = new NaoBeatle("Yoko");

salaConversacao.registar(George);
salaConversacao.registar(Paul);
salaConversacao.registar(Ringo);
salaConversacao.registar(John);
salaConversacao.registar(Yoko);

// participantes na conversa
Yoko.enviar("John", "Hi John!");
Paul.enviar("Ringo", "All you need is love");
Ringo.enviar("George", "My sweet Lord");
Paul.enviar("John", "Can't buy me love");
John.enviar("Yoko", "My sweet love");
}
}

// "Mediator"
abstract class AbstractChatroom {
public abstract void registar(Participante participante);
public abstract void enviar(
String de, String para, String mensagem);
}

// "ConcreteMediator"
class SalaConversacao
extends AbstractChatroom {
private HashMap participantes = new HashMap();

Anacleto Correia 80
IPS-ESTSetúbal Padrões de Desenho

public void registar(Participante participante) {


if (participantes.get(new Integer(participante.getNome().hashCode())) == null) {
participantes.put(new Integer(participante.getNome().hashCode()),
participante);
}

participante.setSala(this);
}

public void enviar(


String de, String para, String mensagem) {
Participante pto = (Participante) participantes.get(
new Integer(para.hashCode()));
if (pto != null) {
pto.receber(de, mensagem);
}
}
}

// "AbstractColleague"
class Participante {
private SalaConversacao salaConversacao;
private String nome;

// Construtor
public Participante(String nome) {
this.nome = nome;
}

// Propriedades
public String getNome() {
return nome;
}

public SalaConversacao getSala() {


return salaConversacao;
}

public void setSala(SalaConversacao salaConversacao) {


this.salaConversacao = salaConversacao;
}

public void enviar(String para, String mensagem) {


salaConversacao.enviar(nome, para, mensagem);
}

public void receber(


String de, String mensagem) {
System.out.println(de + " a " + getNome() + ": '" + mensagem + "'");
}
}

//" ConcreteColleague1"
class Beatle
extends Participante {
// Construtor
public Beatle(String nome) {
super(nome);
}

public void receber(String de, String mensagem) {


System.out.print("Para um Beatle: ");
super.receber(de, mensagem);
}
}

//" ConcreteColleague2"
class NaoBeatle
extends Participante {
// Construtor
public NaoBeatle(String nome) {
super(nome);
}

public void receber(String de, String mensagem) {


System.out.print("Para um não-Beatle: ");
super.receber(de, mensagem);

Anacleto Correia 81
IPS-ESTSetúbal Padrões de Desenho

}
}

/*
Para um Beatle: Yoko a John: 'Hi John!'
Para um Beatle: Paul a Ringo: 'All you need is love'
Para um Beatle: Ringo a George: 'My sweet Lord'
Para um Beatle: Paul a John: 'Can't buy me love'
Para um não-Beatle: John a Yoko: 'My sweet love'
*/

Anacleto Correia 82
IPS-ESTSetúbal Padrões de Desenho

Memento (Memória)

Sem violar o princípio de encapsulamento, captura e externaliza o


estado interno de um objecto de forma a poder restaurar mais tarde
esse estado do objecto.

Memento é o padrão utilizado para operações de “desfazer” (também


conhecida como undo ou Ctrl+Z). Consiste em guardar snapshots
(fotografias) de determinados estados de objectos para que possam
ser posteriormente restaurados, caso necessário. Num programa de
desenho, por exemplo, guardaríamos (em objectos, ficheiros, strings,
…) a posição, tamanho, cor e formato dos objectos que compõem o
desenho à medida que o utilizador for desenhando. Caso se
pretendesse retornar a um momento anterior do desenho, bastaria
restaurar uma das fotografias armazenadas.

Participantes:

As classes e/ou objectos que participam no padrão são:

· Memento
o Armazena o estado interno do objecto Originator. O
Memento pode armazenar mais ou menos do conteúdo do
estado interno de Originator, em função do critério deste.
o Protege contra o acesso de outros objectos que não o
Originator. Memento tem de facto duas interfaces.
Caretaker vê a interface mais estreita de Memento –
apenas pode passar o Memento para outros objectos.
Originator, pelo contrário, vê uma interface mais
alargada, que lhe permite acesso a todos os dados
necessários para se restaurar o seu estado prévio.
Idealmente, apenas ao Originator, que produz o
Memento, será permitido o acesso ao estado interno
deste.
· Originator
o Cria um Memento contendo um snapshot do seu estado
corrente.
o Utiliza o Memento para restaurar o seu estado interno.
· Caretaker
o É responsável pela salvaguarda de Memento.
o Nunca opera sobre ou examina o conteúdo de Memento.

Anacleto Correia 83
IPS-ESTSetúbal Padrões de Desenho

Exemplo do padrão no mundo real

Este padrão é comum nas situações em que por exemplo se faz uma
auto-reparação. Imagine-se a reparação dos travões de cilindro, de
numa viatura. Os cilindros são removidos de ambos os lados,
expondo os travões do lado direito e esquerdo. Só se desmonta um
dos lados, enquanto o outro serve de auxiliar de memória (Memento)
da forma como o travão deve ficar montados. Só após um dos lados
ter sido reparado, é que o outro é desmontado. O travão reparado
será então Memento do segundo a ser reparado.

Exemplo

// padrão Memento
// Cliente
public class PatMemento {
public static void main(String[] args) {
PrevisaoVendas s = new PrevisaoVendas();
s.setNome("João Pedro");
s.setTelefone ("21-5630990");
s.setOrcamento(25000.0);

// Armazena o estado interno


ArmazenaMemoria m = new ArmazenaMemoria();
m.setMemento(s.GuardarMemento());

// Continua a alterar o originator


s.setNome("Afonso Silva");
s.setTelefone("22-2097111");
s.setOrcamento(1000000.0);

Anacleto Correia 84
IPS-ESTSetúbal Padrões de Desenho

// Restore saved state


s.RecuperarMemento(m.getMemento());

}
}

// "Originator"
class PrevisaoVendas {
private String nome;
private String telefone;
private double orcamento;

// Propriedades
public String getNome() {
return nome;
}
public void setNome(String valor) {
nome = valor;
System.out.println("Nome: " + nome);
}

public String getTelefone() {


return telefone;
}
public void setTelefone(String valor) {
telefone = valor;
System.out.println("Telefone: " + telefone);
}

public double getOrcamento() {


return orcamento;
}
public void setOrcamento(double valor) {
orcamento = valor;
System.out.println("Orcamento: " + orcamento);
}

public Memento GuardarMemento()


{
System.out.println("\nGuardar estado --\n");
return new Memento(nome, telefone, orcamento);
}

public void RecuperarMemento(Memento memento)


{
System.out.println("\nRecuperar estado --\n");
this.setNome(memento.getNome());
this.setTelefone(memento.getTelefone());
this.setOrcamento(memento.getOrcamento());
}
}

// "Memento"
class Memento {
private String nome;
private String telefone;
private double orcamento;

// Construtor
public Memento(String nome, String telefone, double orcamento)
{

Anacleto Correia 85
IPS-ESTSetúbal Padrões de Desenho

this.nome = nome;
this.telefone = telefone;
this.orcamento = orcamento;
}

// Propriedades
public String getNome() {
return nome;
}
public void setNome(String valor) {
nome = valor;
}

public String getTelefone() {


return telefone;
}
public void setTelefone(String valor) {
telefone = valor;
}

public double getOrcamento() {


return orcamento;
}
public void setOrcamento(double valor) {
orcamento = valor;
}
}

// "Caretaker"
class ArmazenaMemoria
{
private Memento memento;

// Propriedade
public Memento getMemento() {
return memento;
}
public void setMemento(Memento valor) {
memento = valor;
}
}
/*
Nome: João Pedro
Telefone: 21-5630990
Orcamento: 25000.0

Guardar estado --

Nome: Afonso Silva


Telefone: 22-2097111
Orcamento: 1000000.0

Recuperar estado --

Nome: João Pedro


Telefone: 21-5630990
Orcamento: 25000.0
*/

Anacleto Correia 86
IPS-ESTSetúbal Padrões de Desenho

Observer (Observador)

Define uma dependência um-para-muitos entre objectos, de forma a


avisar vários objectos (observadores), quando o estado de um outro
objecto (observado) se altera. Os objectos observadores podem
assim, desencadear as acções necessárias à actualização do seu
estado.

O padrão observador é útil, principalmente quando se utiliza a


abordagem MVC (Model-View-Controller) no desenvolvimento. Os
objectos de domínio são a camada de modelo, enquanto que as
interfaces gráficas formam a camada de visão. Quando vários
componentes gráficos mostram dados de um determinado objecto do
modelo e este é alterado, o que fazer para que todos os componentes
gráficos se actualizem automaticamente? O padrão Observer resolve
isso fazendo com que o objecto do modelo seja o “observado” e as
interfaces gráficas sejam os “observadores”. Quando é criado, cada
observador regista-se junto do observado, que mantém uma lista de
observadores na sua estrutura interna. Assim, quando o observado é
alterado, envia uma mensagem para cada observador, informando-o
do ocorrido. Este então pode-se actualizar em conformidade.

Este padrão reduz o acoplamento entre os objectos, pois na


abordagem MVC, não é razoável que o objecto do modelo conheça
cada uma das interfaces que o manipula. Existe suporte para este
padrão na API J2SE: java.util.Observer e java.util.Observable.

Participantes:

As classes e/ou objectos que participam no padrão são:

· Subject
o Conhece os seus observadores. Qualquer número de
objectos Observer pode observar um Subject.
o Fornece uma interface para adicionar e remover objectos
Observer.
· ConcreteSubject
o Armazena os estados que interessam ao
ConcreteObserver.
o Envia uma notificação aos Observers, quando o estado se
altera.
· Observer
o Define uma interface de alteração, para os objectos que
devam ser notificados de alterações verificados no
Subject.

Anacleto Correia 87
IPS-ESTSetúbal Padrões de Desenho

· ConcreteObserver
o Mantém uma referência para o objecto ConcreteSubject.
o Armazena o estado consistente com o de Subject.
o Implementa a interface de actualização definida em
Observer, para que o seu estado seja consistente com o
de Subject.

Exemplo do padrão no mundo real

Os leilões demonstram este padrão. Cada licitante possui uma


tabuleta numerada, que é usada para indicar a sua licitação. O
licitador inicia o leilão, e vai observando se alguma tabuleta é
erguida, em sinal de aceitação do preço licitado. Essa aceitação vai
fazer alterar esse preço, sendo o novo preço difundido por todos os
licitantes.

Anacleto Correia 88
IPS-ESTSetúbal Padrões de Desenho

Exemplo

// Padrão Observer
// Definir dependências entre objectos do tipo um-para-muitos.
// Quando o objecto altera o seu estado, todos os objectos
// dependentes são notificados para poderem agir em conformidade.

// Subject
import java.util.Observable;
// Observer
import java.util.Observer;

class Flor {
private boolean estaAberto;
private NotificadorAbertura notificaAbertura = new NotificadorAbertura();
private NotificadorFecho notificaFecho = new NotificadorFecho();
public Flor() {
estaAberto = false;
}

public void abre() { // Abre as pétalas


estaAberto = true;
notificaAbertura.notifyObservers();
notificaFecho.abre();
}

public void fecha() { // Fecha as pétalas


estaAberto = false;
notificaFecho.notifyObservers();
notificaAbertura.fecha();
}

public Observable notificadorAbertura() {


return notificaAbertura;
}

public Observable notificadorFecho() {


return notificaFecho;
}

// ConcreteSubject
private class NotificadorAbertura
extends Observable {
private boolean jaAberto = false;
public void notifyObservers() {
if (estaAberto && !jaAberto) {
setChanged();
super.notifyObservers();
jaAberto = true;
}
}

public void fecha() {


jaAberto = false;
}
}

// ConcreteSubject
private class NotificadorFecho
extends Observable {
private boolean jaFechado = false;
public void notifyObservers() {
if (!estaAberto && !jaFechado) {
setChanged();
super.notifyObservers();
jaFechado = true;
}
}

public void abre() {


jaFechado = false;
}
}
}

Anacleto Correia 89
IPS-ESTSetúbal Padrões de Desenho

class Abelha {
private String nome;
private ObservadorAbertura abreObsrv = new ObservadorAbertura();
private ObservadorFecho fechaObsrv = new ObservadorFecho();
public Abelha(String nm) {
nome = nm;
}

// ConcreteObserver
private class ObservadorAbertura
implements Observer { // observa aberturas
public void update(Observable ob, Object a) {
System.out.println("Abelha " + nome + ": hora do pequeno-almoço!");
}
}

// ConcreteObserver
private class ObservadorFecho
implements Observer { // observa fechos
public void update(Observable ob, Object a) {
System.out.println("Abelha " + nome + ": hora de deitar!");
}
}

public Observer observadorAbertura() {


return abreObsrv;
}

public Observer observadorFecho() {


return fechaObsrv;
}
}

class BeijaFlor {
private String nome;
private ObservadorAbertura abreObsrv = new ObservadorAbertura();
private ObservadorFecho fechaObsrv = new ObservadorFecho();
public BeijaFlor(String nm) {
nome = nm;
}

// ConcreteObserver
private class ObservadorAbertura
implements Observer { // obs abertu
public void update(Observable ob, Object a) {
System.out.println("BeijaFlor " + nome + ": hora do pequeno-almoço!");
}
}

// ConcreteObserver
private class ObservadorFecho
implements Observer { // observa fechos
public void update(Observable ob, Object a) {
System.out.println("BeijaFlor " + nome + ": hora de deitar!");
}
}

public Observer observadorAbertura() {


return abreObsrv;
}

public Observer observadorFecho() {


return fechaObsrv;
}
}

// Cliente
public class ObserverPattern {
public static void main(String args[]) {
Flor f = new Flor(); // objecto Observado
Abelha aA = new Abelha("A"); // objecto Observador
Abelha aB = new Abelha("B"); // objecto Observador
BeijaFlor bA = new BeijaFlor("A"); // objecto Observador
BeijaFlor bB = new BeijaFlor("B"); // objecto Observador

System.out.println("=> Adiciona Observadores");


f.notificadorAbertura().addObserver(bA.observadorAbertura());

Anacleto Correia 90
IPS-ESTSetúbal Padrões de Desenho

f.notificadorAbertura().addObserver(bB.observadorAbertura());
f.notificadorAbertura().addObserver(aA.observadorAbertura());
f.notificadorAbertura().addObserver(aB.observadorAbertura());
f.notificadorFecho().addObserver(bA.observadorFecho());
f.notificadorFecho().addObserver(bB.observadorFecho());
f.notificadorFecho().addObserver(aA.observadorFecho());
f.notificadorFecho().addObserver(aB.observadorFecho());

// BeijaFlor B decide adormecer


System.out.println("=> BeijaFlor B decide adormecer");
f.notificadorAbertura().deleteObserver(bB.observadorAbertura());

System.out.println("=> Flor abre");


f.abre(); // Uma alteração que interessa aos observadores
f.abre(); // Já estava aberta

// BeijaFlor A não quer ir para a cama


System.out.println("=> BeijaFlor A não quer ir para a cama");
f.notificadorFecho().deleteObserver(bA.observadorFecho());

System.out.println("=> Flor fecha");


f.fecha();
f.fecha(); // Já estava fechada

System.out.println("=> Remove Observadores Abertura");


f.notificadorAbertura().deleteObservers(); // Levantam-se mais tarde

System.out.println("=> Flor abre");


f.abre();
System.out.println("=> Flor fecha");
f.fecha();
}
}
/*
=> Adiciona Observadores
=> BeijaFlor B decide adormecer
=> Flor abre
Abelha B: hora do pequeno-almoço!
Abelha A: hora do pequeno-almoço!
BeijaFlor A: hora do pequeno-almoço!
=> BeijaFlor A não quer ir para a cama
=> Flor fecha
Abelha B: hora de deitar!
Abelha A: hora de deitar!
BeijaFlor B: hora de deitar!
=> Remove Observadores Abertura
=> Flor abre
=> Flor fecha
Abelha B: hora de deitar!
Abelha A: hora de deitar!
BeijaFlor B: hora de deitar!
*/

Anacleto Correia 91
IPS-ESTSetúbal Padrões de Desenho

State (Estado)

Permite que um objecto altere seu comportamento quando o seu


estado interno muda. O objecto aparenta mudar de classe com a
mudança de estado.

Este padrão visa especificar diferentes estados que um objecto pode


assumir. Ao executar um determinado método, é verificado em que
estado o objecto se encontra, podendo-se realizar uma operação
diferente para cada estado.

Participantes:

As classes e/ou objectos que participam no padrão são:

· Context
o Define uma interface para os clientes.
o Mantém uma instância de uma subclasse ConcreteState
que define o estado actual.
· State
o Define uma interface para encapsular o comportamento
associado a um estado particular de Context.
· Concrete State
o Cada subclasse implementa o comportamento associado a
um estado particular de Context.

Anacleto Correia 92
IPS-ESTSetúbal Padrões de Desenho

Exemplo do padrão no mundo real

Este padrão pode ser observado nas máquinas de venda automática.


O estado dessas máquinas, é função da disponibilidade de produtos,
possibilidade de efectuar trocos, do artigo seleccionado, etc. Quando
é depositada uma quantia e seleccionado um produto, podem ocorrer
em função do estado da máquina as seguintes respostas: entrega do
produto sem troco, entrega do produto com troco, não entrega do
produto devido a insuficiência de troco ou não entrega do produto
devido à sua inexistência.

Anacleto Correia 93
IPS-ESTSetúbal Padrões de Desenho

Exemplo

// Padrão State
// Aplicação Cliente
public class PatState {
public static void main(String[] args) {
// Abrir nova Conta
Conta conta = new Conta("Jim Johnson");

// Efectuar transacções financeiras


conta.deposito(500.0);
conta.deposito(300.0);
conta.deposito(550.0);
conta.pagarJuro();
conta.levantar(2000.00);
conta.levantar(1100.00);
}
}

// "State"
abstract class Estado {
protected Conta conta;
protected double saldo;

protected double juro;


protected double limiteInferior;
protected double limiteSuperior;

// Propriedades
public Conta getConta() {
return conta;
}

public void setConta(Conta valor) {


conta = valor;
}

public double getSaldo() {


return saldo;
}

public void setSaldo(double valor) {


saldo = valor;
}

public abstract void deposito(double quantia);

public abstract void levantar(double quantia);

public abstract void pagarJuro();


}

// "ConcreteState"
// Conta a descoberto
class EstadoVermelho
extends Estado {
double taxaServico;

// Construtor
public EstadoVermelho(Estado estado) {
this.saldo = estado.getSaldo();
this.conta = estado.getConta();
inicializar();
}

private void inicializar() {


// Estes valores devem vir de uma base de dados
juro = 0.0;
limiteInferior = -100.0;
limiteSuperior = 0.0;
taxaServico = 15.00;
}

public void deposito(double quantia) {


saldo += quantia;

Anacleto Correia 94
IPS-ESTSetúbal Padrões de Desenho

verificarAlteracaoEstado();
}

public void levantar(double quantia) {


saldo = saldo - taxaServico;
System.out.println("Não existem fundos disponíveis para levantamento!");
}

public void pagarJuro() {


// Não é pago juro
}

private void verificarAlteracaoEstado() {


if (saldo > limiteSuperior) {
conta.setEstado(new EstadoPrata(this));
}
}
}

// "ConcreteState"
// Silver é um estado em que não há juros de depósito
class EstadoPrata
extends Estado {
// construtores sobrecarregados
public EstadoPrata(Estado estado) {
this(estado.getSaldo(), estado.getConta());
}

public EstadoPrata(double saldo, Conta conta) {


this.saldo = saldo;
this.conta = conta;
inicializar();
}

private void inicializar() {


// Estes valores devem vir de uma base de dados
juro = 0.0;
limiteInferior = 0.0;
limiteSuperior = 1000.0;
}

public void deposito(double quantia) {


saldo += quantia;
verificarAlteracaoEstado();
}

public void levantar(double quantia) {


saldo -= quantia;
verificarAlteracaoEstado();
}

public void pagarJuro() {


saldo += juro * saldo;
verificarAlteracaoEstado();
}

private void verificarAlteracaoEstado() {


if (saldo < limiteInferior) {
conta.setEstado(new EstadoVermelho(this));
}
else if (saldo > limiteSuperior) {
conta.setEstado(new EstadoOuro(this));
}
}
}

// "ConcreteState"
// Estado em que há juro de depósitos
class EstadoOuro
extends Estado {
// construtores sobrecarregados
public EstadoOuro(Estado estado) {
this(estado.getSaldo(), estado.getConta());
}

public EstadoOuro(double saldo, Conta conta) {


this.saldo = saldo;

Anacleto Correia 95
IPS-ESTSetúbal Padrões de Desenho

this.conta = conta;
inicializar();
}

private void inicializar() {


// Estes valores devem vir de uma base de dados
juro = 0.05;
limiteInferior = 1000.0;
limiteSuperior = 10000000.0;
}

public void deposito(double quantia) {


saldo += quantia;
verificarAlteracaoEstado();
}

public void levantar(double quantia) {


saldo -= quantia;
verificarAlteracaoEstado();
}

public void pagarJuro() {


saldo += juro * saldo;
verificarAlteracaoEstado();
}

private void verificarAlteracaoEstado() {


if (saldo < 0.0) {
conta.setEstado(new EstadoVermelho(this));
}
else if (saldo < limiteInferior) {
conta.setEstado(new EstadoPrata(this));
}
}
}

// "Context"
class Conta {
private Estado estado;
private String owner;

// Construtor
public Conta(String owner) {
// As novas contas são por omissão 'Silver'
this.owner = owner;
estado = new EstadoPrata(0.0, this);
}

// Propriedades
public double getSaldo() {
return estado.getSaldo();
}

public Estado getEstado() {


return estado;
}

public void setEstado(Estado valor) {


estado = valor;
}

public void deposito(double quantia) {


estado.deposito(quantia);
System.out.println("Depósito --- " + quantia);
System.out.println(" Saldo = " + this.getSaldo());
System.out.println(" Estado = " +
this.getEstado().getClass().getName());
System.out.println("");
}

public void levantar(double quantia) {


estado.levantar(quantia);
System.out.println("Levantamento --- " + quantia);
System.out.println(" Saldo = " + this.getSaldo());
System.out.println(" Estado = " +
this.getEstado().getClass().getName());
}

Anacleto Correia 96
IPS-ESTSetúbal Padrões de Desenho

public void pagarJuro() {


estado.pagarJuro();
System.out.println("Pagamento Juro --- ");
System.out.println(" Saldo = " + this.getSaldo());
System.out.println(" Estado = " +
this.getEstado().getClass().getName());
}
}
/*
Depósito --- 500.0
Saldo = 500.0
Estado = EstadoPrata
Depósito --- 300.0
Saldo = 800.0
Estado = EstadoPrata
Depósito --- 550.0
Saldo = 1350.0
Estado = EstadoOuro
Pagamento Juro ---
Saldo = 1417.5
Estado = EstadoOuro
Levantamento --- 2000.0
Saldo = -582.5
Estado = EstadoVermelho
Não existem fundos disponíveis para levantamento!
Levantamento --- 1100.0
Saldo = -597.5
Estado = EstadoVermelho
*/

Anacleto Correia 97
IPS-ESTSetúbal Padrões de Desenho

Strategy (Estratégia)

Define uma família de algoritmos, que podem ser utilizados de forma


intermutável. O padrão Strategy permite que o algoritmo varie
independentemente dos clientes que o usam. Cada algoritmo é
encapsulado numa classe.

Strategy é idêntico a State na sua implementação. A diferença é que


uma estratégia não se altera, enquanto o estado pode variar
substancialmente. Isso é naturalmente relativo, e poderíamos
considerar que ambos os padrões são, de facto, um só. No entanto
por vezes pretende-se representar um estado, enquanto que noutras,
pretende-se representar um algoritmo.

Participantes:

As classes e/ou objectos que participam no padrão são:

· Strategy
o Declara uma interface comum a todos os algoritmos
suportados. Context utiliza esta interface para evocar o
algoritmo definido por um ConcreteStrategy
· ConcreteStrategy
o Implementa o algoritmo utilizando a interface Strategy.
· Context
o É configurado com um objecto ConcreteStrategy.
o Mantém uma referência para um objecto Strategy.
o Pode definir uma interface para permitir o acesso de
Strategy aos seus dados.

Anacleto Correia 98
IPS-ESTSetúbal Padrões de Desenho

Exemplo do padrão no mundo real

Os tipos de transporte para o aeroporto de uma cidade são um


exemplo de Strategy. Existem várias opções, tais como a condução
da viatura ligeira particular, tomar um táxi, um autocarro, o metro,
etc. Qualquer dos meios pode ser utilizado de forma alternativa. O
viajante deverá seleccionar o meio adequado, em função do custo,
proximidade e tempo disponível.

Exemplo

// Padrão Strategy
// Strategy
interface Preco {
double algoritmo(double p);
}
// As diferentes estratégias
class PrecoPublico implements Preco {
public double algoritmo(double p) {
return p;
}
}
class PrecoCredito implements Preco {
public double algoritmo(double p) {
return (p*1.2);
}
}
class PrecoVip implements Preco {
public double algoritmo(double p) {
return (p*0.8);
}
}
// O "Context" controla a estratégia
class DeterminaPreco {
private Preco politicaPreco;
public DeterminaPreco(Preco estrat) {
politicaPreco = estrat;
}
double precoAplicavel(double p) {
return politicaPreco.algoritmo(p);
}
void trocaAlgoritmo(Preco novoAlgoritmo) {
politicaPreco = novoAlgoritmo;

Anacleto Correia 99
IPS-ESTSetúbal Padrões de Desenho

}
}
// A classe Cliente
public class testaPadrao {
static DeterminaPreco dt =new DeterminaPreco(new
PrecoPublico());
static double preco = 40.0;
public static void print(double p) {
System.out.println(p);
}
public static void main(String args[]) {
print(dt.precoAplicavel(preco));
dt.trocaAlgoritmo(new PrecoVip());
print(dt.precoAplicavel(preco));
}
}
/*
40.0
32.0
*/

Anacleto Correia 100


IPS-ESTSetúbal Padrões de Desenho

Template Method (Método-Modelo)

O padrão Template Method define o esqueleto de um algoritmo num


método, permitindo que as subclasses completem algumas das
etapas. É permitido assim às subclasses, que redefinam determinadas
etapas de um algoritmo sem alterar a sua estrutura.

O padrão consiste pois em implementar um método numa classe


abstracta, que evoca um ou mais métodos abstractos. Ou seja, parte
da implementação daquele método fica delegada às subclasses, que
terão que implementar os métodos abstractos. Por exemplo, pode-se
criar uma classe utilitária (para reutilização) que defina uma janela
com alguns botões, mas deixar às subclasses a tarefa de implementar
o que deverá ser efectuado quando os botões forem accionados.

Participantes:

As classes e/ou objectos que participam no padrão são:

· AbstractClass
o Define métodos abstractos, que as subclasses concretas
irão definir, para implementar passos concretos do
algoritmo.
o Implementa um método-padrão, que define o esqueleto
de um algoritmo. O método-padrão evoca os métodos
abstractos ou outros métodos definidos na classe
AbstractClass, ou métodos definidos noutros objectos.
· ConcreteClass
o Implementa os métodos abstractos definidos na classe
AbstractClass, de forma que a subclasse possa realizar
passos especializados do algoritmo.

Anacleto Correia 101


IPS-ESTSetúbal Padrões de Desenho

Exemplo do padrão no mundo real

Os construtores civis utilizam o Template Method quando constroem


vivendas. Uma vivenda típica consiste num número limitado de
assoalhadas, que tem um tipo de acabamento. As fundações,
canalização, instalação eléctrica, são idênticas em todas as vivendas.
Os acabamentos só serão efectuados em fases posteriores da
construção, de forma a colocar no mercado uma diversidade de
modelos de vivendas.

Exemplo

Padrão Template Method


// AbstractClass
abstract class ClasseAbstracta {
public ClasseAbstracta() { template(); }
abstract void customiza1();
abstract void customiza2();
private void template() {
for(int i = 0; i < 5; i++) {
customiza1();
customiza2();
}
}
}
// ConcreteClass
class ClasseConcreta extends ClasseAbstracta {
void customiza1() {
System.out.print("Olá ");
}
void customiza2() {
System.out.println("Mundo!");
}
}
public class ClienteTemplateMethod {
ClasseConcreta app = new ClasseConcreta();
public static void main(String args[]) {
new ClienteTemplateMethod();
}
}

Anacleto Correia 102


IPS-ESTSetúbal Padrões de Desenho

// Olá Mundo!
// Olá Mundo!
// Olá Mundo!
// Olá Mundo!
// Olá Mundo!

Anacleto Correia 103


IPS-ESTSetúbal Padrões de Desenho

Visitor (Visitante)

Representa um método a ser executado sobre os elementos de uma


estrutura de objectos. O padrão Visitor permite que se defina um
novo método sem alterar as classes dos elementos sobre os quais o
método age.

Visitor promove a extensão de funcionalidades através da criação de


classes que obedeçam a uma interface comum. Esta interface, Visitor
(Visitante), especifica sobre que objectos os métodos poderão ser
executados, definindo um método visitar(ObjetoX o) para cada
ObjetoX possível. Os métodos são codificadas então em classes que
implementam Visitante, o que permite que se crie novos métodos,
mas não permite que se altere os objectos sobre os quais operam. Os
objectos clientes criam o visitante que quiserem e chamam o método
visitar() para o objecto sobre o qual pretendam operar.

Participantes:

As classes e/ou objectos que participam no padrão são:

· Visitor
o Declara o método visit para cada classe de
ConcreteElement na estrutura de objectos. O nome do
método e a assinatura, identificam a classe que envia o
pedido visit ao Visitor Isso permite ao visitor determinar a
classe concreta do elemento a ser visitado. Então o visitor
pode aceder directamente aos elementos, através da sua
interface particular.
· ConcreteVisitor
o Implementa os métodos declarados pelo Visitor. Cada
método implementa um fragmento do algoritmo definido
para a correspondente classe ou objecto na estrutura.
ConcreteVisitor fornece o contexto para o algoritmo e
armazena o seu estado local. Este estado, geralmente
acumula resultados durante a passagem pelos elementos
da estrutura.
· Element
o Define o método Accept que recebe um visitor como
argumento.
· ConcreteElement
o Implementa o método Accept que recebe um visitor como
argumento.
· ObjectStructure
o Pode enumerar os seus elementos

Anacleto Correia 104


IPS-ESTSetúbal Padrões de Desenho

o Pode fornecer uma interface de alto-nível, para permitir o


visitor visitar os seus elementos.
o Pode ser um padrão Composite ou uma collection – list ou
set.

Anacleto Correia 105


IPS-ESTSetúbal Padrões de Desenho

Exemplo do padrão no mundo real

Este padrão pode ser observado no modo de operação das empresas


de táxis. Quando alguém chama um táxi (Visitor), passa a fazer parte
da lista de clientes em espera. A empresa envia então um táxi ao
cliente (que aceita o Visitante). Após entrar no táxi, o cliente deixa de
ter controlo sobre o seu transporte, passando o mesmo para a
responsabilidade do táxi.

Exemplo

// Padrão Visitor
import java.util.ArrayList;

// Aplicação Cliente
public class PatVisitor {
public static void main(String[] args) {
// Carregamento de uma collection de empregado
Empregados e = new Empregados();
e.adicionar(new Administrativo());
e.adicionar(new DirectorDepartamento());
e.adicionar(new DirectorGeral());

// Empregados são 'visitados'


e.aceitar(new RendimentoVisitado());
e.aceitar(new FeriasVisitado());

}
}

// "Visitor"
interface IVisitor {
void visitar(Elemento elemento);
}

// "ConcreteVisitor1"
class RendimentoVisitado
implements IVisitor {
public void visitar(Elemento elemento) {
Empregado empregado = (Empregado) elemento;

// Dá 10% de aumento de ordenado


empregado.setRendimento(empregado.getRendimento() * 1.10);
System.out.println(empregado.getClass().getName() + " " +
empregado.getNome() + " - novo salário: " +
empregado.getRendimento());
}
}

// "ConcreteVisitor2"

Anacleto Correia 106


IPS-ESTSetúbal Padrões de Desenho

class FeriasVisitado
implements IVisitor {
public void visitar(Elemento elemento) {
Empregado empregado = (Empregado) elemento;

// Dá 3 dias extra de férias


empregado.setDiasFerias(empregado.getDiasFerias() + 3);
System.out.println(empregado.getClass().getName() + " " +
empregado.getNome() + " - nova duração das férias: " +
empregado.getDiasFerias());
}
}

class Administrativo
extends Empregado {
// Constructor
public Administrativo() {
super("Pedro", 25000.0, 14);
}
}

class DirectorDepartamento
extends Empregado {
// Constructor
public DirectorDepartamento() {
super("Elias", 35000.0, 16);
}
}

class DirectorGeral
extends Empregado {
// Construtor
public DirectorGeral() {
super("Duarte", 45000.0, 21);
}
}

// "Elemento"
abstract class Elemento {
public abstract void aceitar(IVisitor visitor);
}

// "ConcreteElement"
class Empregado
extends Elemento {
String nome;
double rendimento;
int diasFerias;

// Construtor
public Empregado(String nome, double rendimento,
int diasFerias) {
this.nome = nome;
this.rendimento = rendimento;
this.diasFerias = diasFerias;
}

// Propriedades
public String getNome() {
return nome;
}

public void setNome(String value) {


nome = value;
}

public double getRendimento() {


return rendimento;
}

public void setRendimento(double value) {


rendimento = value;
}

public int getDiasFerias() {


return diasFerias;
}

Anacleto Correia 107


IPS-ESTSetúbal Padrões de Desenho

public void setDiasFerias(int value) {


diasFerias = value;
}

public void aceitar(IVisitor visitor) {


visitor.visitar(this);
}
}

// "ObjectStructure"
class Empregados {
private ArrayList empregados = new ArrayList();

public void adicionar(Empregado empregado) {


empregados.add(empregado);
}

public void remover(Empregado empregado) {


empregados.remove(empregado);
}

public void aceitar(IVisitor visitor) {


for (int i = 0; i < empregados.size(); i++) {
( (Empregado) empregados.get(i)).aceitar(visitor);
}
System.out.println();
}
}
/*
Administrativo Pedro - novo salário: 27500.00
DirectorDepartamento Elias - novo salário: 38500.00
DirectorGeral Duarte - novo salário: 49500.00

Administrativo Pedro - nova duração das férias: 17


DirectorDepartamento Elias - nova duração das férias: 19
DirectorGeral Duarte - nova duração das férias: 24

*/

Anacleto Correia 108


IPS-ESTSetúbal Padrões de Desenho

Relações entre Padrões GoF

Anacleto Correia 109


IPS-ESTSetúbal Padrões de Desenho

Referências
http://www.jablo.com.br/page/engenho/
Patterns in C#
http://www.dofactory.com/Patterns/PatternBuilder.aspx
Patterns Home Page - http://hillside.net/
Pattern Index - http://c2.com/cgi/wiki?PatternIndex
Design Patterns in Java -http://www.fluffycat.com/java/patterns.html
Duell, Michael, Non-Software Examples of Software Design Patterns -
http://www2.ing.puc.cl/~jnavon/IIC2142/patexamples.htm

Anacleto Correia 110

Vous aimerez peut-être aussi