Você está na página 1de 70

APOSTILA DE PADRES DE DESIGN

Ivan Mathias Filho Departamento de Informtica PUC-Rio

Sumrio

Sumrio
Introduo aos Padres de Design ...................................... 1
1.1
1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7

O Padro Factory Method ........................................................................ 2


Objetivo ..................................................................................................................... 2 Contexto .................................................................................................................... 2 Estrutura.................................................................................................................... 3 Aplicabilidade ............................................................................................................. 4 Conseqncias............................................................................................................ 4 Exemplo..................................................................................................................... 4 Cdigo do Exemplo ..................................................................................................... 5

1.2
1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7

O Padro Abstract Factory ......................................................................11


Objetivo ....................................................................................................................11 Contexto ...................................................................................................................11 Estrutura...................................................................................................................11 Aplicabilidade ............................................................................................................13 Conseqncias...........................................................................................................13 Exemplo....................................................................................................................13 Cdigo do Exemplo ....................................................................................................14

1.3
1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7

O Padro Singleton ................................................................................18


Objetivo ....................................................................................................................18 Contexto ...................................................................................................................18 Estrutura...................................................................................................................18 Aplicabilidade ............................................................................................................18 Conseqncias...........................................................................................................19 Exemplo....................................................................................................................19 Cdigo do Exemplo ....................................................................................................20

1.4
1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7

O Padro Facade ...................................................................................23


Objetivo ....................................................................................................................23 Contexto ...................................................................................................................23 Estrutura...................................................................................................................23 Aplicabilidade ............................................................................................................24 Conseqncias...........................................................................................................25 Exemplo....................................................................................................................25 Cdigo do Exemplo ....................................................................................................26

1.5
1.5.1 1.5.2 1.5.3 1.5.4

O Padro Composite ..............................................................................31


Objetivo ....................................................................................................................31 Contexto ...................................................................................................................31 Estrutura...................................................................................................................31 Aplicabilidade ............................................................................................................32

Ivan Mathias Filho

PUC-Rio

Sumrio
1.5.5 1.5.6 1.5.7 Conseqncias...........................................................................................................33 Exemplo....................................................................................................................33 Cdigo do Exemplo ....................................................................................................33

1.6
1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7

O Padro Observer.................................................................................37
Objetivo ....................................................................................................................37 Contexto ...................................................................................................................37 Estrutura...................................................................................................................37 Aplicabilidade ............................................................................................................38 Conseqncias...........................................................................................................38 Exemplo....................................................................................................................38 Cdigo do Exemplo ....................................................................................................39

1.7
1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7

O Padro Strategy .................................................................................42


Objetivo ....................................................................................................................42 Contexto ...................................................................................................................42 Estrutura...................................................................................................................42 Aplicabilidade ............................................................................................................43 Conseqncias...........................................................................................................44 Exemplo....................................................................................................................44 Cdigo do Exemplo ....................................................................................................45

1.8
1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7

O Padro State ......................................................................................47


Objetivo ....................................................................................................................47 Contexto ...................................................................................................................47 Estrutura...................................................................................................................47 Aplicabilidade ............................................................................................................49 Conseqncias...........................................................................................................49 Exemplo....................................................................................................................50 Cdigo do Exemplo ....................................................................................................52

1.9
1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7

O Padro Proxy......................................................................................59
Objetivo ....................................................................................................................59 Contexto ...................................................................................................................59 Estrutura...................................................................................................................60 Aplicabilidade ............................................................................................................60 Conseqncias...........................................................................................................61 Exemplo....................................................................................................................61 Cdigo 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 Padro Factory Method. .......................................................... 3 Figura 1.3 Exemplo do Padro Factory Method. .......................................................... 5 Figura 1.4 Estrutura do Padro Abstract Factory........................................................ 12 Figura 1.5 Exemplo do Padro Abstract Factory. ....................................................... 14 Figura 1.6 Estrutura do Padro Singleton.................................................................... 18 Figura 1.7 Exemplo do Padro Singleton. ................................................................... 20 Figura 1.8 xxxxxxx Facade......................................................................................... 23 Figura 1.9 Estrutura do Padro Facade. ...................................................................... 24 Figura 1.10 Exemplo do Padro Facade. .................................................................... 26 Figura 1.11 Estrutura do Padro Composite................................................................ 32 Figura 1.12 Exemplo do Padro Composite. ............................................................... 33 Figura 1.13 Estrutura do Padro Observer. ................................................................. 37 Figura 1.14 Adio de Um Novo Observador............................................................. 38 Figura 1.15 Notificao de Um Evento. ...................................................................... 38 Figura 1.16 Exemplo do Padro Observer. ................................................................. 39 Figura 1.17 Estrutura do Padro Strategy.................................................................... 43 Figura 1.18 Exemplo do Padro Strategy.................................................................... 45 Figura 1.19 Estrutura do Padro State. ........................................................................ 48 Figura 1.20 Mquina de Estados do Editor de Parmetros.......................................... 51 Figura 1.21 A Representao dos Estados Atravs de uma Hierarquia de Classes..... 52 Figura 1.22 Exemplo de Uso do Padro Proxy. .......................................................... 59 Figura 1.23 Estrutura do Padro Proxy. ...................................................................... 60

Ivan Mathias Filho

III

PUC-Rio

Introduo aos Padres de Design

Captulo 1
Introduo aos Padres de Design
Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Ivan Mathias Filho

PUC-Rio

Introduo aos Padres de Design

1.1 O Padro 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 instanciao para as subclasses.

1.1.2 Contexto
Considere o problema de construir um framework para aplicaes que gerenciem documentos. Tais aplicaes so tipicamente centradas nos documentos e nos arquivos que sero manipulados. A operao de uma aplicao dessa natureza comea, normalmente, com uma instruo 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 aplicao deve fornecer suporte de alto-nvel para as operaes usuais, tais como a criao, a abertura e a gravao de documentos. O suporte a estas operaes geralmente inclui um conjunto consistente de mtodos que so chamados em resposta aos comandos dos usurios. Para ilustrar esta discusso, iremos introduzir a classe Application como sendo a classe que fornece os mtodos necessrios para a manipulao de documentos. Um objeto da classe Application dever ser o ponto central para onde iro convergir todas as solicitaes originadas na interface com os usurios em resposta aos comandos por eles emitidos. Como no funo deste objeto implementar diretamente as solicitaes dos usurios, a classe Application ir delegar a maioria das mensagens para os objetos que efetivamente gerenciam os documentos. Por sua vez, a lgica para a implementao dos comandos de manipulao definida nestes objetos ir variar em funo do tipo do documento. Entretanto, existem operaes, tais como a exibio do ttulo do documento na barra de ttulo, que so 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 especficas aos diversos tipos de documentos suportados pela aplicao (Figura 1.1).

Ivan Mathias Filho

PUC-Rio

Introduo aos Padres de Design O que no foi dito at agora, e que tambm no mostrado no diagrama da Figura 1.1, como um objeto da classe Application ir criar as instncias das classes especficas aos diversos tipos de documentos existentes, de tal maneira que ela permanea independente do tipo de documento a ser manipulado. Este o objetivo do padro 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 papis das classes e das interfaces que compem o padro Factory Method.

Figura 1.2 Estrutura do Padro Factory Method.

Ivan Mathias Filho

PUC-Rio

Introduo aos Padres de Design A seguir apresentado o papel de cada uma dos componentes do padro Factory

Method:
Product superclasse abstrata dos objetos que sero produzidos pelo Factory

Method.
ConcreteProductX classes concretas instanciadas pelo Factory Method. Se as classes no compartilharem uma lgica comum, a classe Product poder ser substituda por uma interface. CreationRequestor classe que precisa criar um objeto especfico. Ela o far atravs de uma instncia da classe Factory. FactoryIF os objetos que iro instanciar objetos da classe Product tero que implementar a interface FactoryIF. Factory classe que implementa a interface FactoryIF e, por conseguinte, define o mtodo que instancia uma das subclasses de Product (ConcreteProductX).

1.1.4 Aplicabilidade
Use o padro Factory Method quando: Uma classe no puder antecipar as classes dos objetos que ele precisa criar. Uma classe tiver que delegar as suas subclasses a responsabilidade pela criao de objetos de uma aplicao. Isso permitir localizar nas subclasses o conhecimento necessrio criao dos objetos.

1.1.5 Conseqncias
a. O

CreationRequestor

torna-se

independente podendo trabalhar

da

classe

do

objeto classes

(ConcreteProductX)

instanciado,

com

quaisquer

concretas definidas pelo framework. b. O tipo dos produtos que sero manipulados pela aplicao pode ser mudado dinamicamente.

1.1.6 Exemplo
Suponha que estejamos desenvolvendo uma extenso da classe Socket com o objetivo de codificar streams de bytes enviados por uma conexo socket e de decodificar

Ivan Mathias Filho

PUC-Rio

Introduo aos Padres de Design

streams de bytes recebidos por conexes de mesma natureza. Chamaremos esta


classe de EncryptedSocket. A classe EncryptedSocket dever suportar mltiplos algoritmos de codificao. Em funo das restries legais impostas pelo governo dos EUA importao e exportao de algoritmos de codificao, a classe EncryptedSocket dever ser mantida independente das classes que implementam os algoritmos de codificao e decodificao. O uso de mltiplos algoritmos de codificao e decodificao, sem que a classe

EncryptedSocket tenha que conhecer antecipadamente as classes que encapsulam tais


algoritmos, nos sugere a aplicao do padro Factory Method para solucionar este problema. A Figura 1.3 mostra um diagrama de classes que ilustra a situao descrita acima.

Figura 1.3 Exemplo do Padro Factory Method.

1.1.7 Cdigo 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

PUC-Rio

Introduo aos Padres 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 import import import import java.io.InputStream; java.io.FilterInputStream; java.io.FilterOutputStream; java.io.OutputStream; 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

PUC-Rio

Introduo aos Padres 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 import import import import java.io.InputStream; java.io.FilterInputStream; java.io.FilterOutputStream; java.io.OutputStream; 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

PUC-Rio

Introduo aos Padres 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

PUC-Rio

Introduo aos Padres 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 import import import import import java.io.InputStream; java.io.IOException; java.io.OutputStream; java.net.Socket; java.security.Key; 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

PUC-Rio

Introduo aos Padres 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

Introduo aos Padres de Design

1.2 O Padro Abstract Factory


1.2.1 Objetivo
Fornecer uma interface para a criao de famlias 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 grficas (GUI) que possa ser utilizado sobre vrios sistemas gerenciadores de janelas diferentes, tais como MS Windows, Motif ou MacOS. Voc dever tambm fazer com que o framework funcione de acordo com as caractersticas visuais de cada uma das plataformas definidas. Uma possvel soluo 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 questo. Para tornar a implementao robusta, ser necessrio garantir que os objetos que implementam os componentes visuais sejam criados de acordo com a plataforma desejada. Neste contexto, uma fbrica abstrata ir declarar mtodos para a criao de instncias de cada uma das classes abstratas que representam componentes visuais. Enquanto isso, as subclasses concretas iro definir fbricas concretas que iro conter os mtodos necessrios criao 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 padro Abstract Factory.

Ivan Mathias Filho

11

PUC-Rio

Introduo aos Padres de Design

Figura 1.4 Estrutura do Padro Abstract Factory.

A seguir apresentado o papel de cada uma das classes do padro Abstract Factory: Client utiliza as classes que representam os componentes visuais para solicitar servios plataforma com a qual est trabalhando. A classe Client tem conhecimento apenas das classes abstratas que representam os componentes visuais abstratos. AbstractFactory declara os mtodos abstratos para a criao de instncias das classes concretas que implementam os componentes visuais. ConcreteFactoryX implementam os mtodos declarados na fbrica abstrata com o objetivo de criar instncias 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

Introduo aos Padres de Design

1.2.4 Aplicabilidade
Use o padro Abstract Factory quando: Um sistema for independente de como os seus componentes so criados, compostos ou representados. Um sistema deve ser configurado como um produto de uma famlia de mltiplos produtos. Uma famlia de objetos-produto for projetada para ser usada em conjunto, e for necessrio garantir tal restrio. Quisermos fornecer uma biblioteca de classes de produtos e revelarmos apenas as interfaces, deixando as suas implementaes ocultas.

1.2.5 Conseqncias
c. As classes concretas que implementam os componentes visuais so independentes das classes que as usam, dado que a fbrica abstrata encapsula o processo de criao de tais componentes visuais. d. Inserir novas classes que dem suporte a novas plataformas uma tarefa simples. Uma classe que represente uma fbrica concreta usualmente referenciada em apenas um ponto do framework. De modo similar, bastante simples alterar uma fbrica concreta para tratar de uma nova plataforma a ser adicionada ao

framework.
e. Ao forarmos os clientes a usarem as fbricas concretas para a criao dos componentes visuais, o padro Abstract Factory assegura que os clientes usaro um conjunto de objetos consistentes com a plataforma com a qual desejam interagir. f. A principal deficincia do padro Abstract Factory o excesso de trabalho necessrio 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 diagnsticos remotos em computadores para um fabricante de equipamentos. Atravs dos anos, o fabricante em questo produziu computadores com substanciais diferenas nas suas respectivas

Ivan Mathias Filho

13

PUC-Rio

Introduo aos Padres de Design arquiteturas. Nos equipamentos mais antigos foram usados chips de CPU da Enginola, que so baseados na tradicional arquitetura CISC. Desde ento, o nosso fabricante produziu computadores baseados na sua prpria arquitetura RISC, chamdas de Ember, SuperEmber e UltraEmber. Os componentes principais destas vrias arquiteturas executam funes similares, porm possuem conjuntos de componentes distintos. A Figura 1.5 mostra um diagrama de classes que ilustra uma situao semelhante descrita acima, porm com apenas dois componentes para duas arquiteturas distintas.

Figura 1.5 Exemplo do Padro Abstract Factory.

1.2.7 Cdigo 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

Introduo aos Padres 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

Introduo aos Padres 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

Introduo aos Padres 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

Introduo aos Padres de Design

1.3 O Padro Singleton


1.3.1 Objetivo
Garantir que uma classe tenha somente uma nica instncia e fornecer um ponto global de acesso para tal instncia.

1.3.2 Contexto
Algumas classes devem possuir exatamente uma instncia. Tais classes geralmente esto 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 reutilizao de uma conexo com um gerenciador de banco de dados, ou o recurso pode ser interno, como no caso em que um objeto mantm estatsticas de erro para um compilador.

1.3.3 Estrutura
O padro Singleton relativamente simples, uma vez que ele envolve uma nica classe (Figura 1.6). A classe unitria possui uma varivel esttica que mantm uma referncia para a nica instncia que se deseja manipular. Esta instncia criada quando a classe carregada na memria ou quando ocorrer a primeira tentativa de acesso instncia. Devemos implementar a classe unitria de tal modo que no seja permitida a criao de instncias adicionais nica instncia permitida. Isto significa que devemos nos assegurar que todos os construtores da classe unitria sejam declarados como privados.

Figura 1.6 Estrutura do Padro Singleton.

1.3.4 Aplicabilidade
Use o padro Singleton quando:

Ivan Mathias Filho

18

PUC-Rio

Introduo aos Padres de Design Deve haver apenas uma nica instncia de uma classe, e essa instncia deve poder ser acessada pelos clientes a partir de um ponto bem conhecido. Quando a nica instncia tiver que ser estendida atravs de subclasses, possibilitando aos clientes usar a instncia estendida sem alterar os seus respectivos cdigos.

1.3.5 Conseqncias
a. Como a classe Singleton encapsula a sua nica instncia, ela pode ter o controle total de como e quando os clientes acessam esta instncia. b. O padro Singleton representa uma melhoria em relao ao uso de variveis globais. c. A classe Singleton pode ser estendida atravs de subclasses, sendo bastante simples configurar uma aplicao para trabalhar com a extenso. d. A classe Singleton pode ser modificada para suportar um nmero maior de instncias, embora ainda se possa ter o controle do nmero de instncias que uma aplicao v utilizar. e. Uma outra maneira de implementar um Singleton atravs do uso de mtodos estticos; entretanto, a utilizao desta tcnica dificulta a mudana de um projeto para permitir um nmero maior de instncias. Alm disso, as funes estticas no so polimrficas, o que significa que as subclasses no podem redefini-las polimorficamente.

1.3.6 Exemplo
Suponha que desejemos escrever uma classe que possa ser usada por uma applet para garantir que no mais que um udio clip possa ser executado em um dado momento. Se uma applet contiver dois trechos de cdigo que reproduzam udio clips independentemente, ento seria possvel para ambas reproduzirem os udio clips ao mesmo tempo. Tal ocorrncia poderia levar uma condio de erro, ou a uma situao de grande confuso, com o usurio ouvindo um mix de sons no muito agradvel. Para evitar tal situao necessrio que a classe responsvel pela reproduo de udio clips interrompa a reproduo corrente antes de comear uma nova. Um meio de implementar essa poltica assegurar que exista uma nica instncia da classe de reproduo, e que ela seja compartilhada por todas as classes que desejem reproduzir

Ivan Mathias Filho

19

PUC-Rio

Introduo aos Padres de Design udio clips. Se todas as solicitaes para reproduo forem direcionadas para um nico objeto, ser fcil para este objeto parar a reproduo de um clip antes de iniciar a seguinte. A Figura 1.7 mostra um diagrama com uma classe com as caractersticas descritas acima.

Figura 1.7 Exemplo do Padro Singleton.

1.3.7 Cdigo 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

Introduo aos Padres 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

Introduo aos Padres 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

Introduo aos Padres de Design

1.4 O Padro Facade


1.4.1 Objetivo
O padro Facade simplifica o acesso a um conjunto de objetos relacionados utilizando, para tal, um outro objeto, que responsvel pela comunicao entre os objetos externos ao conjunto e o conjunto de objetos em questo.

1.4.2 Contexto
Estruturar um sistema em subsistemas ajuda a reduzir a complexidade. Um objetivo comum a todos os projetos minimizar a comunicao e as dependncias entre os subsistemas que compem uma aplicao. Uma maneira de alcanar 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 padro

Facade.
O objeto Client interage com o objeto Facade, que fornece a funcionalidade necessria para a interao com o restante dos objetos. Se existir alguma funcionalidade adicional, necessria apenas para alguns clientes, ser melhor ento que o objeto

Ivan Mathias Filho

23

PUC-Rio

Introduo aos Padres de Design

Facade fornea um mtodo para que seja possvel acessar diretamente o objeto
responsvel por tal funcionalidade, ao invs de inclu-la na interface do objeto Facade.

Figura 1.9 Estrutura do Padro Facade.

A seguir apresentado o papel dos componentes do padro Facade: Client precisa de uma funcionalidade de um dado subsistema mas no deseja estar a par da complexidade que envolve a execuo de tal funcionalidade. Facade conhece as classes do subsistema que so responsveis pelo atendimento de uma solicitao. Em funo disso, delega as solicitaes dos clientes aos objetos apropriados do subsistema. Classes do Subsistema implementam as funcionalidades do subsistema. Estas classes encarregam-se do trabalho que lhes foi atribudo pelo objeto Facade. Como regra geral, as classes do subsistema no devem manter referncias para o objeto

Facade.

1.4.4 Aplicabilidade
Use o padro Facade quando: Voc desejar fornecer uma interface simples para um subsistema complexo.

Ivan Mathias Filho

24

PUC-Rio

Introduo aos Padres de Design Existirem muitas dependncias entre os clientes e as classes que implementam uma certa abstrao. Ao introduzir uma fachada para reduzir o acoplamento entre o subsistema e os clientes (ou outros subsistemas), estar-se- promovendo a independncia e portabilidade dos subsistemas. Voc desejar estruturar em camadas seus subsistemas. Use uma fachada para definir o ponto de entrada para cada nvel de subsistema.

1.4.5 Conseqncias
a. Isola os clientes dos componentes de um subsistema, reduzindo o nmero de objetos com os quais os clientes tm que lidar, e tornando, assim, mais fcil o uso de tal subsistema. b. Promove o fraco acoplamento entre um subsistema e os seus clientes. Esta caracterstica 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 utilizao diminui a ocorrncia de alteraes em cascata em funo da necessidade de uma alterao em um certo subsistema. d. No impede que as aplicaes 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 manipulao das funcionalidades fornecidas por um subsistema.

1.4.6 Exemplo
Considere a organizao de um conjunto classes que fornece o servio de criao 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 so criados e trocam mensagens entre si. Se todo cliente tiver que lidar com toda essa complexidade adicional, ser difcil a reutilizao de tais classes em um outro contexto. O padro Facade fornece um meio de proteger um cliente da complexidade de usar este conjunto de classes. Isto feito atravs de um objeto adicional que oculta a maior parte das complexas interaes existentes. Como pode ser visto na Figura 1.10, os

Ivan Mathias Filho

25

PUC-Rio

Introduo aos Padres de Design clientes tm que estar a par apenas da existncia da classe MessageCreator. Isto possvel porque a lgica interna da classe MessageCreator responsvel pela criao das partes de uma mensagem de e-mail em uma determinada ordem.

Figura 1.10 Exemplo do Padro Facade.

1.4.7 Cdigo 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 private private private Hashtable headerFields = new Hashtable(); RichText messageBody; Vector attachments = new Vector(); 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

Introduo aos Padres 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

Introduo aos Padres 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

Introduo aos Padres 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

Introduo aos Padres 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

Introduo aos Padres de Design

1.5 O Padro Composite


1.5.1 Objetivo

1.5.2 Contexto
Suponha que precisemos escrever um programa para a formatao de documentos. Ele dever formatar caracteres de linhas de texto, que so organizadas em colunas, que so organizadas em pginas. Alm disso, tal documento pode conter ainda outros elementos. Colunas e pginas 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 aplicao dessa natureza. Objetos que representam pginas e frames tm que saber como manipular e combinar diversos tipos de componentes. O padro Composite procura remover tal complexidade fazendo com que estes objetos compostos precisem saber apenas como gerenciar um nico tipo de elemento. Isso alcanado 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 padro Composite pode ser visto na Figura 1.11.

Ivan Mathias Filho

31

PUC-Rio

Introduo aos Padres de Design

Figura 1.11 Estrutura do Padro Composite.

A seguir apresentada a descrio das classes que participam da estrutura recursiva do padro 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 instncias 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 padro Composite. Esta classe declara e fornece implementaes default para todos os mtodos usados no gerenciamento dos componentes de um objeto composto. ConcreteCompositeX as instncias destas classes so os objetos compostos que tm como componentes os objetos das outras subclasses de AbstractComponent.

1.5.4 Aplicabilidade
xxxxxxxxx: yyyy

Ivan Mathias Filho

32

PUC-Rio

Introduo aos Padres de Design zzzzz.

1.5.5 Conseqncias
f. Zzzzzzzzzzzz.

1.5.6 Exemplo
O exemplo de aplicao do padro Composite uma verso mais detalhada do

formatador de documentos apresentado na seo de Contexto. A Figura 1.12 mostra o


diagrama de classes detalhado do problema em questo.

Figura 1.12 Exemplo do Padro Composite.

1.5.7 Cdigo 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

Introduo aos Padres 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

Introduo aos Padres 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

Introduo aos Padres 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

Introduo aos Padres de Design

1.6 O Padro Observer


1.6.1 Objetivo

1.6.2 Contexto

1.6.3 Estrutura

Figura 1.13 Estrutura do Padro Observer.

Ivan Mathias Filho

37

PUC-Rio

Introduo aos Padres de Design

Figura 1.14 Adio de Um Novo Observador.

Figura 1.15 Notificao de Um Evento.

1.6.4 Aplicabilidade

xxxxxxxxx: yyyy zzzzz.

1.6.5 Conseqncias
g. Zzzzzzzzzzzz.

1.6.6 Exemplo

Ivan Mathias Filho

38

PUC-Rio

Introduo aos Padres de Design

Figura 1.16 Exemplo do Padro Observer.

1.6.7 Cdigo 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 * this object. * @param device A number that * this notification. * @param event This should be * interface. */ public void notify(int device, } // interface SecurityObserver

deliver a security notification to identifies the device that originated one of the constants defined in this

int event);

/** * 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

Introduo aos Padres 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

Introduo aos Padres 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

Introduo aos Padres de Design

1.7 O Padro Strategy


1.7.1 Objetivo
Encapsular algoritmos relacionados em um conjunto de classes, que so 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 rgida tais algoritmos nas classes que os utilizam no desejvel, por vrias razes: Os clientes que necessitam das quebras de linhas se tornam mais complexos se incluem o cdigo de quebra de linhas. Isso os torna maiores e mais difceis de manter, especialmente se suportam mltiplos algoritmos de quebra de linhas. Diferentes algoritmos sero apropriados em diferentes situaes. No seria uma boa estratgia suportar mltiplos algoritmos de quebra de linha se no for necessrio us-los. difcil 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 (estratgia).

1.7.3 Estrutura
A Figura 1.17 mostra um diagrama de classes com os principais componentes do padro Strategy.

Ivan Mathias Filho

42

PUC-Rio

Introduo aos Padres de Design

Figura 1.17 Estrutura do Padro Strategy.

A seguir apresentado um resumo do papel que cada classe exerce no padro em questo: Client uma classe Client delega a execuo de uma operao a uma classe abstrata ou a uma interface. Ela o faz sem conhecer a classe do objeto ao qual a operao delegada, e como tal classe implementa a operao solicitada. AbstractStrategy fornece uma interface comum para acessar as operaes definidas pelas suas subclasses. Pode-se usar uma interface, ao invs de uma classe abstrata, para exercer este papel. ConcreteStrateyX definem os diferentes mtodos que implementam as diferentes estratgias existentes para a operao solicitada pelo cliente.

1.7.4 Aplicabilidade
Use o padro Strategy quando: Muitas classes relacionadas diferem somente no seu comportamento. As estratgias fornecem uma maneira de configurar uma classe com um, dentre muitos comportamentos. Voc necessita de variantes de um algoritmo. Por exemplo, podemos definir algoritmos que reflitam diferentes solues de compromisso entre espao/tempo. Um algoritmo usa dados que os clientes deveriam desconhecer. Use o padro

Strategy para evitar a exposio das estruturas de dados complexas, especficas do


algoritmo.

Ivan Mathias Filho

43

PUC-Rio

Introduo aos Padres de Design Uma classe define muitos comportamentos, e estes aparecem em suas operaes como mltiplos comandos condicionais.

1.7.5 Conseqncias
a. Podemos usar este padro para criar diferentes hierarquias de estratgias, que iro definir famlias de algoritmos e comportamentos relacionados. b. Uma alternativa ao uso do padro Strategy seria especializar a classe Client para lhe dar diferentes comportamentos. Entretanto, isso congelaria o comportamento do cliente, misturando a implementao do algoritmo em questo com o resto do cdigo da classe Client, e tornando esta classe mais difcil de compreender, manter e estender. Alm disso, teramos a desvantagem de no poder variar os algoritmos dinamicamente. A soluo dada pelo padro Strategy permite variar o algoritmo independentemente dos seus clientes, tornando mais fcil troc-los, compreendlos e estend-los. c. Uma possvel desvantagem do padro Strategy estaria no fato de que um cliente pode ser obrigado, em algumas situaes, a conhecer a maneira pela qual as estratgias diferem uma das outras. Isso poderia levar a uma situao indesejvel, onde os clientes teriam que estar expostos aos detalhes de implementao das diferentes estratgias.

1.7.6 Exemplo
Suponha que precisemos escrever um programa para exibir calendrios. Um dos requisitos deste programa estar apto a exibir os feriados celebrados por diferentes naes e diferentes grupos religiosos. O usurio deste programa deve poder escolher quais grupos de feriados devem ser exibidos. Seria interessante atender este requisito colocando a lgica da aplicao 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. Gostaramos tambm que as classes que fossem utilizar os servios para exibio dos calendrios no precisassem estar a par da existncia de nenhum feriado, ou conjunto de feriados, especfico. A Figura 1.18 mostra como utilizar o padro Strategy para implementar uma soluo para o problema descrito acima.

Ivan Mathias Filho

44

PUC-Rio

Introduo aos Padres de Design

Figura 1.18 Exemplo do Padro Strategy.

1.7.7 Cdigo 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

Introduo aos Padres 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

Introduo aos Padres de Design

1.8 O Padro 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 rgida tais algoritmos nas classes que os utilizam no desejvel, por vrias razes: Os clientes que necessitam das quebras de linhas se tornam mais complexos se incluem o cdigo de quebra de linhas. Isso os torna maiores e mais difceis de manter, especialmente se suportam mltiplos algoritmos de quebra de linhas. Diferentes algoritmos sero apropriados em diferentes situaes. No seria uma boa estratgia suportar mltiplos algoritmos de quebra de linha se no for necessrio us-los. difcil 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 (estratgia).

1.8.3 Estrutura
A Figura 1.19 mostra um diagrama de classes com os principais componentes do padro State.

Ivan Mathias Filho

47

PUC-Rio

Introduo aos Padres de Design

Figura 1.19 Estrutura do Padro State.

A seguir apresentado um resumo do papel que cada classe exerce no padro em questo: Context classe cujas instncias exibem um comportamento que deve ser modelado por uma mquina de estados. As instncias desta classe determinam os seus respectivos estados correntes atravs de uma referncia (currentState) para uma instncia de uma subclasse concreta da classe ContextState. ContextState esta classe a superclasse de todas as classes usadas para representar os estados de uma instncia da classe Context. A classe ContextState define os seguintes mtodos: O mtodo start executa todas as tarefas necessrias para a inicializao da mquina de estados de um objeto, e retorna um objeto que corresponde ao estado inicial do objeto em questo. O mtodo processEvent um mtodo abstrato que recebe como argumento um evento e retorna o novo estado corrente do objeto em questo. Cada subclasse concreta de ContextState deve redefinir este mtodo de acordo com as necessidades do estado que ela representa. Quaisquer aes de sada (exit actions) relativas ao estado corrente devem ser executadas neste mtodo (desde que o evento tenha causado uma transio de sada do estado corrente).

Ivan Mathias Filho

48

PUC-Rio

Introduo aos Padres de Design O mtodo enter responsvel pela execuo das aes de entrada (entry

actions) de um estado. A implementao default, fornecida pela classe ContextState, no contm comandos (vazia), e deve ser redefinida pelas
subclasses que representam estados que contenham aes de entrada. Os mtodos operation1, operation2, ...., implementam as operaes especficas de cada um dos estados. ConcreteStateX subclasses concretas de ContextState, que representam os estados existentes em uma mquina de estados. Estas classes tm que implementar o mtodo

processEvent para fornecerem respostas adequadas aos eventos ocorridos.

1.8.4 Aplicabilidade
Use o padro State quando: O comportamento de um objeto depender do seu estado e ele tiver que mudar de comportamento em tempo de execuo em funo de uma mudana no seu estado. As operaes contiverem grandes estruturas condicionais, com muitas alternativas. Freqentemente, tais estruturas iro conter grandes pores de cdigo que iro se repetir atravs de vrias operaes, gerando replicao desnecessria de cdigo. O padro 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 implementao de uma mquina de estados.

1.8.5 Conseqncias
a. O padro State implementa uma mquina de estados particionando os comportamentos relativos aos estados e organizando tais comportamentos em objetos especficos. Desse modo, novos estados e transies podem ser facilmente adicionados mquina de estados atravs da definio de novas subclasses. b. Quando um objeto define o seu estado corrente unicamente em termos dos valores dos seus atributos, a suas transies de estado no tm representao explcita; elas ficam caracterizadas apenas pela mudana de valores de alguns atributos. A introduo de objetos distintos para representar os diferentes estados de mquina de estados torna as transies mais explcitas.

Ivan Mathias Filho

49

PUC-Rio

Introduo aos Padres de Design

1.8.6 Exemplo
Suponha que estejamos escrevendo um editor de parmetros de programas. Uma caixa de dilogo para esta aplicao ter botes que iro representar as seguintes aes: Um boto de OK ir salvar os valores dos parmetros informados em um arquivo e na rea de trabalho do programa. Um boto de Save ir apenas salvar os valores dos parmetros informados em um arquivo. Um boto de Apply ir apenas salvar os valores dos parmetros informados na rea de trabalho do programa. Um boto de Revert ir restaurar os valores dos parmetros a partir do arquivo. possvel projetar este dilogo de modo que ele no tenha estados. Se um dilogo no contm estados, ele ir se comportar sempre da mesma maneira. O boto de OK estar sempre ativado, mesmo se o usurio tiver recm restaurado os valores dos parmetros a partir do arquivo. Se no houver outras consideraes, o uso de uma abordagem sem estados no design deste dilogo pode ser satisfatrio. Em alguns casos, entretanto, o comportamento descrito acima pode causar alguns problemas. A alterao dos valores dos parmetros 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 operaes desnecessrias inserir estados no dilogo de tal maneira que as operaes no possam ser executadas quando no forem necessrias. Ao invs disso, o dilogo s ir permitir a execuo destas operaes quando os valores dos parmetros 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

Introduo aos Padres de Design

Figura 1.20 Mquina de Estados do Editor de Parmetros.

Para implementar a mquina de estados da Figura 1.20, iremos criar cinco classes, como mostrado na Figura 1.21. Quatro delas iro corresponder a cada um dos quatro estados mostrado na mquina de estados da Figura 1.20, e a quinta ser a superclasse comum s quatro primeiras. A superclasse DirtyState possui um mtodo pblico chamado processEvent. Este mtodo recebe como parmetro um identificador de evento e retorna um objeto que representa o estado seguinte. Cada subclasse de DirtyState ir redefinir o mtodo

processEvent de acordo com as suas necessidades. A classe DirtyState possui tambm


um mtodo esttico chamado start. Este mtodo ir por as coisas para funcionar criando uma instncia para cada subclasse de DirtyState e retornando o objeto que representa o estado inicial. O mtodo start cria tambm uma instncia de DirtyState e guarda nas variveis notDirty, fileDirty, paramDirty e bothDirty as referncias para os objetos que representam os estados.

Ivan Mathias Filho

51

PUC-Rio

Introduo aos Padres de Design

Figura 1.21 A Representao dos Estados Atravs de uma Hierarquia de Classes.

1.8.7 Cdigo do Exemplo


import java.awt.*; class DirtyState { // Symbolic constants for events public static final int DIRTY_EVENT public static final int APPLY_EVENT public static final int SAVE_EVENT public static final int REVERT_EVENT // Symbolic constants for states private final BothDirty bothDirty private final FileDirty fileDirty private final ParamDirty paramDirty private final NotDirty 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;

= = = =

1; 2; 3; 4;

= = = =

new new new new

BothDirty(); FileDirty(); ParamDirty(); NotDirty();

Ivan Mathias Filho

52

PUC-Rio

Introduo aos Padres 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

Introduo aos Padres 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

Introduo aos Padres 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

Introduo aos Padres 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 public static final int APPLY_EVENT public static final int SAVE_EVENT public static final int REVERT_EVENT // Symbolic constants for states private static final int BOTH_DIRTY private static final int FILE_DIRTY private static final int PARAM_DIRTY private static final int NOT_DIRTY

= = = =

1; 2; 3; 4;

= = = =

101; 102; 103; 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

Introduo aos Padres 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

Introduo aos Padres 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

Introduo aos Padres de Design

1.9 O Padro Proxy


1.9.1 Objetivo
Fazer com que o envio de mensagens para um dado objeto ocorra indiretamente atravs de um objeto proxy, que atua como um surrogate (representante) do objeto em questo. 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 esto no esto interagindo diretamente com o objeto alvo.

1.9.2 Contexto
Um objeto proxy um objeto que recebe chamadas de mtodos no lugar de um outro objeto. Os clientes enviam mensagens para um objeto proxy. Este, por sua vez, no fornece diretamente o servio solicitado. Ao invs disso, o objeto proxy aciona os mtodos do objeto responsvel por prover os servios solicitados pelos clientes. A Figura 1.22 mostra um diagrama de colaborao que ilustra o que foi dito.

Figura 1.22 Exemplo de Uso do Padro Proxy.

Existem vrios tipos de servios que os objetos proxy esto habilitados a fornecer. Entre eles esto: A execuo de um servio muito demorado por ser respondida imediatamente pelo objeto proxy, enquanto o objeto alvo cuida da execuo da tarefa. Isso libera o cliente para executar outras tarefas, enquanto o servio solicitado executado pelo objeto alvo. Um proxy cria a iluso de que um objeto localizado em uma mquina remota esteja carregado no mesmo espao de endereamento 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 servios. Este tipo de

proxy conhecido como Proxy de Acesso.

Ivan Mathias Filho

59

PUC-Rio

Introduo aos Padres de Design Um proxy pode criar a iluso de que um objeto servidor exista antes mesmo da sua criao. Isso pode ser muito til quando o custo de criao do objeto servidor for muito alto e o uso dos seus servios no for muito freqente. 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 padro Proxy.

Figura 1.23 Estrutura do Padro Proxy.

O gerenciamento transparente de um objeto provedor de servios pode ser alcanado obrigando que todos os acessos a tal objeto seja feito atravs de um proxy. Para atingir a desejada transparncia, o objeto proxy e o objeto provedor de servios devem compartilhar a mesma superclasse ou implementar uma interface comum, como mostra a Figura 1.23. A Figura 1.23 no mostra nenhum detalhe de implementao de nenhum mtodo de gerenciamento em particular. Entretanto, o padro Proxy no ser muito til a no ser que seja empregado algum tipo de poltica de gerenciamento de acesso ao objeto provedor de servios.

1.9.4 Aplicabilidade
O padro Proxy aplicvel sempre que h necessidade de uma referncia mais verstil, ou sofisticada, do que um simples apontador para um objeto. Entre as situaes mais comuns nas quais o padro Proxy aplicvel podemos citar:

Ivan Mathias Filho

60

PUC-Rio

Introduo aos Padres de Design O uso de um proxy remoto para fornecer um representante local para um objeto que reside em outro espao de endereamento. O uso de um proxy de acesso quando for necessrio existir diferentes direitos de acesso a um objeto. O uso de um mecanismo de referncia mais sofisticado do que um simples ponteiro. Os casos tpicos incluem: A utilizao de um contador de referncias para um objeto real, de modo que o mesmo possa ser liberado quando no houver mais referncias para ele. Carregar um objeto persistente para a memria quando ele for referenciado pela primeira vez. Verificar se o objeto real est bloqueado antes de ser acessado, para assegurar que nenhum outro objeto possa alterar o seu contedo.

1.9.5 Conseqncias
a. Um proxy virtual pode executar otimizaes, tais como a criao de um objeto sob demanda. b. Tanto proxies de proteo como referncias sofisticadas permitem tarefas adicionais de organizao (housekeeping) quando um objeto acessado.

1.9.6 Exemplo
O padro Proxy no muito til na sua forma mais elementar. Ele deve ser combinado com um mecanismo de gerncia de acesso para que possamos obter algo de til. O exemplo a seguir usa o padro Proxy para postergar uma operao custosa at que ela seja realmente necessria. Se no for necessrio, a operao no ser nunca executada. O exemplo uma subclasse de java.util.Hashtable que funcionalmente equivalente a esta. A diferena est no modo como a subclasse em questo trata a operao de clonagem, que pode ser extremamente custosa.

1.9.7 Cdigo do Exemplo


import java.util.Enumeration; import java.util.Hashtable; /**

Ivan Mathias Filho

61

PUC-Rio

Introduo aos Padres 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

Introduo aos Padres 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

Introduo aos Padres 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

Introduo aos Padres 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

Você também pode gostar