quarta-feira, dezembro 20, 2006

Observadores em Java

É recorrente, para os programadores, terem que conceber programas em que algumas partes estão dependentes do estado ou dos dados que algum componente detem. Por exemplo, ter um programa de chat no qual, cada mensagem recebida pela rede tem que se apresentada na interface ao mesmo tempo que é guardada num histórico da aplicação.

Uma forma elegante de resolver este problema é de recorrer a uma "software pattern" denominado Observer. Neste pattern, muitas vezes também chamado publish/subscribe, os objectos observadores registam-se junto dos que pretendem observar à espera de um evento.

Em Java, a API suporta explicitamente este pattern disponibilizando as classes Observer e Observable e vou aqui dar um exemplo muito ingénuo de como utilizá-las.
Aproveitando a ideia do client de chat, vou aqui apresentar o código esqueleto para a aplicação deste pattern.
Para começar, temos que ter o objecto que vai ser alvo de observação, em Java este objecto tem que estender a classe java.util.Observable:

import java.util.Observable;

public class
MsgObservable extends Observable implements Runnable
{
private String msg;

public void
run()
{
while(true)
{
msg = waitForMsg();

// marca o objecto como alterado
setChanged();

// avisa os observadores da mudança
notifyObservers( msg );
}
}

private String
waitForMsg() throws Exception
{
// recebe as mensagens
}
}
Neste excerto de código, os pontos importantes são as funções setChanged() e notifyObservers(...) que avisam os observadores de um evento.

Visto que se faz sentido ter algo para observar se estiver alguém a observar, temos que criar um observador, por exemplo, a classe para fazer o logging:

import java.util.Observable;
import java.util.Observer;

public class
LogWriter implements Observer
{
public void
update(Observable obj, Object arg)
{
logMessage(arg);
}

private void
LogMessage(Object arg) {
// TODO
}
}
A parte importante de este excerto de código é a função update( Observable obj, Object arg ) que é a função que é executada quando o observador é notificado de um evento.

Agora só falta colar isto tudo de forma a que os observadores possam ser notificados dos eventos do objecto observado. Mais uma vez simplificando, em muito, o código para o essencial:

import java.util.Observer;

ChatClient implements Observer
{

ChatClient
{
// criar um MsgObservable
// para receber as mensagens da rede
MsgObservable msgObj = new MsgObservable();

// criar os observadores
LogWriter log = new LogWriter();
ChatUI ui = new ChatUI();

// associar os observadores ao objecto observado
msgObj.addObserver(log);
msgObj.addObserver(ui);

// iniciar o MsgObservable
msgObj.run();
}
}
Aqui temos uma forma bastante elegante de separar bastantes componentes de código e de limitar a sua zona de interacção no código facilitando a sua reutilização.

É também importante referir que o Observable está muito dependente do desempenho das funções update( Observable obj, Object arg ), visto que só retorna o seu fluxo normal de execução depois de todos os observadores tenham sido notificados.

Sem comentários: