APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf ·...

72
APOSTILA DE PADRÕES DE DESIGN Ivan Mathias Filho Departamento de Informática – PUC-Rio

Transcript of APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf ·...

Page 1: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

APOSTILA DE PADRÕES DE DESIGN

Ivan Mathias Filho

Departamento de Informática – PUC-Rio

Page 2: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Sumário

Ivan Mathias Filho PUC-Rio I

Sumário

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

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

1.2 O Padrão Abstract Factory....................................................................... 11 1.2.1 Objetivo.......................................................................................................................11 1.2.2 Contexto......................................................................................................................11 1.2.3 Estrutura .....................................................................................................................11 1.2.4 Aplicabilidade...............................................................................................................13 1.2.5 Conseqüências .............................................................................................................13 1.2.6 Exemplo ......................................................................................................................13 1.2.7 Código do Exemplo ......................................................................................................14

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

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

1.5 O Padrão Composite ............................................................................... 31 1.5.1 Objetivo.......................................................................................................................31 1.5.2 Contexto......................................................................................................................31 1.5.3 Estrutura .....................................................................................................................32 1.5.4 Aplicabilidade...............................................................................................................33

Page 3: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Sumário

Ivan Mathias Filho PUC-Rio II

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

1.6 O Padrão Observer ................................................................................. 38 1.6.1 Objetivo.......................................................................................................................38 1.6.2 Contexto......................................................................................................................38 1.6.3 Estrutura .....................................................................................................................39 1.6.4 Aplicabilidade...............................................................................................................41 1.6.5 Conseqüências .............................................................................................................41 1.6.6 Exemplo ......................................................................................................................41 1.6.7 Código do Exemplo ......................................................................................................42

1.7 O Padrão Strategy .................................................................................. 45 1.7.1 Objetivo.......................................................................................................................45 1.7.2 Contexto......................................................................................................................45 1.7.3 Estrutura .....................................................................................................................45 1.7.4 Aplicabilidade...............................................................................................................46 1.7.5 Conseqüências .............................................................................................................47 1.7.6 Exemplo ......................................................................................................................47 1.7.7 Código do Exemplo ......................................................................................................48

1.8 O Padrão State ....................................................................................... 50 1.8.1 Objetivo.......................................................................................................................50 1.8.2 Contexto......................................................................................................................50 1.8.3 Estrutura .....................................................................................................................50 1.8.4 Aplicabilidade...............................................................................................................51 1.8.5 Conseqüências .............................................................................................................52 1.8.6 Exemplo ......................................................................................................................52 1.8.7 Código do Exemplo ......................................................................................................54

1.9 O Padrão Proxy ...................................................................................... 61 1.9.1 Objetivo.......................................................................................................................61 1.9.2 Contexto......................................................................................................................61 1.9.3 Estrutura .....................................................................................................................62 1.9.4 Aplicabilidade...............................................................................................................62 1.9.5 Conseqüências .............................................................................................................63 1.9.6 Exemplo ......................................................................................................................63 1.9.7 Código do Exemplo ......................................................................................................63

Bibliografia..........................................................................68

Page 4: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Lista de Figuras

Ivan Mathias Filho PUC-Rio

III

Lista de Figuras

Figura 1 – Framework para o Gerenciamento de Documentos........................................ 3 Figura 2 – Estrutura do Padrão Factory Method. ............................................................. 3 Figura 3 – Exemplo do Padrão Factory Method. ............................................................. 5 Figura 4 – Estrutura do Padrão Abstract Factory........................................................... 12 Figura 5 – Exemplo do Padrão Abstract Factory. .......................................................... 14 Figura 6 – Estrutura do Padrão Singleton. ...................................................................... 18 Figura 7 – Exemplo do Padrão Singleton. ...................................................................... 20 Figura 8 – O Uso do Padrão Facade. ............................................................................. 23 Figura 9 – Estrutura do Padrão Facade. ......................................................................... 24 Figura 10 – Exemplo do Padrão Facade. ....................................................................... 26 Figura 11 – A Estrutura de Um Documento................................................................... 31 Figura 12 – Estrutura do Padrão Composite................................................................... 32 Figura 13 – Exemplo do Padrão Composite. .................................................................. 34 Figura 14 – Os Elementos do Padrão Observer. ............................................................ 38 Figura 15 – Estrutura do Padrão Observer. .................................................................... 39 Figura 16 – Adição de Um Novo Observador................................................................ 40 Figura 17 – Notificação de Um Evento. ......................................................................... 40 Figura 18 – Exemplo do Padrão Observer. .................................................................... 42 Figura 19 – Estrutura do Padrão Strategy....................................................................... 46 Figura 20 – Exemplo do Padrão Strategy. ...................................................................... 48 Figura 21 – Estrutura do Padrão State. ........................................................................... 50 Figura 22 – Máquina de Estados do Editor de Parâmetros. ........................................... 53 Figura 23 – A Representação dos Estados Através de uma Hierarquia de Classes........ 54 Figura 24 – Exemplo de Uso do Padrão Proxy. ............................................................. 61 Figura 25 – Estrutura do Padrão Proxy. ......................................................................... 62

Page 5: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 1

Capítulo 1

Introdução aos Padrões de Design

Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Page 6: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 2

1.1 O Padrão Factory Method

1.1.1 Objetivo

Definir uma interface para criar um objeto, mas deixar que as subclasses decidam que

classe instanciar. O Factory Method permite adiar a instanciação para as subclasses.

1.1.2 Contexto

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

documentos. Tais aplicações são tipicamente centradas nos documentos e nos

arquivos que serão manipulados. De um modo geral, uma aplicação dessa natureza

começa, normalmente, com uma instrução para criar ou editar um documento de um

processador de textos, uma planilha ou quaisquer outros documentos ou arquivos que

possam ser manipulados.

Um framework para este tipo de aplicação deve fornecer suporte de alto-nível para as

operações usuais, tais como a criação, a abertura e a gravação de documentos. O

suporte a estas operações geralmente inclui um conjunto consistente de métodos, que

são chamados em resposta aos comandos dos usuários. Para ilustrar esta discussão,

iremos introduzir a classe Application como sendo a classe que fornece os métodos

necessários para a manipulação de documentos.

Um objeto da classe Application deverá ser o ponto central para onde irão convergir

todas as solicitações originadas na interface com os usuários em resposta aos

comandos por eles emitidos. Como não é função deste objeto implementar

diretamente as solicitações dos usuários, a classe Application irá delegar a maioria das

mensagens para os objetos que efetivamente gerenciam os documentos. Por sua vez,

a lógica para a implementação dos comandos de manipulação definida nestes objetos

irá variar em função do tipo do documento. Entretanto, existem operações, tais como a

exibição do título do documento na barra de título, que são comuns a todos os tipos de

documentos. Isto sugere que o framework seja organizado de tal maneira que uma

classe abstrata, chamada Document, contenha as propriedades comuns a todos os

tipos de documentos, e que as subclasses de Document contenham as propriedades

específicas aos diversos tipos de documentos suportados pela aplicação (Figura 1).

Page 7: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 3

O que não foi dito até agora, e que também não é mostrado no diagrama da Figura 1,

é como um objeto da classe Application irá criar as instâncias das classes específicas

aos diversos tipos de documentos existentes, de tal maneira que ela permaneça

independente do tipo de documento a ser manipulado. Este é o objetivo do padrão

Factory Method.

1.1.3 Estrutura

A Figura 2 mostra um diagrama que ilustra os papéis das classes e das interfaces que

compõem o padrão Factory Method.

Figura 1 – Framework para o Gerenciamento de Documentos.

Figura 2 – Estrutura do Padrão Factory Method.

Page 8: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 4

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

Method:

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

Method.

ConcreteProductX – classes concretas instanciadas pelo Factory Method. Se as

classes não compartilharem uma lógica comum, a classe Product poderá ser

substituída por uma interface.

CreationRequestor – classe que precisa criar um objeto específico. Ela o fará através

de uma instância da classe Factory.

FactoryIF – os objetos que irão instanciar objetos da classe Product terão que

implementar a interface FactoryIF.

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

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

1.1.4 Aplicabilidade

Use o padrão Factory Method quando:

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

• Uma classe tiver que delegar às suas subclasses a responsabilidade pela criação de

objetos de uma aplicação. Isso permitirá localizar nas subclasses o conhecimento

necessário à criação dos objetos.

1.1.5 Conseqüências

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

(ConcreteProductX) instanciado, podendo trabalhar com quaisquer classes

concretas definidas pelo framework.

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

dinamicamente.

1.1.6 Exemplo

Suponha que estejamos desenvolvendo uma extensão da classe Socket com o objetivo

de codificar streams de bytes enviados por uma conexão socket e de decodificar

Page 9: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 5

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

classe de EncryptedSocket.

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

função das restrições legais impostas pelo governo dos EUA à importação e exportação

de algoritmos de codificação, a classe EncryptedSocket deverá ser mantida

independente das classes que implementam os algoritmos de codificação e

decodificação.

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

EncryptedSocket tenha que conhecer antecipadamente as classes que encapsulam tais

algoritmos, nos sugere a aplicação do padrão Factory Method para solucionar este

problema.

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

1.1.7 Código do Exemplo

import java.io.InputStream; import java.io.OutputStream; import java.security.Key; /** * Abstract class to encrypt/decrypt streams of bytes. */ abstract public class Encryption { private Key key;

Figura 3 – Exemplo do Padrão Factory Method.

Page 10: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 6

/** * Constructor * @param key The key to use to perform the encryption. */ public Encryption(Key key) { this.key = key; } // Constructor(Key) /** * Return the key this object used for encryption and decryption. */ protected Key getKey() { return key; } // getKey() /** * This method returns an OutputStream that encrypts the bytes * written to it and writes the encrypted bytes to the given * OutputStream. * @param out The OutputStream that the OutputStream returned by * this method will write encrypted bytes to. */ abstract OutputStream encryptOutputStream(OutputStream out); /** * This method returns an InputStream that decrypts the stream of * bytes that it reads from the given InputStream. * @param in The InputStream that the InputStream returned by this * method will read bytes from. */ abstract InputStream decryptInputStream(InputStream in); } // class Encrypt ****************************************************************************** import java.io.InputStream; import java.io.FilterInputStream; import java.io.FilterOutputStream; import java.io.OutputStream; import java.security.Key; /** * class to RSA encrypt/decrypt streams of bytes. */ public class RSAEncryption extends Encryption { /** * Constructor * @param key The key to use to perform the encryption. */ public RSAEncryption(Key key) { super(key); } // Constructor(Key) /** * This method returns an OutputStream that encrypts the bytes * written to it and writes the encrypted bytes to the given * OutputStream. * @param out The OutputStream that the OutputStream returned by * this method will write encrypted bytes to. */ OutputStream encryptOutputStream(OutputStream out) { return new RSAEncrytpedOutputStream(out); } // encryptOutputStream(OutputStream) /** * This method returns an InputStream that decrypts the stream of * bytes that it reads from the given InputStream.

Page 11: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 7

* @param in The InputStream that the InputStream returned by this * method will read bytes from. */ InputStream decryptInputStream(InputStream in) { return new RSAEncrytpedInputStream(in); } // decryptInputStream(InputStream) /** * Class to encrypt an OuputStream. */ private class RSAEncrytpedOutputStream extends FilterOutputStream { /** * constructor * @param out the OutputStream this object should write encrypted * bytes to. */ RSAEncrytpedOutputStream(OutputStream out) { super(out); } // RSAEncrytpedOutputStream(OutputStream) //... } // class RSAEncrytpedOutputStream private class RSAEncrytpedInputStream extends FilterInputStream { /** * constructor * @param in the InputStream this object should read encrypted * bytes from. */ RSAEncrytpedInputStream(InputStream in) { super(in); } // RSAEncrytpedInputStream(InputStream) //... } // class RSAEncrytpedInputStream } // class RSAEncryption ****************************************************************************** import java.io.InputStream; import java.io.FilterInputStream; import java.io.FilterOutputStream; import java.io.OutputStream; import java.security.Key; /** * class to DES encrypt/decrypt streams of bytes. */ public class DESEncryption extends Encryption { /** * Constructor * @param key The key to use to perform the encryption. */ public DESEncryption(Key key) { super(key); } // Constructor(Key) /** * This method returns an OutputStream that encrypts the bytes * written to it and writes the encrypted bytes to the given * OutputStream. * @param out The OutputStream that the OutputStream returned by * this method will write encrypted bytes to. */ OutputStream encryptOutputStream(OutputStream out) { return new DESEncrytpedOutputStream(out); } // encryptOutputStream(OutputStream) /** * This method returns an InputStream that decrypts the stream of

Page 12: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 8

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

Page 13: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 9

* This method returns an instnace of the appropriate subclass of * Encryption as determined from information provided by the given * Key object's getAlgorithm method. * @param key The key that will be used to perform the encryption. */ public Encryption createEncryption(Key key) throws NoSuchAlgorithmException{ String algorithm = key.getAlgorithm(); if ("DES".equals(algorithm)) return new DESEncryption(key); if ("RSA".equals(algorithm)) return new RSAEncryption(key); throw new NoSuchAlgorithmException(algorithm); } // createEncryption(Key) } // class EncryptionFactory ****************************************************************************** import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.security.Key; import java.security.NoSuchAlgorithmException; /** * This class extends socket so that the stream of bytes that goes over * the net is encrypted. */ public class EncryptedSocket extends Socket { private static Encryption crypt; private Key key; /** * Constructor * @param key The key to use for encryption and decryption. This * object will determine the encryption technique to use * by calling the key object's getAlgorithm() method. * @param factory The Factory object to use to create Encryption * objects. * @exception NoSuchAlgorithmException if the key specifies an * encryption technique that is not available. */ public EncryptedSocket(Key key, EncryptionFactoryIF factory) throws NoSuchAlgorithmException { this.key = key; crypt = factory.createEncryption(key); } // Constructor(Key, EncryptionFactoryIF) /** * Returns an input stream for this socket that decrypts the inbound * stream of bytes. * * @return an input stream for reading decrypted bytes from this * socket. * @exception IOException if an I/O error occurs when creating the * input stream. */ public InputStream getInputStream() throws IOException { return crypt.decryptInputStream(super.getInputStream()); } // getInputStream() /** * Returns an output stream for this socket that encrypts the * outbound stream of bytes. * * @return an output stream for reading decrypted bytes from * this socket.

Page 14: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 10

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

Page 15: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 11

1.2 O Padrão Abstract Factory

1.2.1 Objetivo

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

dependentes, sem especificar as suas classes concretas.

1.2.2 Contexto

Suponha que você tenha que construir um framework para interfaces gráficas (GUI)

que possa ser utilizado sobre vários sistemas gerenciadores de janelas diferentes, tais

como MS Windows, Motif ou MacOS. Você deverá também fazer com que o framework

funcione de acordo com as características visuais de cada uma das plataformas

definidas.

Uma possível solução para este problema será definir uma classe abstrata para cada

tipo de componente visual (text field, push button, list box e etc) e criar subclasses

concretas que suportem tais componentes visuais em cada uma das plataformas em

questão.

Para tornar a implementação robusta, será necessário garantir que os objetos que

implementam os componentes visuais sejam criados de acordo com a plataforma

desejada. Nesse contexto, uma fábrica abstrata irá declarar métodos para a criação de

instâncias de cada uma das classes abstratas que representam componentes visuais.

Enquanto isso, as subclasses concretas irão definir fábricas concretas que irão conter

os métodos necessários à criação dos componentes visuais de acordo com a

plataforma escolhida.

1.2.3 Estrutura

A Figura 4 mostra um diagrama de classes que ilustra o papel de cada classe que

participa do padrão Abstract Factory.

Page 16: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 12

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

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

serviços à plataforma com a qual está trabalhando. A classe Client tem conhecimento

apenas das classes abstratas que representam os componentes visuais abstratos.

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

classes concretas que implementam os componentes visuais.

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

objetivo de criar instâncias das classes concretas que implementam os componentes

visuais na plataforma X.

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

framework.

PlatformXWidgetY – classes concretas que implementam os componentes visuais Y

na plataforma X.

Figura 4 – Estrutura do Padrão Abstract Factory.

Page 17: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 13

1.2.4 Aplicabilidade

Use o padrão Abstract Factory quando:

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

compostos ou representados.

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

produtos.

• Uma família de objetos-produto for projetada para ser usada em conjunto, e for

necessário garantir tal restrição.

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

interfaces, deixando as suas implementações ocultas.

1.2.5 Conseqüências

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

das classes que as usam, dado que a fábrica abstrata encapsula o processo de

criação de tais componentes visuais.

b. Inserir novas classes que dêem suporte a novas plataformas é uma tarefa simples.

Uma classe que represente uma fábrica concreta é usualmente referenciada em

apenas um ponto do framework. De modo similar, é bastante simples alterar uma

fábrica concreta para tratar de uma nova plataforma a ser adicionada ao

framework.

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

componentes visuais, o padrão Abstract Factory assegura que eles usarão um

conjunto de objetos consistentes com a plataforma com a qual desejam interagir.

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

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

plataforma.

1.2.6 Exemplo

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

computadores para um fabricante de equipamentos. Através dos anos, o fabricante em

questão produziu computadores com substanciais diferenças nas suas respectivas

arquiteturas. Nos equipamentos mais antigos foram usados chips de CPU da Enginola,

Page 18: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 14

que são baseados na tradicional arquitetura CISC. Desde então, o nosso fabricante

produziu computadores baseados nas suas próprias arquiteturas RISC, chamadas de

Ember, SuperEmber e UltraEmber. Os componentes principais destas várias

arquiteturas executam funções similares, porém possuem conjuntos de componentes

distintos.

A Figura 5 mostra um diagrama de classes que ilustra uma situação semelhante à

descrita acima, porém com apenas dois componentes para duas arquiteturas distintas.

1.2.7 Código do Exemplo

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

Figura 5 – Exemplo do Padrão Abstract Factory.

Page 19: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 15

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

Page 20: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 16

public abstract class CPU extends ComponentTester { //... } // class CPU ****************************************************************************** /** * 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 ******************************************************************************

Page 21: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 17

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

Page 22: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 18

1.3 O Padrão Singleton

1.3.1 Objetivo

Garantir que uma classe tenha somente uma única instância e fornecer um ponto

global de acesso para tal instância.

1.3.2 Contexto

Algumas classes devem possuir exatamente uma instância. Tais classes geralmente

estão envolvidas no gerenciamento de algum recurso, ou controlando alguma atividade

(controller). O recurso pode ser externo, como no caso em que um objeto gerencia a

reutilização de uma conexão com um gerenciador de banco de dados, ou o recurso

pode ser interno, como no caso em que um objeto mantém estatísticas de erro para

um compilador.

1.3.3 Estrutura

O padrão Singleton é relativamente simples, uma vez que ele envolve uma única classe

(Figura 6).

A classe unitária possui uma variável estática que mantém uma referência para a única

instância que se deseja manipular. Esta instância é criada quando a classe é carregada

na memória ou quando ocorrer a primeira tentativa de acesso à instância. Devemos

implementar a classe unitária de tal modo que não seja possível criar instâncias

adicionais à única instância permitida. Isto significa que devemos nos assegurar que

todos os construtores da classe unitária sejam declarados como privados.

1.3.4 Aplicabilidade

Use o padrão Singleton quando:

Figura 6 – Estrutura do Padrão Singleton.

Page 23: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 19

• Deve haver apenas uma única instância de uma classe, e essa instância deve poder

ser acessada pelos clientes a partir de um ponto bem conhecido.

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

possibilitando assim a reutilização do código existente.

1.3.5 Conseqüências

a. Como a classe Singleton encapsula a sua única instância, ela pode ter o controle

total de como e quando os clientes acessam esta instância.

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

globais.

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

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

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

instâncias, embora ainda se possa ter o controle do número de instâncias que uma

aplicação vá utilizar.

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

estáticos; entretanto, a utilização desta técnica dificulta a mudança de um projeto

para permitir um número maior de instâncias. Além disso, as funções estáticas não

são polimórficas, o que significa que as subclasses não podem redefini-las

polimorficamente.

1.3.6 Exemplo

Suponha que desejemos escrever uma classe que possa ser usada por uma applet para

garantir que não mais que um áudio clip possa ser executado em um dado momento.

Se uma applet contiver dois trechos de código que reproduzam áudio clips

independentemente, então seria possível para ambas reproduzirem os áudio clips ao

mesmo tempo. Tal ocorrência poderia levar uma condição de erro, ou a uma situação

de grande confusão, com o usuário ouvindo um mix de sons não muito agradável.

Para evitar tal situação é necessário que a classe responsável pela reprodução de

áudio clips interrompa a reprodução corrente antes de começar uma nova. Um meio de

implementar essa política é assegurar que exista uma única instância da classe de

reprodução, e que ela seja compartilhada por todas as classes que desejem reproduzir

áudio clips. Se todas as solicitações para reprodução forem direcionadas para um único

Page 24: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 20

objeto, será fácil para este objeto parar a reprodução de um clip antes de iniciar a

seguinte.

A Figura 7 mostra um diagrama com uma classe com as características descritas

acima.

1.3.7 Código do Exemplo

import java.util.HashSet; /** * This class has methods to ensure that an object is never garbage * collected. */ public class ObjectPreserver implements Runnable { // This keeps this class and everything it references from being // garbage collected private static ObjectPreserver lifeLine = new ObjectPreserver(); // Since this class won't be garbage collected, neither will this // HashSet or the object that it references. private static HashSet protectedSet = new HashSet(); /** * Constructor. */ private ObjectPreserver() { new Thread(this).start(); } // constructor() public void run() { try { wait(); } catch (InterruptedException e) { } // try } // run() /** * Garbage collection of objects passed to this method will be * prevented until they are passed to the unpreserveObject method. */ public static void preserveObject(Object o) { protectedSet.add(o); } // preserveObject() /** * Objects passed to this method lose the protection that the

Figura 7 – Exemplo do Padrão Singleton.

Page 25: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 21

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

Page 26: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 22

* 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

Page 27: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 23

1.4 O Padrão Facade

1.4.1 Objetivo

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

para tal, um outro objeto, que é responsável pela comunicação entre os objetos

externos ao conjunto e o conjunto de objetos em questão.

1.4.2 Contexto

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

comum a todos os projetos é minimizar a comunicação e as dependências entre os

subsistemas que compõem uma aplicação. Uma maneira de alcançar este objetivo é

introduzir um objeto facade (fachada), o qual fornece uma interface única para os

recursos e facilidades mais gerais de um subsistema (Figura 8).

1.4.3 Estrutura

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

Facade.

O objeto Client interage com o objeto Facade, que fornece a funcionalidade necessária

para a interação com o restante dos objetos. Se existir alguma funcionalidade

adicional, necessária apenas para alguns clientes, será melhor então que o objeto

Figura 8 – O Uso do Padrão Facade.

Facade

Classes Clientes

Classes do Subsistema

Page 28: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 24

Facade forneça um método em separado para que seja possível acessar diretamente o

objeto responsável por tal funcionalidade, ao invés de incluí-la na interface principal.

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

Client – precisa de uma funcionalidade de um dado subsistema mas não deseja estar

a par da complexidade que envolve a execução de tal funcionalidade.

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

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

apropriados do subsistema.

Classes do Subsistema – implementam as funcionalidades do subsistema. Estas

classes encarregam-se do trabalho que lhes foi atribuído pelo objeto Facade. Como

regra geral, as classes do subsistema não devem manter referências para o objeto

Facade.

1.4.4 Aplicabilidade

Use o padrão Facade quando:

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

Figura 9 – Estrutura do Padrão Facade.

Page 29: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 25

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

uma certa abstração. Ao introduzir uma fachada para reduzir o acoplamento entre

o subsistema e os clientes (ou outros subsistemas), estar-se-á promovendo a

independência e portabilidade dos subsistemas.

• Você desejar estruturar em camadas seus subsistemas. Use uma fachada para

definir o ponto de entrada para cada nível de subsistema.

1.4.5 Conseqüências

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

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

de tal subsistema.

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

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

clientes.

c. Simplifica o porte de um sistema para outras plataformas, uma vez a sua utilização

diminui a ocorrência de alterações em cascata em função da necessidade de uma

alteração em um certo subsistema.

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

caso necessitem fazê-lo. Assim, pode-se escolher entre a facilidade de uso e uma

maior flexibilidade na manipulação das funcionalidades fornecidas por um

subsistema.

1.4.6 Exemplo

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

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

Como pode ser visto, trabalhar diretamente com este conjunto adiciona complexidade

à classe Client. Para interagir com estas classes, um cliente tem que conhecer pelo

menos seis delas, os relacionamentos entre elas, e a ordem na qual os objetos são

criados e trocam mensagens entre si. Se todo cliente tiver que lidar com toda essa

complexidade adicional, será difícil a reutilização de tais classes em um outro contexto.

O padrão Facade fornece um meio de proteger um cliente da complexidade de usar

este conjunto de classes. Isso é feito através de um objeto adicional que oculta a

maior parte das complexas interações existentes. Como pode ser visto na Figura 10, os

Page 30: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 26

clientes têm que estar a par apenas da existência da classe MessageCreator. Isto é

possível porque a lógica interna da classe MessageCreator é responsável pela criação

das partes de uma mensagem de e-mail em uma determinada ordem.

1.4.7 Código do Exemplo

import java.util.Hashtable; import java.util.Vector; /** * Instances of this class are used to create and send e-mail messages. * It assumes that an e-mail message consists of a message body and zero or * more attachments. The content of the message body must be provided as * either a String object or an object that implements an interface called * RichText. Any kind of an object can be provided as the content of an * attachment. */ public class MessageCreator { // Constants to indicate the type of message to create public final static int MIME = 1; public final static int MAPI = 2; public final static int NOTES = 3; public final static int BANYAN = 4; private Hashtable headerFields = new Hashtable(); private RichText messageBody; private Vector attachments = new Vector(); private boolean signMessage; /** * Constructor to create a MessageCreator object that will create an * e-mail message and send it to the given address. It will attempt to * infer the type of message to create from the "to" address. * @param to The address that this object will send a message to.

Figura 10 – Exemplo do Padrão Facade.

Page 31: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 27

* @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);

Page 32: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 28

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

Page 33: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 29

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

Page 34: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 30

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

Page 35: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 31

1.5 O Padrão Composite

1.5.1 Objetivo

O padrão Composite permite a criação de objetos complexos através da composição

recursiva de objetos similares, organizados em uma estrutura de árvore. Ele permite

também a manipulação uniforme de todos os objetos da árvore, obrigando que todos

eles possuam uma superclasse (ou implementem uma interface) comum.

1.5.2 Contexto

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

Ele deverá formatar caracteres de linhas de texto, que são organizadas em colunas,

que são organizadas em páginas. Além disso, tal documento pode conter ainda outros

elementos. Colunas e páginas podem conter frames, que por sua vez podem conter

colunas. Colunas, frames e linhas de texto podem conter imagens. A Figura 11 mostra

um diagrama de classes que exemplifica tais relacionamentos.

Figura 11 – A Estrutura de Um Documento.

Page 36: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 32

Como podemos ver, existe muita complexidade em uma aplicação dessa natureza.

Objetos que representam páginas e frames têm que saber como manipular e combinar

diversos tipos de componentes. O padrão Composite procura remover tal complexidade

fazendo com que estes objetos compostos precisem saber apenas como gerenciar um

único tipo de elemento. Isso é alcançado fazendo com que todos os elementos de um

documento composto tenham uma superclasse comum.

1.5.3 Estrutura

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

pode ser visto na Figura 12.

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

do padrão Composite:

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

objetos que pertencem à árvore de objetos que participam de um objeto composto. Os

objetos compostos tratam normalmente os seus componentes como sendo instâncias

da classe AbstractComponent.

ConcreteComponentX – classes que definem os objetos que representam as folhas

na árvore de componentes de um objeto composto.

Figura 12 – Estrutura do Padrão Composite.

Page 37: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 33

AbstractComposite – superclasse abstrata comum a todos os objetos compostos que

participam do padrão Composite. Esta classe declara e fornece implementações default

para todos os métodos usados no gerenciamento dos componentes de um objeto

composto.

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

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

1.5.4 Aplicabilidade

Use o padrão Composite quando:

• For necessário representar hierarquias parte-todo entre grupos de objetos.

• Quisermos que objetos clientes sejam capazes de ignorar as diferenças existentes

entre uma composição de objetos e cada objeto individualmente. Os clientes

devem ser capazes de tratar todos os objetos de uma composição de maneira

uniforme.

1.5.5 Conseqüências

a. Hierarquias de classes que consistem de objetos primitivos e objetos compostos

são definidas. Os objetos primitivos podem compor objetos mais complexos, que

por sua vez podem compor outros objetos, e assim por diante, de maneira

recursiva.

b. Torna mais simples a implementação de um objeto cliente. Estruturas compostas e

objetos individuais poderão ser tratados de maneira uniforme. Não será necessário

que um cliente saiba se está manipulando uma folha ou um objeto composto.

c. Facilita a inclusão de novos tipos de componentes na estrutura. Novas subclasses

irão funcionar automaticamente com as estruturas existentes, sem que os códigos

dos objetos clientes precisem ser alterados.

d. Pode tornar o projeto excessivamente genérico. Novos componentes poderão ser

acrescentados sem que seja possível restringir os tipos dos componentes de um

dado composto.

Page 38: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 34

1.5.6 Exemplo

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

formatador de documentos apresentado na seção de Contexto. A Figura 13 mostra o

diagrama de classes detalhado do problema em questão.

1.5.7 Código do Exemplo

import java.util.Vector; import java.awt.Font; /** * Instances of this class represent a page. */ class Page extends CompositeDocumentElement { //... } // class Page ****************************************************************************** /** * Instances of this class represent a character in a document. */ class Character extends DocumentElement { //... /** * Return the number of characters that this object contains. */ public int getCharLength() { return 1; } // getCharLength() } // class Character

Figura 13 – Exemplo do Padrão Composite.

Page 39: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 35

****************************************************************************** /** * 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() { cachedCharLength = -1; if (parent != null) parent.changeNotification(); } // changeNotification()

Page 40: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 36

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

Page 41: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 37

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

Page 42: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 38

1.6 O Padrão Observer

1.6.1 Objetivo

Permitir que um objeto possa registrar dinamicamente outros objetos que dele

dependam, de tal modo que o objeto em questão possa notificar os seus dependentes

quando houver mudanças no seu estado.

1.6.2 Contexto

Um efeito colateral bastante comum que resulta do particionamento de um sistema em

uma coleção de classes que cooperam entre si é a necessidade de manter a

consistência entre os objetos relacionados. Não é desejável que tal consistência seja

obtida aumentando-se o acoplamento entre as classes, pois isso reduziria a

oportunidade de reutilização das mesmas.

Seja, por exemplo, um sistema onde uma planilha, um gráfico de barras e um gráfico

de “pizza” exibem informações obtidas de um único objeto (Figura 14). A planilha e os

gráficos não têm conhecimento uns dos outros, permitindo, deste modo, a reutilização

em separado de cada componente gráfico. Entretanto, eles devem se comportar como

se conhecessem uns aos outros, pois quando um usuário alterar os dados através da

planilha os gráficos deverão refletir tal mudança imediatamente, e vice-versa.

Este comportamento implica que a planilha e os gráficos são dependentes do(s)

objeto(s) que armazena(m) os dados e, por conseguinte, deveriam ser notificados

Figura 14 – Os Elementos do Padrão Observer.

Page 43: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 39

sobre quaisquer mudanças no estado do(s) objeto(s). Outro aspecto que deve ser

observado é que não há razão para limitar o número de objetos dependentes, podendo

existir um número qualquer de observadores.

1.6.3 Estrutura

A Figura 15 mostra um diagrama de classes com os principais componentes do padrão

Observer.

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

questão:

ObserverIF – uma interface neste papel declara uma operação tipicamente chamada

de notify ou update. Um objeto observado chama um método que implementa esta

operação para informar que o seu estado mudou. Nesta chamada devem ser passados

todos os argumentos necessários.

Observer – os objetos desta classe implementam a interface ObserverIF e recebem

notificações de mudanças de estado dos objetos da classe Observable.

Figura 15 – Estrutura do Padrão Observer.

Page 44: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 40

ObservableIF – os objetos da classe Observable devem implementar esta interface.

Ela declara duas operações que permitem aos objetos observadores se registrarem (e

removerem o registro) para receber as notificações de mudanças de estado.

Observable – os objetos desta classe devem implementar a interface ObservableIF.

Eles são também responsáveis pelo gerenciamento dos registros dos observadores,

que desejam ser notificados sobre as mudanças de estado ocorridas. As instâncias

desta classe são também responsáveis pela implementação das notificações.

Entretanto, elas não devem executar esta tarefa diretamente, e sim através da

delegação de tal responsabilidade aos objetos da classe Multicaster.

Multicaster – os objetos desta classe gerenciam o registro dos objetos que

implementam a interface ObserverIF e enviam para eles as notificações geradas por

objetos da classe Observable. Ao delegarmos esta tarefa aos objetos da classe

Multicaster estaremos aumentando a chance de reutilizarmos este mecanismo de

notificação em outros contextos.

As figuras a seguir sumarizam a colaboração entre os objetos que participam do

padrão Observer.

Na primeira delas (Figura 16) podemos ver o mecanismo de registro dos observadores,

enquanto na segunda (Figura 17) é ilustrado o processo de notificação dos

observadores quando da mudança de estado do objeto observado.

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

Figura 17 – Notificação de Um Evento.

Page 45: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 41

1.6.4 Aplicabilidade

Use o padrão Observer quando:

• Uma abstração tem dois aspectos, um dependente do outro. Encapsulando estes

aspectos em objetos distintos será possível variá-los e reutilizá-los

independentemente.

• Uma mudança em um objeto exigir mudanças em outros e não soubermos quantos

objetos precisam ser modificados.

• Quisermos que um objeto seja capaz de notificar outros objetos sem que tenha

que ter conhecimento, ou usar informações, de tais objetos. Em outras palavras,

quando quisermos manter baixo o acoplamento entre tais objetos.

1.6.5 Conseqüências

a. Tudo que o objeto observado tem que saber é que ele é observado por uma

coleção de observadores, todos implementando uma interface muito simples

(ObserverIF). Como o objeto observado não precisa conhecer a classe concreta de

nenhum dos seus observadores, o acoplamento entre o objeto observado e seus

observadores é mínimo.

b. Diferentemente de uma solicitação ordinária, a notificação que um objeto

observado envia não precisa especificar o seu receptor. A notificação é transmitida

automaticamente para todos os observadores registrados; implementando, assim,

um mecanismo de broadcasting.

c. Como um observador não tem conhecimento da presença de outros observadores,

não é possível avaliar à priori o custo global de uma mudança no estado do objeto

observado. Desta forma, uma alteração aparentemente simples no objeto

observado pode disparar uma cascata de modificações nos objetos observadores e

nos seus respectivos objetos dependentes.

1.6.6 Exemplo

Seja uma empresa que produz detectores de fumaça, sensores de movimento e outros

dispositivos de segurança. Para aproveitar novas oportunidades de mercado, esta

empresa planeja introduzir uma nova linha de dispositivos. Tais dispositivos devem

estar aptos a enviar sinais para uma placa de segurança que possa ser instalada na

Page 46: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 42

maioria dos computadores comercializados atualmente. O objetivo é permitir que as

empresas que desenvolvem sistemas de monitoramento possam integrar estes novos

dispositivos nas suas soluções. Para facilitar a tarefa de integração, a API que será

fornecida com a nova linha de dispositivos foi implementada com o padrão Observer.

A Figura 18 mostra o design da API que será fornecida junto com os novos dispositivos

de segurança.

1.6.7 Código do Exemplo

import java.util.ArraySet; import java.util.Iterator; /** * Classes that implement this interface can register to receive * security notifications from SecurityNotifier objects. */ public interface SecurityObserver { public final int ALARM = 1; public final int LOW_POWER = 2; public final int DIAGNOSTIC = 3; /** * This is method is called to deliver a security notification to * this object. * @param device A number that identifies the device that originated * this notification. * @param event This should be one of the constants defined in this * interface. */ public void notify(int device, int event); } // interface SecurityObserver ******************************************************************************

Figura 18 – Exemplo do Padrão Observer.

Page 47: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 43

/** * 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; } // 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) {

Page 48: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 44

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) { Iterator iterator = observers.iterator(); while (iterator.hasNext()) { ((SecurityObserver)iterator.next()).notify(device, event); } // while } // notify(int, int) } // class SecurityNotifier

Page 49: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 45

1.7 O Padrão Strategy

1.7.1 Objetivo

Encapsular algoritmos relacionados em um conjunto de classes - subclasses de uma

superclasse comum - de tal maneira que a escolha do algoritmo desejado possa ser

feita dinamicamente.

1.7.2 Contexto

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

maneira fixa e rígida tais algoritmos nas classes que os utilizam não é desejável por

várias razões:

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

incluem o código de quebra de linhas. Isso os torna maiores e mais difíceis de

manter, especialmente se suportam múltiplos algoritmos de quebra de linhas.

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

boa estratégia suportar múltiplos algoritmos de quebra de linha se não for

necessário usá-los.

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

é parte integrante de um objeto.

Podemos evitar estes problemas definindo classes que encapsulem diferentes

algoritmos de quebra de linhas. Um algoritmo encapsulado desta maneira é chamado

de strategy (estratégia).

1.7.3 Estrutura

A Figura 19 mostra um diagrama de classes com os principais componentes do padrão

Strategy.

Page 50: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 46

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

questão:

Client – uma classe Client delega a execução de uma operação a uma classe abstrata

ou a uma interface. Ela o faz sem conhecer a classe do objeto ao qual a operação é

delegada e como tal classe implementa a operação solicitada.

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

definidas pelas suas subclasses. Pode-se usar uma interface, ao invés de uma classe

abstrata, para exercer este papel.

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

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

1.7.4 Aplicabilidade

Use o padrão Strategy quando:

• Muitas classes relacionadas diferem somente no seu comportamento. As

estratégias fornecem uma maneira de configurar uma classe com um dentre vários

comportamentos.

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

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

• Um algoritmo usa dados que os outros objetos deveriam desconhecer. Use o

padrão Strategy para evitar a exposição das estruturas de dados complexas,

específicas do algoritmo.

Figura 19 – Estrutura do Padrão Strategy.

Page 51: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 47

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

como múltiplos comandos condicionais.

1.7.5 Conseqüências

a. Podemos usar este padrão para criar diferentes hierarquias de estratégias, que irão

definir famílias de algoritmos e comportamentos relacionados.

b. Uma alternativa ao uso do padrão Strategy seria especializar a classe Client para

lhe dar diferentes comportamentos. Entretanto, isso congelaria o comportamento

do cliente, misturando a implementação do algoritmo em questão com o resto do

código da classe Client, e tornando esta classe mais difícil de compreender, manter

e estender. Além disso, teríamos a desvantagem de não poder variar os algoritmos

dinamicamente. A solução dada pelo padrão Strategy permite variar o algoritmo

independentemente dos seus clientes, tornando mais fácil trocá-los, compreendê-

los e estendê-los.

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

cliente pode ser obrigado, em algumas situações, a conhecer a maneira pela qual

as estratégias diferem uma das outras. Isso poderia levar a uma situação

indesejável, onde tais objetos teriam que estar expostos aos detalhes de

implementação das diferentes estratégias.

1.7.6 Exemplo

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

requisitos deste programa é estar apto a exibir os feriados celebrados por diferentes

nações e diferentes grupos religiosos. O usuário deste programa deve poder escolher

quais grupos de feriados devem ser exibidos.

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

cada grupo de feriados em uma classe separada, de tal maneira que tenhamos um

pequeno conjunto de classes ao qual podemos facilmente adicionar novos elementos.

Gostaríamos também que as classes que fossem utilizar os serviços para exibição dos

calendários não precisassem estar a par da existência de nenhum feriado, ou conjunto

de feriados, específico.

A Figura 20 mostra como utilizar o padrão Strategy para implementar uma solução

para o problema descrito acima.

Page 52: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 48

1.7.7 Código do Exemplo

import java.util.Date; /** * Skeletal definition of class to display a calendar */ class CalendarDisplay { private Holiday holiday; private static final String[]noHoliday = new String[0]; //... /** * Instances of this private class are used to cache information about * dates that are to be displayed. */ private class DateCache { private Date date; private String[] holidayStrings; DateCache(Date dt) { date = dt; //... if (holiday == null) { holidayStrings = noHoliday; } else { holidayStrings = holiday.getHolidays(date); } // if //... } // constructor(Date) } // class DateCache } // class CalendarDisplay ****************************************************************************** import java.util.Date; /** * This abstract class is the superclass of classes that can determine * if a date is a holiday. Subclasses of this class will be specific to * nations or religions. */ public abstract class Holiday { protected final static String[] noHoliday = new String[0]; /** * This method returns a array of strings that describe the holidays * that fall on the given date. If no holidays fall on the given * date, then this method returns an array of length zero. * @param dt The date to check. */

Figura 20 – Exemplo do Padrão Strategy.

Page 53: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 49

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

Page 54: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 50

1.8 O Padrão State

1.8.1 Objetivo

Permitir a um objeto alterar o seu comportamento quando o seu estado interno muda.

O objeto parecerá ter mudado sua classe.

1.8.2 Contexto

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

objeto, cujos valores podem mudar dinamicamente. Para que possamos tratar

adequadamente as diversas mudanças de estado que um objeto irá experimentar ao

longo do seu ciclo de vida é necessário criar uma estrutura que seja escalável em

relação aos estados, evitando assim a implementação de uma solução complexa para

uma máquina de estados que descreva o comportamento de tal objeto.

1.8.3 Estrutura

A Figura 21 mostra um diagrama de classes com os principais componentes do padrão

State.

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

questão:

Figura 21 – Estrutura do Padrão State.

Page 55: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 51

Context – classe cujas instâncias exibem um comportamento que deve ser modelado

por uma máquina de estados. As instâncias desta classe determinam os seus

respectivos estados correntes através de uma referência (currentState) para uma

instância de uma subclasse concreta da classe ContextState.

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

representar os estados de uma instância da classe Context. A classe ContextState

define os seguintes métodos:

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

máquina de estados de um objeto, e retorna um objeto que corresponde ao

estado inicial do objeto em questão.

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

um evento e retorna o novo estado corrente do objeto em questão. Cada

subclasse concreta de ContextState deve redefinir este método de acordo

com as necessidades do estado que ela representa. Quaisquer ações de saída

(exit actions) relativas ao estado corrente devem ser executadas neste

método (desde que o evento tenha causado uma transição de saída do

estado corrente).

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

actions) de um estado. A implementação default, fornecida pela classe

ContextState, não contém comandos (vazia), e deve ser redefinida pelas

subclasses que representam estados que contenham ações de entrada.

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

específicas de cada um dos estados.

ConcreteStateX – subclasses concretas de ContextState, que representam os estados

existentes em uma máquina de estados. Estas classes têm que implementar o método

processEvent para fornecerem respostas adequadas aos eventos ocorridos.

1.8.4 Aplicabilidade

Use o padrão State quando:

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

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

estado.

Page 56: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 52

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

Freqüentemente tais estruturas irão conter grandes porções de código que irão se

repetir através de várias operações, gerando replicação desnecessária de código. O

padrão State coloca cada ramo da estrutura condicional em uma classe separada,

permitindo, assim, tratar os estados como se fossem objetos e tornar mais simples

a implementação de uma máquina de estados.

1.8.5 Conseqüências

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

comportamentos relativos aos estados e organizando tais comportamentos em

objetos específicos. Desse modo, novos estados e transições podem ser facilmente

adicionados à máquina de estados através da definição de novas subclasses.

b. Quando um objeto define o seu estado corrente unicamente em termos dos valores

dos seus atributos, a suas transições de estado não têm representação explícita;

elas ficam caracterizadas apenas pela mudança de valores de alguns atributos. A

introdução de objetos distintos para representar os diferentes estados de máquina

de estados torna as transições mais explícitas.

1.8.6 Exemplo

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

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

ações:

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

arquivo, e também na área de trabalho do programa.

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

um arquivo.

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

na área de trabalho do programa.

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

arquivo.

É possível projetar este diálogo de modo que ele não tenha estados. Se um diálogo

não contém estados, ele irá se comportar sempre da mesma maneira. O botão de OK

estará sempre ativado, mesmo se o usuário tiver recém restaurado os valores dos

Page 57: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 53

parâmetros a partir do arquivo. Se não houver outras considerações, o uso de uma

abordagem sem estados no design deste diálogo pode ser satisfatório.

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

problemas. A alteração dos valores dos parâmetros na área de trabalho do programa

poderá interromper o funcionamento do mesmo. O armazenamento dos valores em um

arquivo pode tomar um tempo consideravelmente longo se ele estiver localizado em

um servidor remoto. Um modo de evitar operações desnecessárias é inserir estados no

diálogo de tal maneira que as operações não possam ser executadas quando não

forem necessárias. Deste modo, o diálogo só irá permitir a execução destas operações

quando os valores dos parâmetros forem diferentes dos valores armazenados no

arquivo, ou dos valores existentes na área de trabalho do programa. A Figura 22

mostra um diagrama de estados que representa o comportamento desejado.

Para implementar a máquina de estados da Figura 22, iremos criar cinco classes, como

é mostrado na Figura 23. Quatro delas irão corresponder a cada um dos quatro

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

Page 58: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 54

estados mostrado na máquina de estados da Figura 22, e a quinta será a superclasse

comum às quatro primeiras.

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

método recebe como parâmetro um identificador de evento e retorna um objeto que

representa o estado seguinte. Cada subclasse de DirtyState irá redefinir o método

processEvent de acordo com as suas necessidades. A classe DirtyState possui também

um método estático chamado start. Este método irá por as coisas para funcionar

criando uma instância para cada subclasse de DirtyState e retornando o objeto que

representa o estado inicial. O método start cria também uma instância de DirtyState e

guarda nas variáveis notDirty, fileDirty, paramDirty e bothDirty as referências para os

objetos que representam os estados.

1.8.7 Código do Exemplo

import java.awt.*; class DirtyState { // Symbolic constants for events public static final int DIRTY_EVENT = 1; public static final int APPLY_EVENT = 2; public static final int SAVE_EVENT = 3; public static final int REVERT_EVENT = 4; // Symbolic constants for states private final BothDirty bothDirty = new BothDirty(); private final FileDirty fileDirty = new FileDirty(); private final ParamDirty paramDirty = new ParamDirty(); private final NotDirty notDirty = new NotDirty(); private Parameters parameters;

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

Page 59: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 55

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; 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

Page 60: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 56

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); } // 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() {

Page 61: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 57

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(); 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:

Page 62: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 58

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; } // saveParam() boolean applyParam() { //... return true; } // applyParam() boolean revertParam() { //... return true; } // revertParam() } // class Parameters ****************************************************************************** import java.awt.*; class Procedural extends Dialog { // Symbolic constants for events public static final int DIRTY_EVENT = 1; public static final int APPLY_EVENT = 2; public static final int SAVE_EVENT = 3; public static final int REVERT_EVENT = 4; // Symbolic constants for states private static final int BOTH_DIRTY = 101; private static final int FILE_DIRTY = 102; private static final int PARAM_DIRTY = 103; private static final int NOT_DIRTY = 104; Button applyButton, saveButton, revertButton; private int state = NOT_DIRTY; /** * Constructor * @param parent The parent Frame */ Procedural(Frame parent) { super(parent, "Parameter Editor"); //... gotoState(NOT_DIRTY); } // Constructor()

Page 63: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 59

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

Page 64: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 60

// 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 state = newState; } // gotoState(int) //... private boolean saveParam() { //... return true; } // saveParam() private boolean applyParam() { //... return true; } // applyParam() private boolean revertParam() { //... return true; } // revertParam() } // class Procedural

Page 65: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 61

1.9 O Padrão Proxy

1.9.1 Objetivo

Fazer com que o envio de mensagens para um dado objeto ocorra indiretamente

através de um objeto proxy, que atua como um surrogate (representante) do objeto

em questão. O objeto proxy recebe as mensagens e as repassa para o objeto alvo,

sem que os clientes tenham que ficar a par do fato de que eles não estão interagindo

diretamente com o objeto alvo.

1.9.2 Contexto

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

objeto. Os clientes enviam mensagens para um objeto proxy. Este, por sua vez, não

fornece diretamente o serviço solicitado. Ao invés disso, o objeto proxy aciona os

métodos do objeto responsável por prover os serviços solicitados pelos clientes. A

Figura 24 mostra um diagrama de colaboração que ilustra o que foi dito.

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

Entre eles estão:

• A resposta à solicitação de um serviço muito demorado pode ser disponibilizada

imediatamente pelo objeto proxy, enquanto o objeto alvo cuida da execução da

tarefa. Isso libera o cliente para executar outras tarefas, enquanto o serviço

solicitado é executado pelo objeto alvo.

• Um proxy cria a ilusão de que um objeto remoto esteja carregado no mesmo

espaço de endereçamento do objeto cliente. Este tipo de proxy é conhecido como

Proxy Remoto; sendo usado pelo Remote Method Invocation (RMI), presente na

plataforma Java.

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

proxy é conhecido como Proxy de Acesso.

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

Page 66: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 62

• Um proxy pode criar a ilusão de que um objeto servidor exista antes mesmo da sua

criação. Isso pode ser muito útil quando o custo de criação do objeto servidor for

muito alto e o uso dos seus serviços não for muito freqüente. Este tipo de proxy é

conhecido como Proxy Virtual.

1.9.3 Estrutura

A Figura 25 mostra um diagrama de classes com os principais componentes do padrão

Proxy.

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

obrigando que todos os acessos a tal objeto seja feito através de um proxy. Para

atingir a desejada transparência, o objeto proxy e o objeto provedor de serviços

devem compartilhar a mesma superclasse ou implementar uma interface comum,

como mostra a Figura 25.

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

gerenciamento em particular. Entretanto, o padrão Proxy não será muito útil a não ser

que seja empregado algum tipo de política de gerenciamento de acesso ao objeto

provedor de serviços.

1.9.4 Aplicabilidade

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

versátil, ou sofisticada, do que um simples apontador para um objeto. Entre as

situações mais comuns nas quais o padrão Proxy é aplicável podemos citar:

Figura 25 – Estrutura do Padrão Proxy.

Page 67: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 63

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

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

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

acesso a um objeto.

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

ponteiro. Os casos típicos incluem:

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

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

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

primeira vez.

Verificar se o objeto real está bloqueado antes de ser acessado, para assegurar

que nenhum outro objeto possa alterar o seu conteúdo.

1.9.5 Conseqüências

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

demanda.

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

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

1.9.6 Exemplo

O padrão Proxy não é muito útil na sua forma mais elementar. Ele deve ser combinado

com um mecanismo de gerência de acesso para que possamos obter algo de útil. O

exemplo a seguir usa o padrão Proxy para postergar uma operação custosa até que ela

seja realmente necessária. Se não for necessário, a operação não será nunca

executada.

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

equivalente a esta. A diferença está no modo como a subclasse em questão trata a

operação de clonagem, que pode ser extremamente custosa.

1.9.7 Código do Exemplo

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

Page 68: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 64

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

Page 69: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 65

* 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

Page 70: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 66

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

Page 71: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Introdução aos Padrões de Design

Ivan Mathias Filho PUC-Rio 67

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

Page 72: APOSTILA DE PADRÕES DE DESIGN - inf.puc-rio.brivan/INF1013/Apostila/ApostilaPadroesDesign.pdf · Introdução aos Padrões de Design Ivan Mathias Filho PUC-Rio 3 O que não foi dito

Bibliografia

Ivan Mathias Filho PUC-Rio 68

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.