Vous êtes sur la page 1sur 70

APOSTILA DE PADRÕES DE DESIGN

Ivan Mathias Filho

Departamento de Informática – PUC-Rio


Sumário

Sumário

Introdução aos Padrões de Design ...................................... 1


1.1 O Padrão Factory Method ........................................................................ 2
1.1.1 Objetivo ..................................................................................................................... 2
1.1.2 Contexto .................................................................................................................... 2
1.1.3 Estrutura.................................................................................................................... 3
1.1.4 Aplicabilidade ............................................................................................................. 4
1.1.5 Conseqüências............................................................................................................ 4
1.1.6 Exemplo..................................................................................................................... 4
1.1.7 Código do Exemplo ..................................................................................................... 5

1.2 O Padrão Abstract Factory ......................................................................11


1.2.1 Objetivo ....................................................................................................................11
1.2.2 Contexto ...................................................................................................................11
1.2.3 Estrutura...................................................................................................................11
1.2.4 Aplicabilidade ............................................................................................................13
1.2.5 Conseqüências...........................................................................................................13
1.2.6 Exemplo....................................................................................................................13
1.2.7 Código do Exemplo ....................................................................................................14

1.3 O Padrão Singleton ................................................................................18


1.3.1 Objetivo ....................................................................................................................18
1.3.2 Contexto ...................................................................................................................18
1.3.3 Estrutura...................................................................................................................18
1.3.4 Aplicabilidade ............................................................................................................18
1.3.5 Conseqüências...........................................................................................................19
1.3.6 Exemplo....................................................................................................................19
1.3.7 Código do Exemplo ....................................................................................................20

1.4 O Padrão Facade ...................................................................................23


1.4.1 Objetivo ....................................................................................................................23
1.4.2 Contexto ...................................................................................................................23
1.4.3 Estrutura...................................................................................................................23
1.4.4 Aplicabilidade ............................................................................................................24
1.4.5 Conseqüências...........................................................................................................25
1.4.6 Exemplo....................................................................................................................25
1.4.7 Código do Exemplo ....................................................................................................26

1.5 O Padrão Composite ..............................................................................31


1.5.1 Objetivo ....................................................................................................................31
1.5.2 Contexto ...................................................................................................................31
1.5.3 Estrutura...................................................................................................................31
1.5.4 Aplicabilidade ............................................................................................................32

Ivan Mathias Filho I PUC-Rio


Sumário

1.5.5 Conseqüências...........................................................................................................33
1.5.6 Exemplo....................................................................................................................33
1.5.7 Código do Exemplo ....................................................................................................33

1.6 O Padrão Observer.................................................................................37


1.6.1 Objetivo ....................................................................................................................37
1.6.2 Contexto ...................................................................................................................37
1.6.3 Estrutura...................................................................................................................37
1.6.4 Aplicabilidade ............................................................................................................38
1.6.5 Conseqüências...........................................................................................................38
1.6.6 Exemplo....................................................................................................................38
1.6.7 Código do Exemplo ....................................................................................................39

1.7 O Padrão Strategy .................................................................................42


1.7.1 Objetivo ....................................................................................................................42
1.7.2 Contexto ...................................................................................................................42
1.7.3 Estrutura...................................................................................................................42
1.7.4 Aplicabilidade ............................................................................................................43
1.7.5 Conseqüências...........................................................................................................44
1.7.6 Exemplo....................................................................................................................44
1.7.7 Código do Exemplo ....................................................................................................45

1.8 O Padrão State ......................................................................................47


1.8.1 Objetivo ....................................................................................................................47
1.8.2 Contexto ...................................................................................................................47
1.8.3 Estrutura...................................................................................................................47
1.8.4 Aplicabilidade ............................................................................................................49
1.8.5 Conseqüências...........................................................................................................49
1.8.6 Exemplo....................................................................................................................50
1.8.7 Código do Exemplo ....................................................................................................52

1.9 O Padrão Proxy......................................................................................59


1.9.1 Objetivo ....................................................................................................................59
1.9.2 Contexto ...................................................................................................................59
1.9.3 Estrutura...................................................................................................................60
1.9.4 Aplicabilidade ............................................................................................................60
1.9.5 Conseqüências...........................................................................................................61
1.9.6 Exemplo....................................................................................................................61
1.9.7 Código do Exemplo ....................................................................................................61

Bibliografia ........................................................................ 66

Ivan Mathias Filho II PUC-Rio


Lista de Figuras

Lista de Figuras

Figura 1.1 – Framework para o Gerenciamento de Documentos..................................... 3


Figura 1.2 – Estrutura do Padrão Factory Method. .......................................................... 3
Figura 1.3 – Exemplo do Padrão Factory Method. .......................................................... 5
Figura 1.4 – Estrutura do Padrão Abstract Factory........................................................ 12
Figura 1.5 – Exemplo do Padrão Abstract Factory. ....................................................... 14
Figura 1.6 – Estrutura do Padrão Singleton.................................................................... 18
Figura 1.7 – Exemplo do Padrão Singleton. ................................................................... 20
Figura 1.8 – xxxxxxx Facade......................................................................................... 23
Figura 1.9 – Estrutura do Padrão Facade. ...................................................................... 24
Figura 1.10 – Exemplo do Padrão Facade. .................................................................... 26
Figura 1.11 – Estrutura do Padrão Composite................................................................ 32
Figura 1.12 – Exemplo do Padrão Composite. ............................................................... 33
Figura 1.13 – Estrutura do Padrão Observer. ................................................................. 37
Figura 1.14 – Adição de Um Novo Observador............................................................. 38
Figura 1.15 – Notificação de Um Evento. ...................................................................... 38
Figura 1.16 – Exemplo do Padrão Observer. ................................................................. 39
Figura 1.17 – Estrutura do Padrão Strategy.................................................................... 43
Figura 1.18 – Exemplo do Padrão Strategy.................................................................... 45
Figura 1.19 – Estrutura do Padrão State. ........................................................................ 48
Figura 1.20 – Máquina de Estados do Editor de Parâmetros.......................................... 51
Figura 1.21 – A Representação dos Estados Através de uma Hierarquia de Classes..... 52
Figura 1.22 – Exemplo de Uso do Padrão Proxy. .......................................................... 59
Figura 1.23 – Estrutura do Padrão Proxy. ...................................................................... 60

Ivan Mathias Filho III PUC-Rio


Introdução aos Padrões de Design

Capítulo 1

Introdução aos Padrões de Design

Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Ivan Mathias Filho 1 PUC-Rio


Introdução aos Padrões de Design

1.1 O Padrão Factory Method

1.1.1 Objetivo

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

1.1.2 Contexto

Considere o problema de construir um framework para aplicações que gerenciem


documentos. Tais aplicações são tipicamente centradas nos documentos e nos
arquivos que serão manipulados. A operação de uma aplicação dessa natureza
começa, normalmente, com uma instrução para criar, ou editar, um documento de um
processador de textos, uma planilha ou quaisquer outros documentos ou arquivos que
possam ser manipulados.

Um framework para este tipo de aplicação deve fornecer suporte de alto-nível para as
operações usuais, tais como a criação, a abertura e a gravação de documentos. O
suporte a estas operações geralmente inclui um conjunto consistente de métodos que
são chamados em resposta aos comandos dos usuários. Para ilustrar esta discussão,
iremos introduzir a classe Application como sendo a classe que fornece os métodos
necessários para a manipulação de documentos.

Um objeto da classe Application deverá ser o ponto central para onde irão convergir
todas as solicitações originadas na interface com os usuários em resposta aos
comandos por eles emitidos. Como não é função deste objeto implementar
diretamente as solicitações dos usuários, a classe Application irá delegar a maioria das
mensagens para os objetos que efetivamente gerenciam os documentos. Por sua vez,
a lógica para a implementação dos comandos de manipulação definida nestes objetos
irá variar em função do tipo do documento. Entretanto, existem operações, tais como a
exibição do título do documento na barra de título, que são comuns a todos os tipos de
documentos. Isto sugere que o framework seja organizado de tal maneira que uma
classe abstrata, chamada Document, contenha as propriedades comuns a todos os
tipos de documentos, e que as subclasses de Document contenham as propriedades
específicas aos diversos tipos de documentos suportados pela aplicação (Figura 1.1).

Ivan Mathias Filho 2 PUC-Rio


Introdução aos Padrões de Design

O que não foi dito até agora, e que também não é mostrado no diagrama da Figura
1.1, é como um objeto da classe Application irá criar as instâncias das classes
específicas aos diversos tipos de documentos existentes, de tal maneira que ela
permaneça independente do tipo de documento a ser manipulado. Este é o objetivo do
padrão Factory Method.

Figura 1.1 – Framework para o Gerenciamento de Documentos.

1.1.3 Estrutura

A Figura 1.2 mostra um diagrama que ilustra os papéis das classes e das interfaces
que compõem o padrão Factory Method.

Figura 1.2 – Estrutura do Padrão Factory Method.

Ivan Mathias Filho 3 PUC-Rio


Introdução aos Padrões de Design

A seguir é apresentado o papel de cada uma dos componentes do padrão Factory


Method:

Product – superclasse abstrata dos objetos que serão produzidos pelo Factory
Method.

ConcreteProductX – classes concretas instanciadas pelo Factory Method. Se as


classes não compartilharem uma lógica comum, a classe Product poderá ser
substituída por uma interface.

CreationRequestor – classe que precisa criar um objeto específico. Ela o fará através
de uma instância da classe Factory.

FactoryIF – os objetos que irão instanciar objetos da classe Product terão que
implementar a interface FactoryIF.

Factory – classe que implementa a interface FactoryIF e, por conseguinte, define o


método que instancia uma das subclasses de Product (ConcreteProductX).

1.1.4 Aplicabilidade

Use o padrão Factory Method quando:

• Uma classe não puder antecipar as classes dos objetos que ele precisa criar.

• Uma classe tiver que delegar as suas subclasses a responsabilidade pela criação de
objetos de uma aplicação. Isso permitirá localizar nas subclasses o conhecimento
necessário à criação dos objetos.

1.1.5 Conseqüências

a. O CreationRequestor torna-se independente da classe do objeto


(ConcreteProductX) instanciado, podendo trabalhar com quaisquer classes
concretas definidas pelo framework.

b. O tipo dos produtos que serão manipulados pela aplicação pode ser mudado
dinamicamente.

1.1.6 Exemplo

Suponha que estejamos desenvolvendo uma extensão da classe Socket com o objetivo
de codificar streams de bytes enviados por uma conexão socket e de decodificar

Ivan Mathias Filho 4 PUC-Rio


Introdução aos Padrões de Design

streams de bytes recebidos por conexões de mesma natureza. Chamaremos esta


classe de EncryptedSocket.

A classe EncryptedSocket deverá suportar múltiplos algoritmos de codificação. Em


função das restrições legais impostas pelo governo dos EUA à importação e exportação
de algoritmos de codificação, a classe EncryptedSocket deverá ser mantida
independente das classes que implementam os algoritmos de codificação e
decodificação.

O uso de múltiplos algoritmos de codificação e decodificação, sem que a classe


EncryptedSocket tenha que conhecer antecipadamente as classes que encapsulam tais
algoritmos, nos sugere a aplicação do padrão Factory Method para solucionar este
problema.

A Figura 1.3 mostra um diagrama de classes que ilustra a situação descrita acima.

Figura 1.3 – Exemplo do Padrão Factory Method.

1.1.7 Código do Exemplo

import java.io.InputStream;
import java.io.OutputStream;
import java.security.Key;

/**
* Abstract class to encrypt/decrypt streams of bytes.
*/
abstract public class Encryption {
private Key key;

Ivan Mathias Filho 5 PUC-Rio


Introdução aos Padrões de Design

/**
* Constructor
* @param key The key to use to perform the encryption.
*/
public Encryption(Key key) {
this.key = key;
} // Constructor(Key)

/**
* Return the key this object used for encryption and decryption.
*/
protected Key getKey() {
return key;
} // getKey()

/**
* This method returns an OutputStream that encrypts the bytes
* written to it and writes the encrypted bytes to the given
* OutputStream.
* @param out The OutputStream that the OutputStream returned by
* this method will write encrypted bytes to.
*/
abstract OutputStream encryptOutputStream(OutputStream out);

/**
* This method returns an InputStream that decrypts the stream of
* bytes that it reads from the given InputStream.
* @param in The InputStream that the InputStream returned by this
* method will read bytes from.
*/
abstract InputStream decryptInputStream(InputStream in);
} // class Encrypt

******************************************************************************

import java.io.InputStream;
import java.io.FilterInputStream;
import java.io.FilterOutputStream;
import java.io.OutputStream;
import java.security.Key;

/**
* class to RSA encrypt/decrypt streams of bytes.
*/
public class RSAEncryption extends Encryption {
/**
* Constructor
* @param key The key to use to perform the encryption.
*/
public RSAEncryption(Key key) {
super(key);
} // Constructor(Key)

/**
* This method returns an OutputStream that encrypts the bytes
* written to it and writes the encrypted bytes to the given
* OutputStream.
* @param out The OutputStream that the OutputStream returned by
* this method will write encrypted bytes to.
*/
OutputStream encryptOutputStream(OutputStream out) {
return new RSAEncrytpedOutputStream(out);
} // encryptOutputStream(OutputStream)

/**
* This method returns an InputStream that decrypts the stream of
* bytes that it reads from the given InputStream.

Ivan Mathias Filho 6 PUC-Rio


Introdução aos Padrões de Design

* @param in The InputStream that the InputStream returned by this


* method will read bytes from.
*/
InputStream decryptInputStream(InputStream in) {
return new RSAEncrytpedInputStream(in);
} // decryptInputStream(InputStream)

/**
* Class to encrypt an OuputStream.
*/
private class RSAEncrytpedOutputStream extends FilterOutputStream {
/**
* constructor
* @param out the OutputStream this object should write encrypted
* bytes to.
*/
RSAEncrytpedOutputStream(OutputStream out) {
super(out);
} // RSAEncrytpedOutputStream(OutputStream)
//...
} // class RSAEncrytpedOutputStream

private class RSAEncrytpedInputStream extends FilterInputStream {


/**
* constructor
* @param in the InputStream this object should read encrypted
* bytes from.
*/
RSAEncrytpedInputStream(InputStream in) {
super(in);
} // RSAEncrytpedInputStream(InputStream)
//...
} // class RSAEncrytpedInputStream
} // class RSAEncryption

******************************************************************************

import java.io.InputStream;
import java.io.FilterInputStream;
import java.io.FilterOutputStream;
import java.io.OutputStream;
import java.security.Key;

/**
* class to DES encrypt/decrypt streams of bytes.
*/
public class DESEncryption extends Encryption {
/**
* Constructor
* @param key The key to use to perform the encryption.
*/
public DESEncryption(Key key) {
super(key);
} // Constructor(Key)

/**
* This method returns an OutputStream that encrypts the bytes
* written to it and writes the encrypted bytes to the given
* OutputStream.
* @param out The OutputStream that the OutputStream returned by
* this method will write encrypted bytes to.
*/
OutputStream encryptOutputStream(OutputStream out) {
return new DESEncrytpedOutputStream(out);
} // encryptOutputStream(OutputStream)

/**
* This method returns an InputStream that decrypts the stream of

Ivan Mathias Filho 7 PUC-Rio


Introdução aos Padrões de Design

* bytes that it reads from the given InputStream.


* @param in The InputStream that the InputStream returned by this
* method will read bytes from.
*/
InputStream decryptInputStream(InputStream in) {
return new DESEncrytpedInputStream(in);
} // decryptInputStream(InputStream)

/**
* Class to encrypt an OuputStream.
*/
private class DESEncrytpedOutputStream extends FilterOutputStream {
/**
* constructor
* @param out the OutputStream this object should write encrypted
* bytes to.
*/
DESEncrytpedOutputStream(OutputStream out) {
super(out);
} // DESEncrytpedOutputStream(OutputStream)
//...
} // class DESEncrytpedOutputStream

private class DESEncrytpedInputStream extends FilterInputStream {


/**
* constructor
* @param in the InputStream this object should read encrypted
* bytes from.
*/
DESEncrytpedInputStream(InputStream in) {
super(in);
} // DESEncrytpedInputStream(InputStream)
//...
} // class DESEncrytpedInputStream
} // class DESEncryption

******************************************************************************

import java.security.Key;
import java.security.NoSuchAlgorithmException;

/**
* This interface must be implemented by all factory classes used to
* create instances of subclasses of Encryption.
*/
public interface EncryptionFactoryIF {
/**
* This method returns an instance of the appropriate subclass of
* Encryption as determined from information provided by the given
* Key object.
* @param key The key that will be used to perform the encryption.
*/
public Encryption createEncryption(Key key) throws
NoSuchAlgorithmException;
} // interface EncryptionFactoryIF

******************************************************************************

import java.security.Key;
import java.security.NoSuchAlgorithmException;

/**
* This class creates instances of appripriate subclasses of Encryption.
* The appropriate subclass is determined by calling the Key object's
*
*/
public class EncryptionFactory implements EncryptionFactoryIF {
/**

Ivan Mathias Filho 8 PUC-Rio


Introdução aos Padrões de Design

* This method returns an instnace of the appropriate subclass of


* Encryption as determined from information provided by the given
* Key object's getAlgorithm method.
* @param key The key that will be used to perform the encryption.
*/
public
Encryption createEncryption(Key key) throws NoSuchAlgorithmException{
String algorithm = key.getAlgorithm();
if ("DES".equals(algorithm))
return new DESEncryption(key);
if ("RSA".equals(algorithm))
return new RSAEncryption(key);
throw new NoSuchAlgorithmException(algorithm);
} // createEncryption(Key)
} // class EncryptionFactory

******************************************************************************

import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.security.Key;
import java.security.NoSuchAlgorithmException;

/**
* This class extends socket so that the stream of bytes that goes over
* the net is encrypted.
*/
public class EncryptedSocket extends Socket {
private static Encryption crypt;
private Key key;

/**
* Constructor
* @param key The key to use for encryption and decryption. This
* object will determine the encryption technique to use
* by calling the key object's getAlgorithm() method.
* @param factory The Factory object to use to create Encryption
* objects.
* @exception NoSuchAlgorithmException if the key specifies an
* encryption technique that is not available.
*/
public EncryptedSocket(Key key, EncryptionFactoryIF factory) throws
NoSuchAlgorithmException {
this.key = key;
crypt = factory.createEncryption(key);
} // Constructor(Key, EncryptionFactoryIF)

/**
* Returns an input stream for this socket that decrypts the inbound
* stream of bytes.
*
* @return an input stream for reading decrypted bytes from this
* socket.
* @exception IOException if an I/O error occurs when creating the
* input stream.
*/
public InputStream getInputStream() throws IOException {
return crypt.decryptInputStream(super.getInputStream());
} // getInputStream()

/**
* Returns an output stream for this socket that encrypts the
* outbound stream of bytes.
*
* @return an output stream for reading decrypted bytes from
* this socket.

Ivan Mathias Filho 9 PUC-Rio


Introdução aos Padrões de Design

* @exception IOException if an I/O error occurs when creating the


* output stream.
*/
public OutputStream getOutputStream() throws IOException {
return crypt.encryptOutputStream(super.getOutputStream());
} // getOutputStream()
} // class EncryptedSocket

Ivan Mathias Filho 10 PUC-Rio


Introdução aos Padrões de Design

1.2 O Padrão Abstract Factory

1.2.1 Objetivo

Fornecer uma interface para a criação de famílias de objetos relacionados, ou


dependentes, sem especificar as suas classes concretas.

1.2.2 Contexto

Suponha que você tenha que construir um framework para interfaces gráficas (GUI)
que possa ser utilizado sobre vários sistemas gerenciadores de janelas diferentes, tais
como MS Windows, Motif ou MacOS. Você deverá também fazer com que o framework
funcione de acordo com as características visuais de cada uma das plataformas
definidas.

Uma possível solução para este problema será definir uma classe abstrata para cada
tipo de componente visual (text field, push button, list box e etc) e criar subclasses
concretas que suportem tais componentes visuais em cada uma das plataformas em
questão.

Para tornar a implementação robusta, será necessário garantir que os objetos que
implementam os componentes visuais sejam criados de acordo com a plataforma
desejada. Neste contexto, uma fábrica abstrata irá declarar métodos para a criação de
instâncias de cada uma das classes abstratas que representam componentes visuais.
Enquanto isso, as subclasses concretas irão definir fábricas concretas que irão conter
os métodos necessários à criação dos componentes visuais de acordo com a
plataforma escolhida.

1.2.3 Estrutura

A Figura 1.4 mostra um diagrama de classes que ilustra o papel que cada classe
representa no padrão Abstract Factory.

Ivan Mathias Filho 11 PUC-Rio


Introdução aos Padrões de Design

Figura 1.4 – Estrutura do Padrão Abstract Factory.

A seguir é apresentado o papel de cada uma das classes do padrão Abstract Factory:

Client – utiliza as classes que representam os componentes visuais para solicitar


serviços à plataforma com a qual está trabalhando. A classe Client tem conhecimento
apenas das classes abstratas que representam os componentes visuais abstratos.

AbstractFactory – declara os métodos abstratos para a criação de instâncias das


classes concretas que implementam os componentes visuais.

ConcreteFactoryX – implementam os métodos declarados na fábrica abstrata com o


objetivo de criar instâncias das classes concretas que implementam os componentes
visuais na plataforma X.

WidgetY – classes abstratas que definem os componentes visuais que fazem parte do
framework.

PlatformXWidgetY – classes concretas que implementam os componentes visuais Y


na plataforma X.

Ivan Mathias Filho 12 PUC-Rio


Introdução aos Padrões de Design

1.2.4 Aplicabilidade

Use o padrão Abstract Factory quando:

• Um sistema for independente de como os seus componentes são criados,


compostos ou representados.

• Um sistema deve ser configurado como um produto de uma família de múltiplos


produtos.

• Uma família de objetos-produto for projetada para ser usada em conjunto, e for
necessário garantir tal restrição.

• Quisermos fornecer uma biblioteca de classes de produtos e revelarmos apenas as


interfaces, deixando as suas implementações ocultas.

1.2.5 Conseqüências

c. As classes concretas que implementam os componentes visuais são independentes


das classes que as usam, dado que a fábrica abstrata encapsula o processo de
criação de tais componentes visuais.

d. Inserir novas classes que dêem suporte a novas plataformas é uma tarefa simples.
Uma classe que represente uma fábrica concreta é usualmente referenciada em
apenas um ponto do framework. De modo similar, é bastante simples alterar uma
fábrica concreta para tratar de uma nova plataforma a ser adicionada ao
framework.

e. Ao forçarmos os clientes a usarem as fábricas concretas para a criação dos


componentes visuais, o padrão Abstract Factory assegura que os clientes usarão
um conjunto de objetos consistentes com a plataforma com a qual desejam
interagir.

f. A principal deficiência do padrão Abstract Factory é o excesso de trabalho


necessário para criar um conjunto de classes que dê suporte a uma nova
plataforma.

1.2.6 Exemplo

Seja um programa que tenha por objetivo realizar diagnósticos remotos em


computadores para um fabricante de equipamentos. Através dos anos, o fabricante em
questão produziu computadores com substanciais diferenças nas suas respectivas
Ivan Mathias Filho 13 PUC-Rio
Introdução aos Padrões de Design

arquiteturas. Nos equipamentos mais antigos foram usados chips de CPU da Enginola,
que são baseados na tradicional arquitetura CISC. Desde então, o nosso fabricante
produziu computadores baseados na sua própria arquitetura RISC, chamdas de Ember,
SuperEmber e UltraEmber. Os componentes principais destas várias arquiteturas
executam funções similares, porém possuem conjuntos de componentes distintos.

A Figura 1.5 mostra um diagrama de classes que ilustra uma situação semelhante à
descrita acima, porém com apenas dois componentes para duas arquiteturas distintas.

Figura 1.5 – Exemplo do Padrão Abstract Factory.

1.2.7 Código do Exemplo

/**
* This is an abstract factory class for creating objects that are used to
* perform remote tests on core components of computers.
*/
public abstract class ArchitectureToolkit {
private static final EmberToolkit emberToolkit
= new EmberToolkit();
private static EnginolaToolkit enginolaToolkit
= new EnginolaToolkit();
//...

// Symbolic names to identify types of computers


public final static int ENGINOLA = 900;
public final static int EMBER = 901;
// ...
Ivan Mathias Filho 14 PUC-Rio
Introdução aos Padrões de Design

/**
* This method returns a concrete factory object that is an instance
* of the concrete factory class that is appropriate for the given
* computer architecture.
* @param architecture a value indicating the architecture that a
* concrete factory should be returned for.
*/
static final ArchitectureToolkit getFactory(int architecture) {
switch (architecture) {
case ENGINOLA:
return enginolaToolkit;

case EMBER:
return emberToolkit;
// ...
} // switch
String errMsg = Integer.toString(architecture);
throw new IllegalArgumentException(errMsg);
} // getFactory()

/**
* Method to create objects for remote testing CPUs.
*/
public abstract CPU createCPU() ;

/**
* Method to create objects for remote testing MMUs.
*/
public abstract MMU createMMU() ;

//...
} // ArchitectureToolkit

/**
* This is an abstract class for objects that perform remote tests on MMUs.
*/
public abstract class MMU extends ComponentTester {
//...
} // class MMU

/**
* Sample client class to show how a client class can create concrete
* widget objects using an abstract factory
*/
public class Client {
public void doIt () {
ArchitectureToolkit af;
af = ArchitectureToolkit.getFactory(ArchitectureToolkit.EMBER);
CPU cpu = af.createCPU();
//...
} //doIt
} // class Client

public abstract class ComponentTester {


} // class ComponentTester

/**
* This is an abstract class for objects that perform remote tests on CPUs.
*/
public abstract class CPU extends ComponentTester {
//...
} // class CPU

Ivan Mathias Filho 15 PUC-Rio


Introdução aos Padrões de Design

/**
* This is a class for objects that perform remote tests on Ember
* architecture CPUs.
*/
class EmberCPU extends CPU {
//...
} // class EmberCPU

/**
* This is a class for objects that perform remote tests on Ember
* architecture MMUs.
*/
public class EmberMMU extends MMU {
//...
} // class EmbeMMU

/**
* This is a concrete factory class for creating objects that are used to
* perform remote tests on core components of ember architecture
* computers.
*/
class EmberToolkit extends ArchitectureToolkit {
/**
* Method to create objects for remote testing ember CPUs.
*/
public CPU createCPU() {
return new EmberCPU();
} // createCPU()

/**
* Method to create objects for remote testing ember MMUs.
*/
public MMU createMMU() {
return new EmberMMU();
} // createMMU()
//...
} // class EmberToolkit

/**
* This is a class for objects that perform remote tests on Enginola
* architecture CPUs.
*/
class EnginolaCPU extends CPU {
//...
} // class EnginolaCPU

/**
* This is a class for objects that perform remote tests on Enginola
* architecture MMUs.
*/
class EnginolaMMU extends MMU {
//...
} // class EnginolaMMU

/**
* This is a concrete factory class for creating objects that are used to
* perform remote tests on core components of enginola architecture
* computers.
*/
class EnginolaToolkit extends ArchitectureToolkit {

Ivan Mathias Filho 16 PUC-Rio


Introdução aos Padrões de Design

/**
* Method to create objects for remote testing enginola CPUs.
*/
public CPU createCPU() {
return new EnginolaCPU();
} // createCPU()

/**
* Method to create objects for remote testing enginola MMUs.
*/
public MMU createMMU() {
return new EnginolaMMU();
} // createMMU()
//...
} // class EnginolaToolkit

Ivan Mathias Filho 17 PUC-Rio


Introdução aos Padrões de Design

1.3 O Padrão Singleton

1.3.1 Objetivo

Garantir que uma classe tenha somente uma única instância e fornecer um ponto
global de acesso para tal instância.

1.3.2 Contexto

Algumas classes devem possuir exatamente uma instância. Tais classes geralmente
estão envolvidas no gerenciamento de algum recurso, ou controlando alguma atividade
(controller). O recurso pode ser externo, como no caso em que um objeto gerencia a
reutilização de uma conexão com um gerenciador de banco de dados, ou o recurso
pode ser interno, como no caso em que um objeto mantém estatísticas de erro para
um compilador.

1.3.3 Estrutura

O padrão Singleton é relativamente simples, uma vez que ele envolve uma única classe
(Figura 1.6).

A classe unitária possui uma variável estática que mantém uma referência para a única
instância que se deseja manipular. Esta instância é criada quando a classe é carregada
na memória ou quando ocorrer a primeira tentativa de acesso à instância. Devemos
implementar a classe unitária de tal modo que não seja permitida a criação de
instâncias adicionais à única instância permitida. Isto significa que devemos nos
assegurar que todos os construtores da classe unitária sejam declarados como
privados.

Figura 1.6 – Estrutura do Padrão Singleton.

1.3.4 Aplicabilidade

Use o padrão Singleton quando:

Ivan Mathias Filho 18 PUC-Rio


Introdução aos Padrões de Design

• Deve haver apenas uma única instância de uma classe, e essa instância deve poder
ser acessada pelos clientes a partir de um ponto bem conhecido.

• Quando a única instância tiver que ser estendida através de subclasses,


possibilitando aos clientes usar a instância estendida sem alterar os seus
respectivos códigos.

1.3.5 Conseqüências

a. Como a classe Singleton encapsula a sua única instância, ela pode ter o controle
total de como e quando os clientes acessam esta instância.

b. O padrão Singleton representa uma melhoria em relação ao uso de variáveis


globais.

c. A classe Singleton pode ser estendida através de subclasses, sendo bastante


simples configurar uma aplicação para trabalhar com a extensão.

d. A classe Singleton pode ser modificada para suportar um número maior de


instâncias, embora ainda se possa ter o controle do número de instâncias que uma
aplicação vá utilizar.

e. Uma outra maneira de implementar um Singleton é através do uso de métodos


estáticos; entretanto, a utilização desta técnica dificulta a mudança de um projeto
para permitir um número maior de instâncias. Além disso, as funções estáticas não
são polimórficas, o que significa que as subclasses não podem redefini-las
polimorficamente.

1.3.6 Exemplo

Suponha que desejemos escrever uma classe que possa ser usada por uma applet para
garantir que não mais que um áudio clip possa ser executado em um dado momento.
Se uma applet contiver dois trechos de código que reproduzam áudio clips
independentemente, então seria possível para ambas reproduzirem os áudio clips ao
mesmo tempo. Tal ocorrência poderia levar uma condição de erro, ou a uma situação
de grande confusão, com o usuário ouvindo um mix de sons não muito agradável.

Para evitar tal situação é necessário que a classe responsável pela reprodução de
áudio clips interrompa a reprodução corrente antes de começar uma nova. Um meio de
implementar essa política é assegurar que exista uma única instância da classe de
reprodução, e que ela seja compartilhada por todas as classes que desejem reproduzir

Ivan Mathias Filho 19 PUC-Rio


Introdução aos Padrões de Design

áudio clips. Se todas as solicitações para reprodução forem direcionadas para um único
objeto, será fácil para este objeto parar a reprodução de um clip antes de iniciar a
seguinte.

A Figura 1.7 mostra um diagrama com uma classe com as características descritas
acima.

Figura 1.7 – Exemplo do Padrão Singleton.

1.3.7 Código do Exemplo

import java.util.HashSet;

/**
* This class has methods to ensure that an object is never garbage
* collected.
*/
public class ObjectPreserver implements Runnable {
// This keeps this class and everything it references from being
// garbage collected
private static ObjectPreserver lifeLine = new ObjectPreserver();

// Since this class won't be garbage collected, neither will this


// HashSet or the object that it references.
private static HashSet protectedSet = new HashSet();

/**
* Constructor.
*/
private ObjectPreserver() {
new Thread(this).start();
} // constructor()

public void run() {


try {
wait();
} catch (InterruptedException e) {
} // try
} // run()

/**
* Garbage collection of objects passed to this method will be
* prevented until they are passed to the unpreserveObject method.
*/
public static void preserveObject(Object o) {
protectedSet.add(o);
} // preserveObject()

Ivan Mathias Filho 20 PUC-Rio


Introdução aos Padrões de Design

/**
* Objects passed to this method lose the protection that the
* preserveObject method gave them from garbage collection.
*/
public static void unpreserveObject(Object o) {
protectedSet.remove(o);
} // unpreserveObject(Object)
} // class ObjectPreserver

import java.applet.AudioClip;

/**
* This class can be used to avoid playing two audio clips at the same
* time. The class has only one instance that can be accessed through
* its getInstance method. When you play audio clips through that
* object, it stops the last audio clip it was playing before
* it starts the newly requested one. If all audio clips are played
* through the AudioClipManager object then there will never be more
* than one audio clip playing at the same time.
*/
public class AudioClipManager implements AudioClip{
private static AudioClipManager myInstance
= new AudioClipManager();
private AudioClip prevClip; // previously requested audio clip

/**
* This private constructor is defined so the compiler won't
* generate a default public constructor.
*/
private AudioClipManager() { }

/**
* Return a reference to the only instance of this class.
*/
public static AudioClipManager getInstance() {
return myInstance;
} // getInstance()

/**
* Start playing this audio clip. Each time this method is called,
* the clip is restarted from the beginning.
*/
public void play() {
if (prevClip != null)
prevClip.play();
} // play()

/**
* Stop the previously requested audio clip and play the given audio
* clip.
* @param clip the new audio clip to play.
*/
public void play(AudioClip clip) {
if (prevClip != null)
prevClip.stop();
prevClip = clip;
clip.play();
} // play(AudioClip)

/**
* Starts playing this audio clip in a loop.
*/
public void loop() {
if (prevClip != null)
prevClip.loop();
} // loop()

Ivan Mathias Filho 21 PUC-Rio


Introdução aos Padrões de Design

/**
* Stop the previously requested audio clip and play the given audio
* clip in a loop.
* @param clip the new audio clip to play.
*/
public void loop(AudioClip clip) {
if (prevClip != null)
prevClip.stop();
prevClip = clip;
clip.loop();
} // play(AudioClip)

/**
* Stops playing this audio clip.
*/
public void stop() {
if (prevClip != null)
prevClip.stop();
} // stop()
} // class AudioClipManager

Ivan Mathias Filho 22 PUC-Rio


Introdução aos Padrões de Design

1.4 O Padrão Facade

1.4.1 Objetivo

O padrão Facade simplifica o acesso a um conjunto de objetos relacionados utilizando,


para tal, um outro objeto, que é responsável pela comunicação entre os objetos
externos ao conjunto e o conjunto de objetos em questão.

1.4.2 Contexto

Estruturar um sistema em subsistemas ajuda a reduzir a complexidade. Um objetivo


comum a todos os projetos é minimizar a comunicação e as dependências entre os
subsistemas que compõem uma aplicação. Uma maneira de alcançar este objetivo é
introduzir um objeto facade (fachada), o qual fornece uma interface única para os
recursos e facilidades mais gerais de um subsistema.

Figura 1.8 – xxxxxxx Facade.

1.4.3 Estrutura

A Figura 1.9 mostra um diagrama de classes que ilustra a estrutura geral do padrão
Facade.

O objeto Client interage com o objeto Facade, que fornece a funcionalidade necessária
para a interação com o restante dos objetos. Se existir alguma funcionalidade
adicional, necessária apenas para alguns clientes, será melhor então que o objeto

Ivan Mathias Filho 23 PUC-Rio


Introdução aos Padrões de Design

Facade forneça um método para que seja possível acessar diretamente o objeto
responsável por tal funcionalidade, ao invés de incluí-la na interface do objeto Facade.

Figura 1.9 – Estrutura do Padrão Facade.

A seguir é apresentado o papel dos componentes do padrão Facade:

Client – precisa de uma funcionalidade de um dado subsistema mas não deseja estar
a par da complexidade que envolve a execução de tal funcionalidade.

Facade – conhece as classes do subsistema que são responsáveis pelo atendimento


de uma solicitação. Em função disso, delega as solicitações dos clientes aos objetos
apropriados do subsistema.

Classes do Subsistema – implementam as funcionalidades do subsistema. Estas


classes encarregam-se do trabalho que lhes foi atribuído pelo objeto Facade. Como
regra geral, as classes do subsistema não devem manter referências para o objeto
Facade.

1.4.4 Aplicabilidade

Use o padrão Facade quando:

• Você desejar fornecer uma interface simples para um subsistema complexo.

Ivan Mathias Filho 24 PUC-Rio


Introdução aos Padrões de Design

• Existirem muitas dependências entre os clientes e as classes que implementam


uma certa abstração. Ao introduzir uma fachada para reduzir o acoplamento entre
o subsistema e os clientes (ou outros subsistemas), estar-se-á promovendo a
independência e portabilidade dos subsistemas.

• Você desejar estruturar em camadas seus subsistemas. Use uma fachada para
definir o ponto de entrada para cada nível de subsistema.

1.4.5 Conseqüências

a. Isola os clientes dos componentes de um subsistema, reduzindo o número de


objetos com os quais os clientes têm que lidar, e tornando, assim, mais fácil o uso
de tal subsistema.

b. Promove o fraco acoplamento entre um subsistema e os seus clientes. Esta


característica permite variar os componentes de um subsistema sem afetar os seus
clientes.

c. Simplifica o porte de um sistema para outras plataformas, uma vez a sua utilização
diminui a ocorrência de alterações em cascata em função da necessidade de uma
alteração em um certo subsistema.

d. Não impede que as aplicações utilizem diretamente as classes de um subsistema


caso necessitem fazê-lo. Assim, pode-se escolher entre a facilidade de uso e uma
maior flexibilidade na manipulação das funcionalidades fornecidas por um
subsistema.

1.4.6 Exemplo

Considere a organização de um conjunto classes que fornece o serviço de criação e


envio de mensagens de e-mail (Figura 1.10).

Como pode ser visto, trabalhar diretamente com este conjunto adiciona complexidade
à classe Client. Para interagir com estas classes, um cliente tem que conhecer pelo
menos seis delas, os relacionamentos entre elas, e a ordem na qual os objetos são
criados e trocam mensagens entre si. Se todo cliente tiver que lidar com toda essa
complexidade adicional, será difícil a reutilização de tais classes em um outro contexto.
O padrão Facade fornece um meio de proteger um cliente da complexidade de usar
este conjunto de classes. Isto é feito através de um objeto adicional que oculta a maior
parte das complexas interações existentes. Como pode ser visto na Figura 1.10, os

Ivan Mathias Filho 25 PUC-Rio


Introdução aos Padrões de Design

clientes têm que estar a par apenas da existência da classe MessageCreator. Isto é
possível porque a lógica interna da classe MessageCreator é responsável pela criação
das partes de uma mensagem de e-mail em uma determinada ordem.

Figura 1.10 – Exemplo do Padrão Facade.

1.4.7 Código do Exemplo

import java.util.Hashtable;
import java.util.Vector;

/**
* Instances of this class are used to create and send e-mail messages.
* It assumes that an e-mail message consists of a message body and zero or
* more attachments. The content of the message body must be provided as
* either a String object or an object that implements an interface called
* RichText. Any kind of an object can be provided as the content of an
* attachment.
*/
public class MessageCreator {
// Constants to indicate the type of message to create
public final static int MIME = 1;
public final static int MAPI = 2;
public final static int NOTES = 3;
public final static int BANYAN = 4;

private Hashtable headerFields = new Hashtable();


private RichText messageBody;
private Vector attachments = new Vector();
private boolean signMessage;

/**
* Constructor to create a MessageCreator object that will create an
* e-mail message and send it to the given address. It will attempt to
* infer the type of message to create from the "to" address.
* @param to The address that this object will send a message to.

Ivan Mathias Filho 26 PUC-Rio


Introdução aos Padrões de Design

* @param from The address that the message will say it is from.
* @param subject The subject of this message.
*/
public MessageCreator(String to, String from, String subject) {
this(to, from , subject, inferMessageType(to));
} // Constructor(String, String, String)

/**
* Constructor to create a MessageCreator object that will create an
* e-mail message and send it to the given address. It will attempt to
* infer the type of message to create from the "to" address.
* @param to The address that this object will send a message to.
* @param from The address that the message will say it is from.
* @param subject The subject of this message.
* @param type The type of message to create.
*/
public MessageCreator(String to, String from, String subject, int type) {
headerFields.put("to", to);
headerFields.put("from", from);
headerFields.put("subject", subject);
//...
} // Constructor(String, String, String, int)

/**
* Set the contents of the message body.
* @param messageBody The contents of the message body.
*/
public void setMessageBody(String messageBody) {
setMessageBody(new RichTextString(messageBody));
} // setMessageBody(String)

/**
* Set the contents of the contents body.
* @param messageBody The contents of the message body.
*/
public void setMessageBody(RichText messageBody) {
this.messageBody = messageBody;
} // setMessageBody(RichText)

/**
* Add an attachement to the message
* @param attachment the object to attach to the message
*/
public void addAttachment(Object attachment) {
attachments.addElement(attachment);
} // addAttachment(Object)

/**
* set whether this message should be signed. The default is false.
*/
public void setSignMessage(boolean signFlag) {
signMessage = signFlag;
} // setSignMessage(boolean)

/**
* Set the value of a header field.
* @param name The name of the field to set the value of
* @param value The value to set the field to.
*/
public void setHeaderField(String name, String value) {
headerFields.put(name.toLowerCase(), value);
} // setHeaderField(String, String)

/**
* Send the message.
*/
public void send() {
MessageBody body = new MessageBody(messageBody);

Ivan Mathias Filho 27 PUC-Rio


Introdução aos Padrões de Design

for (int i = 0; i < attachments.size(); i++) {


body.addAttachment(new Attachment(attachments.elementAt(i)));
} // for
MessageHeader header = new MessageHeader(headerFields);
Message msg = new Message(header, body);
if (signMessage) {
msg.setSecurity(createSecurity());
} // if
createMessageSender(msg);
} // send()

/**
* Infer an message type from a destination e-mail address.
* @param address an e-mail address.
*/
private static int inferMessageType(String address) {
int type = 0;
//...
return type;
} // inferMessageType(String)

/**
* Create a Security object appropriate for signing this message.
*/
private Security createSecurity() {
Security s = null;
//...
return s;
} // createSecurity()

/**
* Create a MessageSender object appropriate for the type of
* message being sent.
*/
private void createMessageSender(Message msg) {
//...
} // createMessageSender(Message)
//...
} // class MessageCreator

******************************************************************************

/**
* Instances of this class encapsulate message attachments
*/
class Attachment {
/**
* Constructor
* @param content An object that supplies the content for this
* attachment.
*/
Attachment(Object content) {
//...
} // Constructor(Object)
//...
} // class Attachment

******************************************************************************

/**
* Instances of this class encapsulate e-mail messages.
*/
class Message {
/**
* Constructor
* @param header The message header.
* @param body The message body.
*/

Ivan Mathias Filho 28 PUC-Rio


Introdução aos Padrões de Design

Message(MessageHeader header, MessageBody body) {

//...
} // constructor(MessageHeader, MessageBody)

/**
* Set the Security object that this object will use to sign itself.
*/
void setSecurity(Security s) {
//...
} // setSecurity(Security)
//...
} // class Message

******************************************************************************

/**
* Instances of this class encapsulate the messge body for an e-mail
* message.
*/
class MessageBody {
/**
* Constructor
* @param body the content of the message body
*/
MessageBody(RichText body) {
//...
} // Constructor(RichText)

/**
* Add an attachment to this message body.
* @param attachment The object to add to this message.
*/
void addAttachment(Attachment attachment) {
//...
} // addAttachment(Attachment)
} // class MessageBody

******************************************************************************

import java.util.Hashtable;

/**
* Instances of this class encapsulate header information.
*/
class MessageHeader {
/**
* constructor
* @param fields A Hashtable that contains the field values for this
* header.

*/
MessageHeader(Hashtable fields) {
//...
} // constructor(Hashtable)
//...
} // class MessageHeader

******************************************************************************

/**
* Instances of this class are responsible for sending e-mail
* messages on their way.
*/
class MessageSender {
//...
} // class MessageSender

Ivan Mathias Filho 29 PUC-Rio


Introdução aos Padrões de Design

******************************************************************************

/**
* The contents of message bodies must either come from a String object or an
* object the implements this interface.
*/
public interface RichText {
//...
} // interface RichText

******************************************************************************

/**
* Instances of this class encapsulate a string for inclusion in message
* bodies.
*/
class RichTextString implements RichText {
private String text;

/**
* Constructor
* @param text The string that this object adapts to the RichText
* interface.
*/
public RichTextString(String text) {
this.text = text;
//...
} // constructor(String)
//...
} // class RichTextString

******************************************************************************

/**
* Instances of this class encapsulate an algorithm and information
* for signing an e-mail message.
*/
class Security {
//...
} // class Security

Ivan Mathias Filho 30 PUC-Rio


Introdução aos Padrões de Design

1.5 O Padrão Composite

1.5.1 Objetivo

1.5.2 Contexto

Suponha que precisemos escrever um programa para a formatação de documentos.


Ele deverá formatar caracteres de linhas de texto, que são organizadas em colunas,
que são organizadas em páginas. Além disso, tal documento pode conter ainda outros
elementos. Colunas e páginas podem conter frames, que por sua vez podem conter
colunas. Colunas, frames e linhas de texto podem conter imagens.

Como podemos ver, existe muita complexidade em uma aplicação dessa natureza.
Objetos que representam páginas e frames têm que saber como manipular e combinar
diversos tipos de componentes. O padrão Composite procura remover tal complexidade
fazendo com que estes objetos compostos precisem saber apenas como gerenciar um
único tipo de elemento. Isso é alcançado fazendo com que todos os elementos de um
documento composto tenham uma superclasse comum.

1.5.3 Estrutura

O relacionamento entre as classes que representam a estrutura do padrão Composite


pode ser visto na Figura 1.11.

Ivan Mathias Filho 31 PUC-Rio


Introdução aos Padrões de Design

Figura 1.11 – Estrutura do Padrão Composite.

A seguir é apresentada a descrição das classes que participam da estrutura recursiva


do padrão Composite:

AbstractComponent – superclasse abstrata comum a todas a classes que definem os


objetos que pertencem à árvore de objetos que participam de um objeto composto. Os
objetos compostos tratam normalmente os seus componentes como sendo instâncias
da classe AbstractComponent.

ConcreteComponentX – classes que definem os objetos que representam as folhas


na árvore de componentes de um objeto composto.

AbstractComposite – superclasse abstrata comum a todos os objetos compostos que


participam do padrão Composite. Esta classe declara e fornece implementações default
para todos os métodos usados no gerenciamento dos componentes de um objeto
composto.

ConcreteCompositeX – as instâncias destas classes são os objetos compostos que


têm como componentes os objetos das outras subclasses de AbstractComponent.

1.5.4 Aplicabilidade

xxxxxxxxx:

• yyyy

Ivan Mathias Filho 32 PUC-Rio


Introdução aos Padrões de Design

• zzzzz.

1.5.5 Conseqüências

f. Zzzzzzzzzzzz.

1.5.6 Exemplo

O exemplo de aplicação do padrão Composite é uma versão mais detalhada do


formatador de documentos apresentado na seção de Contexto. A Figura 1.12 mostra o
diagrama de classes detalhado do problema em questão.

Figura 1.12 – Exemplo do Padrão Composite.

1.5.7 Código do Exemplo

import java.util.Vector;
import java.awt.Font;

/**
* Instances of this class represent a page.
*/
class Page extends CompositeDocumentElement {
//...
} // class Page

/**
* Instances of this class represent a character in a document.
*/
class Character extends DocumentElement {

Ivan Mathias Filho 33 PUC-Rio


Introdução aos Padrões de Design

//...
/**
* Return the number of characters that this object contains.
*/
public int getCharLength() {
return 1;
} // getCharLength()
} // class Character

/**
* Instances of this class represent a column.
*/
class Column extends CompositeDocumentElement {
//...
} // class Column

/**
* Instances of this class are composite objects that contain
* DocumentElement objects.
*/
abstract class CompositeDocumentElement extends DocumentElement {
// Collection of this object's children
private Vector children = new Vector();

// The cached value from the previous call to getCharLength or -1 to


// indicate that charLength does not contain a cached value.
private int cachedCharLength = -1;

/**
* Return the child object of this object that is at the given
* position.
* @param index The index of the child.
*/
public DocumentElement getChild(int index) {
return (DocumentElement)children.elementAt(index);
} // getChild(int)

/**
* Make the given DocumentElement a child of this object.
*/
public synchronized void addChild(DocumentElement child) {
synchronized (child) {
children.addElement(child);
child.parent = this;
changeNotification();
} // synchronized
} // addChild(DocumentElement)

/**
* Make the given DocumentElement NOT a child of this object.
*/
public synchronized void removeChild(DocumentElement child) {
synchronized (child) {
if (this == child.parent)
child.parent = null;
children.removeElement(child);
changeNotification();
} // synchronized
} // removeChild(DocumentElement)

//...

/**
* A call to this method means that one of this object's children
* has changed in a way that invalidates whatever data this object
* may be cahcing about its children.
*/
public void changeNotification() {

Ivan Mathias Filho 34 PUC-Rio


Introdução aos Padrões de Design

cachedCharLength = -1;
if (parent != null)
parent.changeNotification();
} // changeNotification()

/**
* Return the number of characters that this object contains.
*/
public int getCharLength() {
int len = 0;
for (int i = 0; i < children.size(); i++) {
len += ((DocumentElement)children.elementAt(i)).getCharLength();
} // for
cachedCharLength = len;
return len;
} // getCharLength()
} // class CompositeDocumentElement

/**
* Instances of this class represent a document.
*/
class Document extends CompositeDocumentElement {
//...
} // class Document

/**
* All elements of a document belong to a subclass of this abstract class.
*/
abstract class DocumentElement {
// This is the font associated with this object. If the font
// variable is null, then this object's font will be inherited
// through the container hierarchy from an enclosing object.
private Font font;

CompositeDocumentElement parent; // this object's container

//...

/**
* Return this object's parent or null if it has no parent.
*/
public CompositeDocumentElement getParent() {
return parent;
} // getParent()

/**
* Return the Font associatiated with this object. If there is no
* Font associated with this object, then return the Font associated
* with this object's parent. If there is no Font associated
* with this object's parent the return null.
*/
public Font getFont() {
if (font != null)
return font;
else if (parent != null)
return parent.getFont();
else
return null;
} // getFont()

/**
* Associate a Font with this object.
* @param font The font to associate with this object
*/
public void setFont(Font font) {
this.font = font;
} // setFont(Font)

Ivan Mathias Filho 35 PUC-Rio


Introdução aos Padrões de Design

/**
* Return the number of characters that this object contains.
*/
public abstract int getCharLength() ;
} // class DocumentElement
/**

* Instances of this class represent a Frame.


*/
class Frame extends CompositeDocumentElement {
//...
} // class Frame

/**
* Instances of this class represent a image in a document.
*/
class Image extends DocumentElement {
//...
/**
* Return the number of characters that this object contains.
* Though images don't really contain any characters, for the sake of
* consistenecy, we will treat an image as if it is a character.
*/
public int getCharLength() {
return 1;
} // getCharLength()
} // class Image

/**
* Instances of this class represent a line of text.
*/
class LineOfText extends CompositeDocumentElement {
//...
} // class LineOfText

Ivan Mathias Filho 36 PUC-Rio


Introdução aos Padrões de Design

1.6 O Padrão Observer

1.6.1 Objetivo

1.6.2 Contexto

1.6.3 Estrutura

Figura 1.13 – Estrutura do Padrão Observer.

Ivan Mathias Filho 37 PUC-Rio


Introdução aos Padrões de Design

Figura 1.14 – Adição de Um Novo Observador.

Figura 1.15 – Notificação de Um Evento.

1.6.4 Aplicabilidade

xxxxxxxxx:

• yyyy

• zzzzz.

1.6.5 Conseqüências

g. Zzzzzzzzzzzz.

1.6.6 Exemplo

Ivan Mathias Filho 38 PUC-Rio


Introdução aos Padrões de Design

Figura 1.16 – Exemplo do Padrão Observer.

1.6.7 Código do Exemplo

import java.util.ArraySet;
import java.util.Iterator;

/**
* Classes that implement this interface can register to receive
* security notifications from SecurityNotifier objects.
*/
public interface SecurityObserver {
public final int ALARM = 1;
public final int LOW_POWER = 2;
public final int DIAGNOSTIC = 3;

/**
* This is method is called to deliver a security notification to
* this object.
* @param device A number that identifies the device that originated
* this notification.
* @param event This should be one of the constants defined in this
* interface.
*/
public void notify(int device, int event);
} // interface SecurityObserver

/**
* Instances of this class receive a notification from an object that is
* can only deliver it to an object the implements the SecurityObserver
* interface and apsses it on to a SecurityMonitor object that does not
* implement SecurityObserver.

*/
class SecurityAdapter implements SecurityObserver {
private SecurityMonitor sm;

/**
* Constructor
*/
SecurityAdapter(SecurityMonitor sm) {
this.sm = sm;

Ivan Mathias Filho 39 PUC-Rio


Introdução aos Padrões de Design

} // Constructor(SecurityMonitor)

/**
* This is method is called to deliver a security notification to
* this object.
* @param device A number that identifies the device that originated
* this notification.
* @param event This should be one of the constants defined in this
* interface.
*/
public void notify(int device, int event) {
switch (event) {
case ALARM:
sm.securityAlert(device);
break;

case LOW_POWER:
case DIAGNOSTIC:
sm.diagnosticAlert(device);
break;
} // switch
} // notify(int, int)
} // class SecurityAdapter

/**
* Skeletal definition for a class that monitors security devices.
*/
public class SecurityMonitor {
//...
public void securityAlert(int device) {
//...
} // securityAlert(int)

public void diagnosticAlert(int device) {


} // diagnosticAlert(int)
} // SecurityMonitor

/**
* When an instance of this class receives a notification from a
* security device, it passes it on to all of its registered observers.
*/
class SecurityNotifier {
private ArraySet observers = new ArraySet();
//...
/**
* Add a new observer to this object.
*/
public void addObserver(SecurityObserver observer) {
observers.add(observer);
} // addObserver(SecurityObserver)

/**
* Remove an observer from this object
*/
public void removeObserver(SecurityObserver observer) {
observers.remove(observer);
} // removeObserver(SecurityObserver)

/**
* This method is called when this object needs to pass on a

* notification to its registered observers.


* @param device A number that identifies the device that originated
* this notification.
* @param event This should be one of the constants defined in this
* interface.
*/
private void notify(int device, int event) {

Ivan Mathias Filho 40 PUC-Rio


Introdução aos Padrões de Design

Iterator iterator = observers.iterator();


while (iterator.hasNext()) {
((SecurityObserver)iterator.next()).notify(device, event);
} // while
} // notify(int, int)
} // class SecurityNotifier

Ivan Mathias Filho 41 PUC-Rio


Introdução aos Padrões de Design

1.7 O Padrão Strategy

1.7.1 Objetivo

Encapsular algoritmos relacionados em um conjunto de classes, que são subclasses de


uma superclasse comum, de tal maneira que a escolha do algoritmo desejado possa
ser feita dinamicamente.

1.7.2 Contexto

Existem muitos algoritmos para quebrar um stream de texto em linhas. Codificar de


maneira fixa e rígida tais algoritmos nas classes que os utilizam não é desejável, por
várias razões:

• Os clientes que necessitam das quebras de linhas se tornam mais complexos se


incluem o código de quebra de linhas. Isso os torna maiores e mais difíceis de
manter, especialmente se suportam múltiplos algoritmos de quebra de linhas.

• Diferentes algoritmos serão apropriados em diferentes situações. Não seria uma


boa estratégia suportar múltiplos algoritmos de quebra de linha se não for
necessário usá-los.

• É difícil adicionar novos algoritmos e variar os existentes quando a quebra de linha


é parte integrante de um cliente.

Podemos evitar estes problemas definindo classes que encapsulam diferentes


algoritmos de quebra de linhas. Um algoritmo encapsulado desta maneira é chamado
de strategy (estratégia).

1.7.3 Estrutura

A Figura 1.17 mostra um diagrama de classes com os principais componentes do


padrão Strategy.

Ivan Mathias Filho 42 PUC-Rio


Introdução aos Padrões de Design

Figura 1.17 – Estrutura do Padrão Strategy.

A seguir é apresentado um resumo do papel que cada classe exerce no padrão em


questão:

Client – uma classe Client delega a execução de uma operação a uma classe abstrata
ou a uma interface. Ela o faz sem conhecer a classe do objeto ao qual a operação é
delegada, e como tal classe implementa a operação solicitada.

AbstractStrategy – fornece uma interface comum para acessar as operações


definidas pelas suas subclasses. Pode-se usar uma interface, ao invés de uma classe
abstrata, para exercer este papel.

ConcreteStrateyX – definem os diferentes métodos que implementam as diferentes


estratégias existentes para a operação solicitada pelo cliente.

1.7.4 Aplicabilidade

Use o padrão Strategy quando:

• Muitas classes relacionadas diferem somente no seu comportamento. As


estratégias fornecem uma maneira de configurar uma classe com um, dentre
muitos comportamentos.

• Você necessita de variantes de um algoritmo. Por exemplo, podemos definir


algoritmos que reflitam diferentes soluções de compromisso entre espaço/tempo.

• Um algoritmo usa dados que os clientes deveriam desconhecer. Use o padrão


Strategy para evitar a exposição das estruturas de dados complexas, específicas do
algoritmo.

Ivan Mathias Filho 43 PUC-Rio


Introdução aos Padrões de Design

• Uma classe define muitos comportamentos, e estes aparecem em suas operações


como múltiplos comandos condicionais.

1.7.5 Conseqüências

a. Podemos usar este padrão para criar diferentes hierarquias de estratégias, que irão
definir famílias de algoritmos e comportamentos relacionados.

b. Uma alternativa ao uso do padrão Strategy seria especializar a classe Client para
lhe dar diferentes comportamentos. Entretanto, isso congelaria o comportamento
do cliente, misturando a implementação do algoritmo em questão com o resto do
código da classe Client, e tornando esta classe mais difícil de compreender, manter
e estender. Além disso, teríamos a desvantagem de não poder variar os algoritmos
dinamicamente. A solução dada pelo padrão Strategy permite variar o algoritmo
independentemente dos seus clientes, tornando mais fácil trocá-los, compreendê-
los e estendê-los.

c. Uma possível desvantagem do padrão Strategy estaria no fato de que um cliente


pode ser obrigado, em algumas situações, a conhecer a maneira pela qual as
estratégias diferem uma das outras. Isso poderia levar a uma situação indesejável,
onde os clientes teriam que estar expostos aos detalhes de implementação das
diferentes estratégias.

1.7.6 Exemplo

Suponha que precisemos escrever um programa para exibir calendários. Um dos


requisitos deste programa é estar apto a exibir os feriados celebrados por diferentes
nações e diferentes grupos religiosos. O usuário deste programa deve poder escolher
quais grupos de feriados devem ser exibidos.

Seria interessante atender este requisito colocando a lógica da aplicação relativa a


cada grupo de feriados em uma classe separada, de tal maneira que tenhamos um
pequeno conjunto de classes ao qual podemos facilmente adicionar novos elementos.
Gostaríamos também que as classes que fossem utilizar os serviços para exibição dos
calendários não precisassem estar a par da existência de nenhum feriado, ou conjunto
de feriados, específico.

A Figura 1.18 mostra como utilizar o padrão Strategy para implementar uma solução
para o problema descrito acima.

Ivan Mathias Filho 44 PUC-Rio


Introdução aos Padrões de Design

Figura 1.18 – Exemplo do Padrão Strategy.

1.7.7 Código do Exemplo

import java.util.Date;

/**
* Skeletal definition of class to display a calendar
*/
class CalendarDisplay {
private Holiday holiday;
private static final String[]noHoliday = new String[0];
//...
/**
* Instances of this private class are used to cache information about
* dates that are to be displayed.
*/
private class DateCache {
private Date date;
private String[] holidayStrings;

DateCache(Date dt) {
date = dt;
//...
if (holiday == null) {
holidayStrings = noHoliday;
} else {
holidayStrings = holiday.getHolidays(date);
} // if
//...
} // constructor(Date)
} // class DateCache
} // class CalendarDisplay

******************************************************************************

import java.util.Date;
/**
* This abstract class is the superclass of classes that can determine
* if a date is a holiday. Subclasses of this class will be specific to
* nations or religions.
*/
public abstract class Holiday {
protected final static String[] noHoliday = new String[0];
/**
* This method returns a array of strings that describe the holidays
* that fall on the given date. If no holidays fall on the given
* date, then this method returns an array of length zero.
* @param dt The date to check.
*/

Ivan Mathias Filho 45 PUC-Rio


Introdução aos Padrões de Design

abstract public String[] getHolidays(Date dt) ;


} // class Holiday

******************************************************************************

import java.util.Date;
/**
* This class determines if a particular date is a U.S. holiday.
*/
public class USHoliday extends Holiday {
/**
* This method returns a array of strings that describe the holidays
* that fall on the given date. If no holidays fall on the given
* date, then this method returns an array of length zero.
* @param dt The date to check.
*/
public String[] getHolidays(Date dt) {
String[] holidays = noHoliday;
//...
return holidays;
} // getHolidays(Date)
} // class USHoliday

******************************************************************************

import java.util.Date;
/**
* This class determines if a particular date is a according to a
* collection of Holiday objects.
*/
public class CompositeHoliday extends Holiday {
private Holiday[] holidayArray;

/**
* Constructor
* @param h An array of Holiday objects
*/
public CompositeHoliday(Holiday[] h) {
holidayArray = new Holiday[h.length];
System.arraycopy(h, 0, holidayArray, 0, h.length);
} // CompositeHoliday

/**
* This method returns a array of strings that describe the holidays
* that fall on the given date. If no holidays fall on the given
* date, then this method returns an array of length zero.
* @param dt The date to check.
*/
public String[] getHolidays(Date dt) {
return getHolidays0(dt, 0, 0);
} // getHolidays(Date)

private String[] getHolidays0(Date dt, int offset, int ndx) {


if (ndx >= holidayArray.length) {
return new String[offset];
} // if
String[] holidays = holidayArray[ndx].getHolidays(dt);
String[] result = getHolidays0(dt, offset+holidays.length, ndx+1);
System.arraycopy(holidays, 0, result, offset, holidays.length);
return result;
} // getHolidays0(Date, int, int)
} // class USHoliday

Ivan Mathias Filho 46 PUC-Rio


Introdução aos Padrões de Design

1.8 O Padrão State

1.8.1 Objetivo

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

1.8.2 Contexto

Um objeto possui um conjunto de atributos, chamados conjuntamente de estado do


objeto, cujos valores podem mudar de valor dinamicamente. Existem muitos
algoritmos para quebrar um stream de texto em linhas. Codificar de maneira fixa e
rígida tais algoritmos nas classes que os utilizam não é desejável, por várias razões:

• Os clientes que necessitam das quebras de linhas se tornam mais complexos se


incluem o código de quebra de linhas. Isso os torna maiores e mais difíceis de
manter, especialmente se suportam múltiplos algoritmos de quebra de linhas.

• Diferentes algoritmos serão apropriados em diferentes situações. Não seria uma


boa estratégia suportar múltiplos algoritmos de quebra de linha se não for
necessário usá-los.

• É difícil adicionar novos algoritmos e variar os existentes quando a quebra de linha


é parte integrante de um cliente.

Podemos evitar estes problemas definindo classes que encapsulam diferentes


algoritmos de quebra de linhas. Um algoritmo encapsulado desta maneira é chamado
de strategy (estratégia).

1.8.3 Estrutura

A Figura 1.19 mostra um diagrama de classes com os principais componentes do


padrão State.

Ivan Mathias Filho 47 PUC-Rio


Introdução aos Padrões de Design

Figura 1.19 – Estrutura do Padrão State.

A seguir é apresentado um resumo do papel que cada classe exerce no padrão em


questão:

Context – classe cujas instâncias exibem um comportamento que deve ser modelado
por uma máquina de estados. As instâncias desta classe determinam os seus
respectivos estados correntes através de uma referência (currentState) para uma
instância de uma subclasse concreta da classe ContextState.

ContextState – esta classe é a superclasse de todas as classes usadas para


representar os estados de uma instância da classe Context. A classe ContextState
define os seguintes métodos:

• O método start executa todas as tarefas necessárias para a inicialização da


máquina de estados de um objeto, e retorna um objeto que corresponde ao
estado inicial do objeto em questão.

• O método processEvent é um método abstrato que recebe como argumento


um evento e retorna o novo estado corrente do objeto em questão. Cada
subclasse concreta de ContextState deve redefinir este método de acordo
com as necessidades do estado que ela representa. Quaisquer ações de saída
(exit actions) relativas ao estado corrente devem ser executadas neste
método (desde que o evento tenha causado uma transição de saída do
estado corrente).

Ivan Mathias Filho 48 PUC-Rio


Introdução aos Padrões de Design

• O método enter é responsável pela execução das ações de entrada (entry


actions) de um estado. A implementação default, fornecida pela classe
ContextState, não contém comandos (vazia), e deve ser redefinida pelas
subclasses que representam estados que contenham ações de entrada.

• Os métodos operation1, operation2, ...., implementam as operações


específicas de cada um dos estados.

ConcreteStateX – subclasses concretas de ContextState, que representam os estados


existentes em uma máquina de estados. Estas classes têm que implementar o método
processEvent para fornecerem respostas adequadas aos eventos ocorridos.

1.8.4 Aplicabilidade

Use o padrão State quando:

• O comportamento de um objeto depender do seu estado e ele tiver que mudar de


comportamento em tempo de execução em função de uma mudança no seu
estado.

• As operações contiverem grandes estruturas condicionais, com muitas alternativas.


Freqüentemente, tais estruturas irão conter grandes porções de código que irão se
repetir através de várias operações, gerando replicação desnecessária de código. O
padrão State coloca cada ramo da estrutura condicional em uma classe separada,
permitindo, assim, tratar os estados como se fossem objetos e tornar mais simples
a implementação de uma máquina de estados.

1.8.5 Conseqüências

a. O padrão State implementa uma máquina de estados particionando os


comportamentos relativos aos estados e organizando tais comportamentos em
objetos específicos. Desse modo, novos estados e transições podem ser facilmente
adicionados à máquina de estados através da definição de novas subclasses.

b. Quando um objeto define o seu estado corrente unicamente em termos dos valores
dos seus atributos, a suas transições de estado não têm representação explícita;
elas ficam caracterizadas apenas pela mudança de valores de alguns atributos. A
introdução de objetos distintos para representar os diferentes estados de máquina
de estados torna as transições mais explícitas.

Ivan Mathias Filho 49 PUC-Rio


Introdução aos Padrões de Design

1.8.6 Exemplo

Suponha que estejamos escrevendo um editor de parâmetros de programas. Uma


caixa de diálogo para esta aplicação terá botões que irão representar as seguintes
ações:

• Um botão de OK irá salvar os valores dos parâmetros informados em um


arquivo e na área de trabalho do programa.

• Um botão de Save irá apenas salvar os valores dos parâmetros informados em


um arquivo.

• Um botão de Apply irá apenas salvar os valores dos parâmetros informados


na área de trabalho do programa.

• Um botão de Revert irá restaurar os valores dos parâmetros a partir do


arquivo.

É possível projetar este diálogo de modo que ele não tenha estados. Se um diálogo
não contém estados, ele irá se comportar sempre da mesma maneira. O botão de OK
estará sempre ativado, mesmo se o usuário tiver recém restaurado os valores dos
parâmetros a partir do arquivo. Se não houver outras considerações, o uso de uma
abordagem sem estados no design deste diálogo pode ser satisfatório.

Em alguns casos, entretanto, o comportamento descrito acima pode causar alguns


problemas. A alteração dos valores dos parâmetros na área de trabalho do programa
poderá interromper o funcionamento do mesmo. O armazenamento dos valores em um
arquivo pode tomar um tempo consideravelmente longo se ele estiver localizado em
um servidor remoto. Um de evitar operações desnecessárias é inserir estados no
diálogo de tal maneira que as operações não possam ser executadas quando não
forem necessárias. Ao invés disso, o diálogo só irá permitir a execução destas
operações quando os valores dos parâmetros forem diferentes dos valores
armazenados no arquivo, ou dos valores existentes na área de trabalho do programa.
A Figura 1.20 mostra um diagrama de estados que representa o comportamento
desejado.

Ivan Mathias Filho 50 PUC-Rio


Introdução aos Padrões de Design

Figura 1.20 – Máquina de Estados do Editor de Parâmetros.

Para implementar a máquina de estados da Figura 1.20, iremos criar cinco classes,
como é mostrado na Figura 1.21. Quatro delas irão corresponder a cada um dos
quatro estados mostrado na máquina de estados da Figura 1.20, e a quinta será a
superclasse comum às quatro primeiras.

A superclasse DirtyState possui um método público chamado processEvent. Este


método recebe como parâmetro um identificador de evento e retorna um objeto que
representa o estado seguinte. Cada subclasse de DirtyState irá redefinir o método
processEvent de acordo com as suas necessidades. A classe DirtyState possui também
um método estático chamado start. Este método irá por as coisas para funcionar
criando uma instância para cada subclasse de DirtyState e retornando o objeto que
representa o estado inicial. O método start cria também uma instância de DirtyState e
guarda nas variáveis notDirty, fileDirty, paramDirty e bothDirty as referências para os
objetos que representam os estados.

Ivan Mathias Filho 51 PUC-Rio


Introdução aos Padrões de Design

Figura 1.21 – A Representação dos Estados Através de uma Hierarquia de Classes.

1.8.7 Código do Exemplo

import java.awt.*;

class DirtyState {
// Symbolic constants for events
public static final int DIRTY_EVENT = 1;
public static final int APPLY_EVENT = 2;
public static final int SAVE_EVENT = 3;
public static final int REVERT_EVENT = 4;

// Symbolic constants for states


private final BothDirty bothDirty = new BothDirty();
private final FileDirty fileDirty = new FileDirty();
private final ParamDirty paramDirty = new ParamDirty();
private final NotDirty notDirty = new NotDirty();

private Parameters parameters;


private Button apply, save, revert;

/**
* This constructor should be private to prevent other classes from
* instantiating this one. It is not private because subclasses of
* this class are implemented as inner classes of this class and Java
* 1.2 does not support access of a private constructor by inner classes.
*/
DirtyState() {
} // constructor()

/**
* Initialize the state machine and return its initial state.
* @param p The parameters object that this object will work with
* @param apply The apply button to be enabled/disabled
* @param save The save button to be enabled/disabled
* @param revert The revert button to be enabled/disabled
*/
public static DirtyState start(Parameters p,
Button apply, Button save, Button revert){
DirtyState d = new DirtyState();
d.parameters = p;
d.apply = apply;
d.save = save;
d.revert= revert;

Ivan Mathias Filho 52 PUC-Rio


Introdução aos Padrões de Design

return d.notDirty;
} // start(Button, Button, Button)

/**
* Respond to a given event.
* All subclasses of this class are expected to override this method.
* @param event An event code.
* @return the next state.
* @exception IllegalArgumentException if event is an unexpected value.
*/
public DirtyState processEvent(int event) {
// This non-overridden method should never be called.
throw new IllegalAccessError();
} // processEvent(int)

/**
* This method is called when this object is becomes the current state.
*/
protected void enter() { }

******************************************************************************

/**
* class to represent state for when the fields of the dialog do not match
* the file or the working parameter values.
*/
private class BothDirty extends DirtyState {
/**
* Respond to a given event.
* @param event An event code.
* @return the next state.
* @exception IllegalArgumentException if event is an unexpected
value.
*/
public DirtyState processEvent(int event) {
switch (event) {
case DIRTY_EVENT:
return this;
case APPLY_EVENT:
if (parameters.applyParam()) {
fileDirty.enter();
return fileDirty;
} // if
case SAVE_EVENT:

if (parameters.saveParam()) {
paramDirty.enter();
return paramDirty;
} // if
case REVERT_EVENT:
if (parameters.revertParam()) {
paramDirty.enter();
return paramDirty;
} // if
default:
String msg = "unexpected event "+event;
throw new IllegalArgumentException(msg);
} // switch (event)
} // processDirtyStateEvent(int)

/**
* This method is called when this object is becomes the current
state.
*/
protected void enter() {
apply.setEnabled(true);
revert.setEnabled(true);
save.setEnabled(true);

Ivan Mathias Filho 53 PUC-Rio


Introdução aos Padrões de Design

} // enter
} // class BothDirty

******************************************************************************

/**
* class to represent state for when the fields of the dialog match
* the working parameter values but not the file.
*/
private class FileDirty extends DirtyState {
/**
* Respond to a given event.
* @param event An event code.
* @return the next state.
* @exception IllegalArgumentException if event is an unexpected
value.
*/
public DirtyState processEvent(int event) {
switch (event) {
case DIRTY_EVENT:
bothDirty.enter();
return bothDirty;
case SAVE_EVENT:
if (parameters.saveParam()) {
notDirty.enter();
return notDirty;
} // if
case REVERT_EVENT:
if (parameters.revertParam()) {
paramDirty.enter();
return paramDirty;
} // if
default:
String msg = "unexpected event "+event;
throw new IllegalArgumentException(msg);
} // switch (event)
} // processDirtyStateEvent(int)

/**
* This method is called when this object is becomes the current
state.
*/
protected void enter() {
apply.setEnabled(false);
revert.setEnabled(true);
save.setEnabled(true);
} // enter
} // class FileDirty

******************************************************************************

/**
* class to represent state for when the fields of the dialog match
* the file but not the working parameter values.
*/
private class ParamDirty extends DirtyState {
/**
* Respond to a given event.
* @param event An event code.
* @return the next state.
* @exception IllegalArgumentException if event is an unexpected
value.
*/
public DirtyState processEvent(int event) {
switch (event) {
case DIRTY_EVENT:
bothDirty.enter();

Ivan Mathias Filho 54 PUC-Rio


Introdução aos Padrões de Design

return bothDirty;

case APPLY_EVENT:
if (parameters.applyParam()) {
notDirty.enter();
return notDirty;
} // if
default:
String msg = "unexpected event "+event;
throw new IllegalArgumentException(msg);
} // switch (event)
} // processDirtyStateEvent(int)

/**
* This method is called when this object is becomes the current
state.
*/
protected void enter() {
apply.setEnabled(true);
revert.setEnabled(false);
save.setEnabled(false);
} // enter
} // class ParamDirty

******************************************************************************

/**
* class to represent state for when the fields of the dialog match
* the file and the working parameter values.
*/
private class NotDirty extends DirtyState {
/**
* Respond to a given event.
* @param event An event code.
* @return the next state.
* @exception IllegalArgumentException if event is an unexpected
value.
*/
public DirtyState processEvent(int event) {
switch (event) {
case DIRTY_EVENT:
bothDirty.enter();
return bothDirty;
default:
String msg = "unexpected event "+event;
throw new IllegalArgumentException(msg);
} // switch (event)
} // processDirtyStateEvent(int)

/**
* This method is called when this object is becomes the current
state.
*/
protected void enter() {
apply.setEnabled(false);
revert.setEnabled(false);
save.setEnabled(false);
} // enter
} // class ParamDirty
} // class DirtyState

******************************************************************************

class Parameters {
//...
boolean saveParam() {
//...
return true;

Ivan Mathias Filho 55 PUC-Rio


Introdução aos Padrões de Design

} // saveParam()

boolean applyParam() {
//...
return true;
} // applyParam()

boolean revertParam() {
//...
return true;
} // revertParam()
} // class Parameters

******************************************************************************

import java.awt.*;

class Procedural extends Dialog {


// Symbolic constants for events
public static final int DIRTY_EVENT = 1;
public static final int APPLY_EVENT = 2;
public static final int SAVE_EVENT = 3;
public static final int REVERT_EVENT = 4;

// Symbolic constants for states


private static final int BOTH_DIRTY = 101;
private static final int FILE_DIRTY = 102;
private static final int PARAM_DIRTY = 103;
private static final int NOT_DIRTY = 104;

Button applyButton, saveButton, revertButton;

private int state = NOT_DIRTY;

/**
* Constructor
* @param parent The parent Frame
*/
Procedural(Frame parent) {
super(parent, "Parameter Editor");
//...
gotoState(NOT_DIRTY);
} // Constructor()

/**
* respond to events based on the current state.
* @param event An event code.
* @exception IllegalArgumentException if event is an unexpected value.
* @exception InternalError if the current state is corrupted.
*/
private void processDirtyStateEvent(int event) {
switch (state) {
case BOTH_DIRTY:
switch (event) {
case DIRTY_EVENT:
// Do nothing
break;
case APPLY_EVENT:
if (applyParam())
gotoState(FILE_DIRTY);
break;
case SAVE_EVENT:
if (saveParam())
gotoState(PARAM_DIRTY);
break;
case REVERT_EVENT:
if (revertParam())
gotoState(PARAM_DIRTY);

Ivan Mathias Filho 56 PUC-Rio


Introdução aos Padrões de Design

break;
default:
throw new IllegalArgumentException("unexpected event
"+event);
} // switch (event)
break;
case FILE_DIRTY:
switch (event) {
case DIRTY_EVENT:
gotoState(BOTH_DIRTY);
break;
case SAVE_EVENT:
if (saveParam())
gotoState(NOT_DIRTY);
break;
case REVERT_EVENT:
if (revertParam())
gotoState(PARAM_DIRTY);
break;
default:
throw new IllegalArgumentException("unexpected event
"+event);
} // switch (event)
break;
case PARAM_DIRTY:
switch (event) {
case DIRTY_EVENT:
gotoState(BOTH_DIRTY);
break;
case APPLY_EVENT:
if (applyParam())
gotoState(NOT_DIRTY);
break;
default:
throw new IllegalArgumentException("unexpected event
"+event);
} // switch (event)
break;
default:

throw new InternalError("Unknown state event " + event);


} // switch (state)
} // processDirtyStateEvent(int)

// Set current state and perform entry actions for the state .
private void gotoState(int newState) {
switch (newState) {
case NOT_DIRTY:
applyButton.setEnabled(false);
revertButton.setEnabled(false);
saveButton.setEnabled(false);
break;
case FILE_DIRTY:
applyButton.setEnabled(false);
revertButton.setEnabled(true);
saveButton.setEnabled(true);
break;
case BOTH_DIRTY:
applyButton.setEnabled(true);
revertButton.setEnabled(true);
saveButton.setEnabled(true);
break;
case PARAM_DIRTY:
applyButton.setEnabled(true);
revertButton.setEnabled(false);
saveButton.setEnabled(false);
break;
} // switch

Ivan Mathias Filho 57 PUC-Rio


Introdução aos Padrões de Design

state = newState;
} // gotoState(int)

//...
private boolean saveParam() {
//...
return true;
} // saveParam()

private boolean applyParam() {


//...
return true;
} // applyParam()

private boolean revertParam() {


//...
return true;
} // revertParam()
} // class Procedural

Ivan Mathias Filho 58 PUC-Rio


Introdução aos Padrões de Design

1.9 O Padrão Proxy

1.9.1 Objetivo

Fazer com que o envio de mensagens para um dado objeto ocorra indiretamente
através de um objeto proxy, que atua como um surrogate (representante) do objeto
em questão. O objeto proxy recebe as mensagens e as repassa para o objeto alvo,
sem que os clientes tenham que ficar a par do fato de que eles estão não estão
interagindo diretamente com o objeto alvo.

1.9.2 Contexto

Um objeto proxy é um objeto que recebe chamadas de métodos no lugar de um outro


objeto. Os clientes enviam mensagens para um objeto proxy. Este, por sua vez, não
fornece diretamente o serviço solicitado. Ao invés disso, o objeto proxy aciona os
métodos do objeto responsável por prover os serviços solicitados pelos clientes. A
Figura 1.22 mostra um diagrama de colaboração que ilustra o que foi dito.

Figura 1.22 – Exemplo de Uso do Padrão Proxy.

Existem vários tipos de serviços que os objetos proxy estão habilitados a fornecer.
Entre eles estão:

• A execução de um serviço muito demorado por ser respondida imediatamente pelo


objeto proxy, enquanto o objeto alvo cuida da execução da tarefa. Isso libera o
cliente para executar outras tarefas, enquanto o serviço solicitado é executado pelo
objeto alvo.

• Um proxy cria a ilusão de que um objeto localizado em uma máquina remota esteja
carregado no mesmo espaço de endereçamento do objeto cliente. Este tipo de
proxy é conhecido como Proxy Remoto; sendo usado pelo Remote Method
Invocation (RMI), presente na plataforma Java.

• Um proxy pode controlar o acesso a um objeto provedor de serviços. Este tipo de


proxy é conhecido como Proxy de Acesso.
Ivan Mathias Filho 59 PUC-Rio
Introdução aos Padrões de Design

• Um proxy pode criar a ilusão de que um objeto servidor exista antes mesmo da sua
criação. Isso pode ser muito útil quando o custo de criação do objeto servidor for
muito alto e o uso dos seus serviços não for muito freqüente. Este tipo de proxy é
conhecido como Proxy Virtual.

1.9.3 Estrutura

A Figura 1.23 mostra um diagrama de classes com os principais componentes do


padrão Proxy.

Figura 1.23 – Estrutura do Padrão Proxy.

O gerenciamento transparente de um objeto provedor de serviços pode ser alcançado


obrigando que todos os acessos a tal objeto seja feito através de um proxy. Para
atingir a desejada transparência, o objeto proxy e o objeto provedor de serviços
devem compartilhar a mesma superclasse ou implementar uma interface comum,
como mostra a Figura 1.23.

A Figura 1.23 não mostra nenhum detalhe de implementação de nenhum método de


gerenciamento em particular. Entretanto, o padrão Proxy não será muito útil a não ser
que seja empregado algum tipo de política de gerenciamento de acesso ao objeto
provedor de serviços.

1.9.4 Aplicabilidade

O padrão Proxy é aplicável sempre que há necessidade de uma referência mais


versátil, ou sofisticada, do que um simples apontador para um objeto. Entre as
situações mais comuns nas quais o padrão Proxy é aplicável podemos citar:
Ivan Mathias Filho 60 PUC-Rio
Introdução aos Padrões de Design

• O uso de um proxy remoto para fornecer um representante local para um objeto


que reside em outro espaço de endereçamento.

• O uso de um proxy de acesso quando for necessário existir diferentes direitos de


acesso a um objeto.

• O uso de um mecanismo de referência mais sofisticado do que um simples


ponteiro. Os casos típicos incluem:

 A utilização de um contador de referências para um objeto real, de modo que o


mesmo possa ser liberado quando não houver mais referências para ele.

 Carregar um objeto persistente para a memória quando ele for referenciado pela
primeira vez.

 Verificar se o objeto real está bloqueado antes de ser acessado, para assegurar
que nenhum outro objeto possa alterar o seu conteúdo.

1.9.5 Conseqüências

a. Um proxy virtual pode executar otimizações, tais como a criação de um objeto sob
demanda.

b. Tanto proxies de proteção como referências sofisticadas permitem tarefas


adicionais de organização (housekeeping) quando um objeto é acessado.

1.9.6 Exemplo

O padrão Proxy não é muito útil na sua forma mais elementar. Ele deve ser combinado
com um mecanismo de gerência de acesso para que possamos obter algo de útil. O
exemplo a seguir usa o padrão Proxy para postergar uma operação custosa até que ela
seja realmente necessária. Se não for necessário, a operação não será nunca
executada.

O exemplo é uma subclasse de java.util.Hashtable que é funcionalmente


equivalente a esta. A diferença está no modo como a subclasse em questão trata a
operação de clonagem, que pode ser extremamente custosa.

1.9.7 Código do Exemplo

import java.util.Enumeration;
import java.util.Hashtable;

/**

Ivan Mathias Filho 61 PUC-Rio


Introdução aos Padrões de Design

* This subclass of Hashtable is functionally equivalent to Hashtable. The


* different is the way that it handles the clone operation. Cloning a
* Hashtable is an expensive operation.
* <p>
* One of the more common reasons for cloning an object like a Hashtable is to
* avoid holding a lock on the object for a long time when all that is desired
* is to fetch multiple key-value pairs. In a multi-threaded program, the
* usual way to ensure that a Hashtable is in a consistent state when you
fetch
* a value out of it is to have exclusive access to the Hashtable. While that
* is going on, other threads must wait to gain access to the same Hashtable,
* which may be unacceptable.
* In some other cases it may not be possible to retain exclusive access. An
* example of that is the Enumeration object returned by the Hashtable class'
* elements object.
*<p>
* Cloning a Hashtable prior to fetching values out of it is a defensive
* measure. If some other thread comes along and changes the contents of a
* Hashtable that you are fetching values from, then you may get some
* inconsittent results. Cloning the Hashtable avoids that problem since the
* hashtable you are reading from is a copy of the original that is only
* accessible to the whatever objects have visibility to the copy.
*<p>
* If, after you clone a hashtable, there is no subsequent modification to the
* Hashtable, then the time and memory spent in creating the clone is wasted.
* The point of this class is to avoid that waste. It does that by delaying
* the cloning of the Hashtable until a modification to the Hashtable actually
* occurs.
*<p>
* Instances of this class are a copy-on-write proxy for a Hashtable object.
* When a proxy's clone method is called, it returns a copy of the proxy but
* does not copy the hashtable object. At that point both the original and
* copy of the proxy refer to the same Hashtablec object. When either of the
* proxies is asked to modify the Hashtable, they recognize that they are
* using a shared Hashtable and clone the Hashtable before they make the
* modification.
*<p>
* The way that the proxies know that they are working with a shared Hashtable
* object is that the Hashtable object that proxies work with is an instance
* of a private subclass of Hashtable called ReferenceCountedHashTable. The
* ReferenceCountedHashTable keeps a count of how many proxies refer to it.
*/
public class LargeHashtable extends Hashtable {
// The ReferenceCountedHashTable that this is a proxy for.
private ReferenceCountedHashTable theHashTable;

/**
* Construct an empty hashtable.
* @param initialCapacity the initial capacity of the hashtable.
* @param loadFactor a number between 0.0 and 1.0.
* @exception IllegalArgumentException if initialCapacity <=0
* or loadFactor <= 0
*/
public LargeHashtable(int initialCapacity, float loadFactor) {
theHashTable = new ReferenceCountedHashTable(initialCapacity,
loadFactor);
} // constructor(int, float)

/**
* Construct an empty hashtable.
* @param initialCapacity the initial capacity of the hashtable.
* @exception IllegalArgumentException if initialCapacity <=0
*/
public LargeHashtable(int initialCapacity) {
theHashTable = new ReferenceCountedHashTable(initialCapacity);
} // constructor(int)

/**

Ivan Mathias Filho 62 PUC-Rio


Introdução aos Padrões de Design

* Construct an empty hashtable with a default capacity and load factor.


*/
public LargeHashtable() {
theHashTable = new ReferenceCountedHashTable();
} // constructor()

/**
* Return the number of key-value pairs in this hashtable.
*/
public int size() {
return theHashTable.size();
} // size()

/**
* Return true if this Hashtable contains no key-value pairs.
*/
public boolean isEmpty() {
return theHashTable.isEmpty();
} // isEmpty()

/**
* Return an enumeration of the keys in this Hashtable.
*/
public synchronized Enumeration keys() {
return theHashTable.keys();
} // keys()

/**
* Return an enumeration of the values in this Hashtable.
*/
public synchronized Enumeration elements() {
return theHashTable.elements();
}

/**
* Return true if the given value is part of a key-value pair in this
* Hashtable
* This operation is more expensive and requires a linear search.
* @param value The value to search for.
* @exception NullPointerException if the value is <code>null</code>.
*/
public synchronized boolean contains(Object value) {
return theHashTable.contains(value);
} // contains(Object)

/**
* Return true if the given key is in this hashtable.
* @param key The key to search for.
*/
public synchronized boolean containsKey(Object key) {
return theHashTable.containsKey(key);
} // containsKey(Object)

/**
* Return the value associated with the specified key in this Hashtable.
* @param key a key in the hashtable.
*/
public synchronized Object get(Object key) {
return theHashTable.get(key);
} // get(key)

/**
* Add the given key-value pair to this Hashtable.
* @param key the key.
* @param value the value.
* @return the previous value of the given key in this hashtable,
* or <code>null</code> if it did not have one.
* @exception NullPointerException if the key or value is

Ivan Mathias Filho 63 PUC-Rio


Introdução aos Padrões de Design

* <code>null</code>.
*/
public synchronized Object put(Object key, Object value) {
copyOnWrite();
return theHashTable.put(key, value);
} // put(key, value)

/**
* Remove the key-value pair having the given key from this Hashtable.
* @param key the key that needs to be removed.
*/
public synchronized Object remove(Object key) {
copyOnWrite();
return theHashTable.remove(key);
} // remove(Object)

/**
* Remove all key-value pairs from this Hashtable.
*/
public synchronized void clear() {
copyOnWrite();
theHashTable.clear();
} // clear()

/**
* Return a copy of this proxy that accesses the same Hashtable as this
* proxy. The first attempt for either to modify the contents of the
* Hashtable results in that proxy accessing a modified clone of the
* original Hashtable.
*/
public synchronized Object clone() {
Object copy = super.clone();
theHashTable.addProxy();
return copy;
} // clone()

/**
* This method is called before modifying the underlying Hashtable. If it
* is being shared then this method clones it.
*/
private void copyOnWrite() {
if (theHashTable.getProxyCount() > 1) {
// Synchronize on the original Hashtable to allow consistent
// recovery on error.
synchronized (theHashTable) {
theHashTable.removeProxy();
try {
theHashTable
= (ReferenceCountedHashTable)theHashTable.clone();
} catch (Throwable e) {
theHashTable.addProxy();
} // try
} // synchronized
} // if proxyCount
} // copyOnWrite()

/**
* Return a string representation of this Hashtable.
*/
public synchronized String toString() {
return theHashTable.toString();
}

private class ReferenceCountedHashTable extends Hashtable {


private int proxyCount = 1;

/**
* Construct an empty hashtable.

Ivan Mathias Filho 64 PUC-Rio


Introdução aos Padrões de Design

* @param initialCapacity the initial capacity of the


hashtable.
* @param loadFactor a number between 0.0 and 1.0.
* @exception IllegalArgumentException if initialCapacity <=0
* or loadFactor <= 0
*/
public ReferenceCountedHashTable(int initialCapacity,
float loadFactor) {
super(initialCapacity, loadFactor);
} // constructor(int, float)

/**
* Construct an empty hashtable.
* @param initialCapacity the initial capacity of the
hashtable.
* @exception IllegalArgumentException if initialCapacity <=0
*/
public ReferenceCountedHashTable(int initialCapacity) {
super(initialCapacity);
} // constructor(int)

/**
* Construct an empty hashtable with default capacity and load factor.
*/
public ReferenceCountedHashTable() {
super();
} // constructor()

/**
* Return a copy of this object with proxyCount set back to 1.
*/
public synchronized Object clone() {
ReferenceCountedHashTable copy;
copy = (ReferenceCountedHashTable)super.clone();
copy.proxyCount = 1;
return copy;
} // clone()

/**
* Return the number of proxies using this object.
*/
synchronized int getProxyCount() {
return proxyCount;
} // getProxyCount()

/**
* Increment the number of proxies using this object by one.
*/
synchronized void addProxy() {
proxyCount++;
} // addProxy()

/**
* Decrement the number of proxies using this object by one.
*/
synchronized void removeProxy() {

proxyCount--;
} // removeProxy()
} // class ReferenceCountedHashTable
} // class LargeHashtable

Ivan Mathias Filho 65 PUC-Rio


Bibliografia

Bibliografia

[Gamma95] Gamma, E.; Helm, R.; Johnson, R. e Vlissides, J. Design Patterns:


Elements of Reusable Object-Oriented Software. Addison-Wesley,
Reading, EUA, 1995.

[Grand98] Grand, M. Patterns in Java – A Catalog of Reusable Design


Patterns Illustrated with UML. Vol. 1, John Wiley, New York, EUA,
1998.

Ivan Mathias Filho 66 PUC-Rio

Vous aimerez peut-être aussi