Académique Documents
Professionnel Documents
Culture Documents
Sumário
1.5.5 Conseqüências...........................................................................................................33
1.5.6 Exemplo....................................................................................................................33
1.5.7 Código do Exemplo ....................................................................................................33
Bibliografia ........................................................................ 66
Lista de Figuras
Capítulo 1
Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
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
Um framework para este tipo de aplicação deve fornecer suporte de alto-nível para as
operações usuais, tais como a criação, a abertura e a gravação de documentos. O
suporte a estas operações geralmente inclui um conjunto consistente de métodos que
são chamados em resposta aos comandos dos usuários. Para ilustrar esta discussão,
iremos introduzir a classe Application como sendo a classe que fornece os métodos
necessários para a manipulação de documentos.
Um objeto da classe Application deverá ser o ponto central para onde irão convergir
todas as solicitações originadas na interface com os usuários em resposta aos
comandos por eles emitidos. Como não é função deste objeto implementar
diretamente as solicitações dos usuários, a classe Application irá delegar a maioria das
mensagens para os objetos que efetivamente gerenciam os documentos. Por sua vez,
a lógica para a implementação dos comandos de manipulação definida nestes objetos
irá variar em função do tipo do documento. Entretanto, existem operações, tais como a
exibição do título do documento na barra de título, que são comuns a todos os tipos de
documentos. Isto sugere que o framework seja organizado de tal maneira que uma
classe abstrata, chamada Document, contenha as propriedades comuns a todos os
tipos de documentos, e que as subclasses de Document contenham as propriedades
específicas aos diversos tipos de documentos suportados pela aplicação (Figura 1.1).
O que não foi dito até agora, e que também não é mostrado no diagrama da Figura
1.1, é como um objeto da classe Application irá criar as instâncias das classes
específicas aos diversos tipos de documentos existentes, de tal maneira que ela
permaneça independente do tipo de documento a ser manipulado. Este é o objetivo do
padrão Factory Method.
1.1.3 Estrutura
A Figura 1.2 mostra um diagrama que ilustra os papéis das classes e das interfaces
que compõem o padrão Factory Method.
Product – superclasse abstrata dos objetos que serão produzidos pelo Factory
Method.
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.
1.1.4 Aplicabilidade
• Uma classe não puder antecipar as classes dos objetos que ele precisa criar.
• Uma classe tiver que delegar as suas subclasses a responsabilidade pela criação de
objetos de uma aplicação. Isso permitirá localizar nas subclasses o conhecimento
necessário à criação dos objetos.
1.1.5 Conseqüências
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
A Figura 1.3 mostra um diagrama de classes que ilustra a situação descrita acima.
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;
/**
* 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.
/**
* 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
******************************************************************************
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
/**
* 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
******************************************************************************
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 {
/**
******************************************************************************
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.
1.2.1 Objetivo
1.2.2 Contexto
Suponha que você tenha que construir um framework para interfaces gráficas (GUI)
que possa ser utilizado sobre vários sistemas gerenciadores de janelas diferentes, tais
como MS Windows, Motif ou MacOS. Você deverá também fazer com que o framework
funcione de acordo com as características visuais de cada uma das plataformas
definidas.
Uma possível solução para este problema será definir uma classe abstrata para cada
tipo de componente visual (text field, push button, list box e etc) e criar subclasses
concretas que suportem tais componentes visuais em cada uma das plataformas em
questão.
Para tornar a implementação robusta, será necessário garantir que os objetos que
implementam os componentes visuais sejam criados de acordo com a plataforma
desejada. Neste contexto, uma fábrica abstrata irá declarar métodos para a criação de
instâncias de cada uma das classes abstratas que representam componentes visuais.
Enquanto isso, as subclasses concretas irão definir fábricas concretas que irão conter
os métodos necessários à criação dos componentes visuais de acordo com a
plataforma escolhida.
1.2.3 Estrutura
A Figura 1.4 mostra um diagrama de classes que ilustra o papel que cada classe
representa no padrão Abstract Factory.
A seguir é apresentado o papel de cada uma das classes do padrão Abstract Factory:
WidgetY – classes abstratas que definem os componentes visuais que fazem parte do
framework.
1.2.4 Aplicabilidade
• Uma família de objetos-produto for projetada para ser usada em conjunto, e for
necessário garantir tal restrição.
1.2.5 Conseqüências
d. Inserir novas classes que dêem suporte a novas plataformas é uma tarefa simples.
Uma classe que represente uma fábrica concreta é usualmente referenciada em
apenas um ponto do framework. De modo similar, é bastante simples alterar uma
fábrica concreta para tratar de uma nova plataforma a ser adicionada ao
framework.
1.2.6 Exemplo
arquiteturas. Nos equipamentos mais antigos foram usados chips de CPU da Enginola,
que são baseados na tradicional arquitetura CISC. Desde então, o nosso fabricante
produziu computadores baseados na sua própria arquitetura RISC, chamdas de Ember,
SuperEmber e UltraEmber. Os componentes principais destas várias arquiteturas
executam funções similares, porém possuem conjuntos de componentes distintos.
A Figura 1.5 mostra um diagrama de classes que ilustra uma situação semelhante à
descrita acima, porém com apenas dois componentes para duas arquiteturas distintas.
/**
* 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();
//...
/**
* 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
/**
* This is an abstract class for objects that perform remote tests on CPUs.
*/
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
/**
* 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
1.3.1 Objetivo
Garantir que uma classe tenha somente uma única instância e fornecer um ponto
global de acesso para tal instância.
1.3.2 Contexto
Algumas classes devem possuir exatamente uma instância. Tais classes geralmente
estão envolvidas no gerenciamento de algum recurso, ou controlando alguma atividade
(controller). O recurso pode ser externo, como no caso em que um objeto gerencia a
reutilização de uma conexão com um gerenciador de banco de dados, ou o recurso
pode ser interno, como no caso em que um objeto mantém estatísticas de erro para
um compilador.
1.3.3 Estrutura
O padrão Singleton é relativamente simples, uma vez que ele envolve uma única classe
(Figura 1.6).
A classe unitária possui uma variável estática que mantém uma referência para a única
instância que se deseja manipular. Esta instância é criada quando a classe é carregada
na memória ou quando ocorrer a primeira tentativa de acesso à instância. Devemos
implementar a classe unitária de tal modo que não seja permitida a criação de
instâncias adicionais à única instância permitida. Isto significa que devemos nos
assegurar que todos os construtores da classe unitária sejam declarados como
privados.
1.3.4 Aplicabilidade
• 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.
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.
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
objeto, será fácil para este objeto parar a reprodução de um clip antes de iniciar a
seguinte.
A Figura 1.7 mostra um diagrama com uma classe com as características descritas
acima.
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();
/**
* Constructor.
*/
private ObjectPreserver() {
new Thread(this).start();
} // constructor()
/**
* 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
* 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()
/**
* 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
1.4.1 Objetivo
1.4.2 Contexto
1.4.3 Estrutura
A Figura 1.9 mostra um diagrama de classes que ilustra a estrutura geral do padrão
Facade.
O objeto Client interage com o objeto Facade, que fornece a funcionalidade necessária
para a interação com o restante dos objetos. Se existir alguma funcionalidade
adicional, necessária apenas para alguns clientes, será melhor então que o objeto
Facade forneça um método para que seja possível acessar diretamente o objeto
responsável por tal funcionalidade, ao invés de incluí-la na interface do objeto Facade.
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.
1.4.4 Aplicabilidade
• 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
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.
1.4.6 Exemplo
Como pode ser visto, trabalhar diretamente com este conjunto adiciona complexidade
à classe Client. Para interagir com estas classes, um cliente tem que conhecer pelo
menos seis delas, os relacionamentos entre elas, e a ordem na qual os objetos são
criados e trocam mensagens entre si. Se todo cliente tiver que lidar com toda essa
complexidade adicional, será difícil a reutilização de tais classes em um outro contexto.
O padrão Facade fornece um meio de proteger um cliente da complexidade de usar
este conjunto de classes. Isto é feito através de um objeto adicional que oculta a maior
parte das complexas interações existentes. Como pode ser visto na Figura 1.10, os
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.
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;
/**
* 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.
*/
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);
/**
* 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.
*/
//...
} // 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
******************************************************************************
/**
* 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
1.5.1 Objetivo
1.5.2 Contexto
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
1.5.4 Aplicabilidade
xxxxxxxxx:
• yyyy
• zzzzz.
1.5.5 Conseqüências
f. Zzzzzzzzzzzz.
1.5.6 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
/**
* 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();
/**
* 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()
/**
* 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;
//...
/**
* 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)
/**
* Return the number of characters that this object contains.
*/
public abstract int getCharLength() ;
} // class DocumentElement
/**
/**
* 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
1.6.1 Objetivo
1.6.2 Contexto
1.6.3 Estrutura
1.6.4 Aplicabilidade
xxxxxxxxx:
• yyyy
• zzzzz.
1.6.5 Conseqüências
g. Zzzzzzzzzzzz.
1.6.6 Exemplo
import java.util.ArraySet;
import java.util.Iterator;
/**
* Classes that implement this interface can register to receive
* security notifications from SecurityNotifier objects.
*/
public interface SecurityObserver {
public final int ALARM = 1;
public final int LOW_POWER = 2;
public final int DIAGNOSTIC = 3;
/**
* This is method is called to deliver a security notification to
* this object.
* @param device A number that identifies the device that originated
* this notification.
* @param event This should be one of the constants defined in this
* interface.
*/
public void notify(int device, int event);
} // interface SecurityObserver
/**
* Instances of this class receive a notification from an object that is
* can only deliver it to an object the implements the SecurityObserver
* interface and apsses it on to a SecurityMonitor object that does not
* implement SecurityObserver.
*/
class SecurityAdapter implements SecurityObserver {
private SecurityMonitor sm;
/**
* Constructor
*/
SecurityAdapter(SecurityMonitor sm) {
this.sm = sm;
} // 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)
/**
* When an instance of this class receives a notification from a
* security device, it passes it on to all of its registered observers.
*/
class SecurityNotifier {
private ArraySet observers = new ArraySet();
//...
/**
* Add a new observer to this object.
*/
public void addObserver(SecurityObserver observer) {
observers.add(observer);
} // addObserver(SecurityObserver)
/**
* Remove an observer from this object
*/
public void removeObserver(SecurityObserver observer) {
observers.remove(observer);
} // removeObserver(SecurityObserver)
/**
* This method is called when this object needs to pass on a
1.7.1 Objetivo
1.7.2 Contexto
1.7.3 Estrutura
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.
1.7.4 Aplicabilidade
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.
1.7.6 Exemplo
A Figura 1.18 mostra como utilizar o padrão Strategy para implementar uma solução
para o problema descrito acima.
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.
*/
******************************************************************************
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)
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
1.8.3 Estrutura
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.
1.8.4 Aplicabilidade
1.8.5 Conseqüências
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
É possível projetar este diálogo de modo que ele não tenha estados. Se um diálogo
não contém estados, ele irá se comportar sempre da mesma maneira. O botão de OK
estará sempre ativado, mesmo se o usuário tiver recém restaurado os valores dos
parâmetros a partir do arquivo. Se não houver outras considerações, o uso de uma
abordagem sem estados no design deste diálogo pode ser satisfatório.
Para implementar a máquina de estados da Figura 1.20, iremos criar cinco classes,
como é mostrado na Figura 1.21. Quatro delas irão corresponder a cada um dos
quatro estados mostrado na máquina de estados da Figura 1.20, e a quinta será a
superclasse comum às quatro primeiras.
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;
/**
* 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
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() {
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:
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.*;
/**
* Constructor
* @param parent The parent Frame
*/
Procedural(Frame parent) {
super(parent, "Parameter Editor");
//...
gotoState(NOT_DIRTY);
} // Constructor()
/**
* respond to events based on the current state.
* @param event An event code.
* @exception IllegalArgumentException if event is an unexpected value.
* @exception InternalError if the current state is corrupted.
*/
private void processDirtyStateEvent(int event) {
switch (state) {
case BOTH_DIRTY:
switch (event) {
case DIRTY_EVENT:
// Do nothing
break;
case APPLY_EVENT:
if (applyParam())
gotoState(FILE_DIRTY);
break;
case SAVE_EVENT:
if (saveParam())
gotoState(PARAM_DIRTY);
break;
case REVERT_EVENT:
if (revertParam())
gotoState(PARAM_DIRTY);
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:
// 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()
1.9.1 Objetivo
Fazer com que o envio de mensagens para um dado objeto ocorra indiretamente
através de um objeto proxy, que atua como um surrogate (representante) do objeto
em questão. O objeto proxy recebe as mensagens e as repassa para o objeto alvo,
sem que os clientes tenham que ficar a par do fato de que eles estão não estão
interagindo diretamente com o objeto alvo.
1.9.2 Contexto
Existem vários tipos de serviços que os objetos proxy estão habilitados a fornecer.
Entre eles estão:
• Um proxy cria a ilusão de que um objeto localizado em uma máquina remota esteja
carregado no mesmo espaço de endereçamento do objeto cliente. Este tipo de
proxy é conhecido como Proxy Remoto; sendo usado pelo Remote Method
Invocation (RMI), presente na plataforma Java.
• Um proxy pode 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
1.9.4 Aplicabilidade
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.
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.
import java.util.Enumeration;
import java.util.Hashtable;
/**
/**
* 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)
/**
/**
* 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
* <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();
}
/**
* Construct an empty hashtable.
/**
* 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
Bibliografia