Monday, May 11, 2009Singleton em Java

Simples post para demonstrar o uso do Design Pattern Singleton em Java.

Assegura que uma determinada classe tenha apenas uma única instância, e apenas um meio de acesso a mesma.

Por exemplo em um sistema operacional é o uso de impressoras, temos vários clientes conectados a impressora, mas a mesma possui apenas um spool de documentos.

Segundo Gof temos o diagrama abaixo:

singleton_gofE como codificar isso? Imagine um cenário seguinte, precisamos desenvolver um chat entre múltiplos clientes,

onde todos podem se comunicar com todos, de que forma podemos implementar e assegurar que todos os usuários do nosso chat acessem exclusivamente a mesma instância da classe?


package br.com.ronaldorigoni.singleton;
/**
* Exemplo de Singleton - ChatRoom
* @author ronaldorigoni.com.br
*/
public class ChatRoom {
/**
* Única instancia da sala de bate papo, deve ser privada para
* não permitir acesso direto.
*/
private static ChatRoom instance = null;

/**
* Construtor privado para não permitir a
* criação de novas instancias da classe.
*/
private ChatRoom(){
// aqui codigo para a inicialização do chat.
// verifica configurações em banco ou outras
// operações.
}
/**
* Única forma de acesso para a instancia "instance" da classe
* @return  - A única instância da sala de Chat
*/
public static ChatRoom getInstance(){
if(instance == null)
instance = new ChatRoom();
return instance;
}

}

Através do método getInstance(), qualquer cliente tem apenas uma forma de acesso e em uma única instancia da sala de Chat.

Agora criaremos um método writeMessage(String message):


/**
* Envia uma mensagem a todos os usuários
* @param message
*/
public void writeMessage(String message){
for(User user : userList){
user.sendMessage(message);
}
}

Mesmo o método sendo public, só conseguiremos acessar ele através de uma instancia da classe desta forma:


ChatRoom.getInstance().writeMessage("Mensagem de teste");

Mas cuidado, em um ambiente MultiThread não está seguro que existe apenas uma instancia da classe, se em um determinado momento duas threads acessarem o método getInstance() ao mesmo tempo será criada duas instancias.

Como bloquear isso?  Adicionaremos um bloco s no método getInstance(), desta forma garantimos que toda Thread que entrar neste bloco irá bloquear o objeto instance para si mesmo, e só fará o desbloqueio quando o bloco de código terminar, assim uma vez desbloqueado estará disponível para uma nova thread utilizar.


public static ChatRoom getInstance() {

// garante que apenas uma thread por vez

// executará este bloco de código
synchronized (instance) {
if (instance == null)
instance = new ChatRoom();
}
return instance;
}

Para deixarmos nosso exemplo mais seguro, muitos desenvolvedores acham este método bizarro, mas eu o utilizei varias vezes, altere o método getInstance() para o modificador de acesso private, em seguida crie uma classe interna conforme abaixo, esta classe deve ser public static abstract, mas espere aí? Classe estatica é possível? Sim classe static é possível desde que a mesma seja aninhada ( classe interna).


private static ChatRoom getInstance() {
synchronized (instance) {
if (instance == null)
instance = new ChatRoom();
}
return instance;
}

E agora nossa classe interna aninhada:


public abstract static class ChatRoomHolder{
public static ChatRoom getInstance(){
return ChatRoom.getInstance();
}
}

Repare que a classe é abstrata, com isso fica impossível instanciá-la, mas por ela ser interna ela tem acesso aos métodos private de nossa ChatRoom, invoca o método getInstance() e retorna a instancia criada no construtor.

Desta forma estamos garantindo de duas maneiras que a classe ChatRoom tenha apenas uma única instancia:

  • Com o uso de construtor privado e um bloco sincronizado no método getInstance().
  • Com o uso de uma classe abstrata interna que não permite ser instanciada por ser abstrata.

Singleton pode ser aplicado em diversos casos, ( conexão a banco de dados, mecanismo de Log, Objetos de configuração, etc…).

Dúvidas, ronaldo@ronaldorigoni.com.br

Até um próximo post.

Monday, May 4, 2009Observer em Java

Salve galera.
Hoje iremos abordar um padrão de projeto designado Observer.
Observer é um padrão de projeto que se caracteriza em uma associação 1 x N entre objetos. Em que quando um dos objetos muda de estado ou característica, todos os objetos relacionados sejam notificados.
Um objeto do lado 1 da relação deve permitir acesso a seus elementos sem que sua estrutura interna seja exposta.
Como podemos implementar de uma forma abstrata e sem acoplamento a relação, sendo que qualquer alteração no objeto todos os seus relacionados sejam notificados  e que eles não se conhecem em tempo de compilação?

Devemos ter em mente os seguintes requisitos:

  • Os objetos relacionados devem conhecer o objeto de interesse, mas não devem estar
  • O objeto de interesse ( lado 1 da relação), deve notificar os objetos relacionados e interessados sobre ocorrência modificações.

Deve ser implementado de uma forma que os objetos não se conheçam em tempo de compilação, possibilitando acoplamento em tempo de execução e desfazê-lo a qualquer momento.

Aplicabilidade do padrão é quando necessitamos encapsular dois objetos por uma interface em que eles não se conheçam, sendo assim eles ficam completamente desacoplados, possibilitando o reuso separadamente de cada objeto.

Bem, vamos ao que interessa.

Imagine a seguinte situação de envio de email.

  • O remetente deseja saber quando o email chegou a caixa postal do destinatário e também saber quando o email foi lido.
  • O destinatário deseja saber quando chegou um novo email na caixa postal.

Como podemos implementar isso sem que destinatário e remetente estejam acoplados?

Primeiramente criaremos uma classe EmailEvent que representará os eventos ocorridos sobre um email.


package br.com.ronaldorigoni.observer;

/**
* Classe EmailEvent, representa todo e qualquer evento ocorrido com um email.
* Classe herda de java.util.EventObject para podermos manipular o método
* getSource(), que nos devolve o email que ocorreu o evento.
*
* @author ronaldorigoni.com.br
*/
public class EmailEvent extends java.util.EventObject {

public EmailEvent(Object source) {
super(source);
}

}

Em seguida deveremos criar uma interface EmailListener que será implementada pelo Remetente e Destinatário, que será através dela que eles serão notificados.


package br.com.ronaldorigoni.observer;

/**
* Interface de Listeners para email.
* @author ronaldorigoni.com.br
*
*/
public interface EmailListener {
public void emailRecebido(EmailEvent emailEvent);
public void emailLido(EmailEvent emailEvent);
}

Agora criaremos a classe Email, conforme abaixo.


package br.com.ronaldorigoni.observer;

import java.util.ArrayList;
import java.util.Collection;

/**
* Representa um email.
*
* @author ronaldorigoni.com.br
*
*/
public class Email {
/**
* Lista de objetos interessados nos eventos ocorridos com um email.
*/
private Collection<EmailListener> emailListeners =
new ArrayList<EmailListener>();

/**
* Envia um email.
*
* @param email
*/
public void enviar() {
disparaEmailRecebido();
disparaEmailLido();
}

/**
* Dispara um evento quando este email for recebido.
*/
public void emailRecebido() {
this.disparaEmailRecebido();
}

/**
* Dispara um evento quando este email for lido.
*/
public void emailLido() {
this.disparaEmailLido();
}

/**
* Método sincronizado para impedir que seja adicionado o mesmo listener
* mais de uma vez.
*
* @param emailListener
*            - o Listener
*/
public synchronized void addEmailListener(EmailListener emailListener) {
this.emailListeners.add(emailListener);
}

/**
* Remove um listener.
*
* @param emailListener
*            O listener.
*/
public synchronized void removeEmailListener(EmailListener emailListener) {
this.emailListeners.remove(emailListener);
}

/**
* Notifica todos os listeners que um email foi lido.
*/
private void disparaEmailLido() {
// criando um evento do próprio email
EmailEvent event = new EmailEvent(this);
// iterando sobre todos os listeners
// para notificar evento de email lido.
for (EmailListener listener : emailListeners) {
listener.emailLido(event);
}
}

/**
* Notifica todos os listeners que um email foi recebido.
*/
private void disparaEmailRecebido() {
// criando um evento do próprio email
EmailEvent event = new EmailEvent(this);
// iterando sobre todos os listeners
// para notificar evento de email recebido.
for (EmailListener listener : emailListeners) {
listener.emailRecebido(event);
}
}

}

Em seguida criaremos as classes Remetente e Destinatario, que deverão implementar a interface EmailListeners pois se tratam de interessados nos eventos ocorridos sobre os emails.


package br.com.ronaldorigoni.observer;

/**
* Representa um destinatário para um email.
* @author ronaldorigoni.com.br
*
*/
public class Destinatario implements EmailListener {

@Override
public void emailLido(EmailEvent emailEvent) {
Email email = (Email) emailEvent.getSource();
System.out.println("DESTINATÁRIO: Você acabou de ler o email:" + email + ".");
}

@Override
public void emailRecebido(EmailEvent emailEvent) {
Email email = (Email) emailEvent.getSource();
System.out.println("DESTINATÁRIO: Você acabou de receber um novo email:"+email);
}

}

E a classe Remetente.


package br.com.ronaldorigoni.observer;

/**
* Representa um
* @author ronaldorigoni.com.br
*
*/
public class Remetente implements EmailListener {

@Override
public void emailLido(EmailEvent emailEvent) {
Email email = (Email) emailEvent.getSource();
System.out.println("REMETENTE: O email " + email + " acaba de ser lido.");
}

@Override
public void emailRecebido(EmailEvent emailEvent) {
Email email = (Email) emailEvent.getSource();
System.out.println("REMETENTE: O email " + email
+ " acaba de ser recebido pelo destinatário.");
}

}

Agora criaremos a classe CaixaPostal que é onde a nossa brincadeira acontece.


package br.com.ronaldorigoni.observer;

/**
* Representa uma caixa postal para teste sobre emails.
* @author ronaldorigoni.com.br
*
*/
public class CaixaPostal {
public static void main(String[] args) {
// criando dois emails
Email email1 = new Email();
Email email2 = new Email();
// criando destinatário e remetente
Destinatario destinatario = new Destinatario();
Remetente remetente = new Remetente();
// Adicionaodo os listeners aos emails
email1.addEmailListener(destinatario);
email1.addEmailListener(remetente);

email2.addEmailListener(destinatario);
email2.addEmailListener(remetente);
/**
* Aqui é onde tudo acontece, brinque com os codigos abaixo,
* comente e descomente as linhas.
*/
email1.enviar();

email2.emailRecebido();
//email2.emailLido();
}
}

Execute o arquivo CaixaPostal.java e veja que quando um email é enviado tanto o remetente quando o destinatário são notificados nenhum conhece a implementação do outro.

Segue aqui o aqui para download do projeto

Bom por hoje fico por aqui, qualquer duvidas ou sugestões segue meu email ronaldo@ronaldorigoni.com.br

Até um próximo post.

Get Adobe Flash playerPlugin by wpburn.com wordpress themes

© 2007 Ronaldo Rigoni | iKon Wordpress Theme by Windows Vista Administration | Powered by Wordpress