Submarino.com.br

Registry

Objetivo

Prover uma forma de objetos contactarem outros objectos de forma desacoplada de ambos.

Propósito

Uma aplicação funciona pela colaboração entre objectos, portanto a primeira coisa necessária é que o objeto executor possa encontrar o seu colaborador. Esta colaboração cria uma dependencia entre o objeto executor e aquele que ele necessita para executar a sua tarefa.

A injeção de dependencia (passar o objeto colaborador como argumento do contrutor do objecto executor) é uma opção viável mas que acopla para sempre as instancias de ambos objetos.

Por outro lado o fato de ser necessária a colaboração de um objeto de um certo tipo, isso não significa que esse colaborador não possa ser modificado ou substituido por outro. Além disso, dois objetos instancias da mesma classe podem ser configuradas de forma diferente de forma a realizar a mesma operação de forma diferente ou sobre um contexto diferente. O padrão Registry oferece portanto um nivel maior de configuração de qual instancia irá desempenhar o trabalho.

Se desacoplamos os objetos precisamos de um ponto de encontro em é garantido que o colaborador esteja presente e onde é garantido que o objeto trabalhador o irá procurar. Consequentemente este será um ponto de acesso global.

Implementação

Existem várias formas de implementar o padrão Registry.

Usando uma classe

A forma mais simples é criar uma classe não extensivel e não instanciável onde podemos registrar (dai o nome) o objeto colaborador e onde o objeto executor irá procurá-la.

public final class Registry { // não extensivel

	private Registry(){} // não instanciável e não extensivel
	
	private static Dicionario dicionario;
	
	public static Dicionario getDicionario(){
		return dicionario;
	}
	
	public static void setDicionario(Dicionario umDicionario){
		dicionario = umDicionario;
	}

}				
				
Código 1: Implementação básica

Qualquer objecto que precise utilizar um dicionário agora sabe como obtê-lo sem se preocupar com a classe verdadeira desse objeto. Em particular, o registro pode ser feito utilizando uma interface o que aumenta ainda mais as opções.

Imaginando que Dicionario é uma interface, poderiamos ter classes PtDicionario, EnDicionario e EsDicionario e escolher no inicio da aplicação qual usar.

public class Main{

	public static final void main(String[] args){
		
		// utiliza o dicionario ingles.
		Registry.setDicionario(new EnDicionario());
	
	}
}
				
Código 2: Registrando o objecto

Se a aplicação faz traduções então é necessário utilizar mais do que um dicionario. Poderiamos criar um método de registro para cada um, mas isso seria penoso. É mais fácil utilizar uma chave de registro. A chave de registro é um outro objeto (normalmente um String, mas não precisa ser sempre) que permite recuperar o objecto depois.

No nosso caso utilizaremos um String representando a lingua.Repare como a estrutura interna foi alterada para usar um Map.

public final class Registry { // não extensivel

	private Registry(){} // não instanciável e não extensivel
	
	private static final Map<String,Dicionario> dicionarios = new HashMap<String,Dicionario>();
	
	public static Dicionario getDicionario(String language){
		return dicionarios.get(language);
	}
	
	public static void addDicionario(String language,Dicionario dicionario){
		dicionario.put( language,dicionario);
	}

}				
				
Código 3: Implementação básica

Registros e Repositorios

Um uso comum para o padrão Registry é servir como ponto de encontro de objectos que implementam o padrão Repository. Um repository está associado a uma entidade que é representada por uma classe.

Poderiamos registrar um Repositorio com um método de registro para cada entidade, mas seria mais interessante utilizar a entidade ( a classe da entidade) como chave do registro.

O exemplo a seguir assume que existe um tipo Repositorio<E> genéricamente tipado.

public final class Repositories { // não extensivel

	private Registry(){} // não instanciável e não extensivel
	
	private static final Map<String,Repositorio> repositorios = new HashMap<String,Repositorio>();
	
	public static <E> Repositorio<E> forEntity(Class<E> entityType){
		return repositorios.get(entityType.getName());
	}
	
	public static <E> void addRepository(lass<E> entityType,Repositorio<E> repository){
		repositorios.put( entityType.getName(),repository);
	}

}				
					
Código 4: Implementação com repositorios e genéricos

Exemplos na API padrão

O padrão Registry está presente na API padrão na classe System de onde podem se obter e registrar vários objetos uteis. Particularmente System.getProperty() e System.out e System.in.

Além disso o padrão Registry está presente em DriveManager.getConnection()

Generalização

Como vemos um objecto que implemente o padrão registry pode ser muito util. Existe acesso global aos objetos registrados e podemos utilizar chaves de registro para diferenciar entre objetos diferentes mas da mesma classe.

Este padrão é tão util que é seguido pela tecnologia Java Name and Directory Interface (JNDI). Esta tencologia da plataforma java utiliza caminhos como chaves de registro permitindo não apenas registrar os objetos, mas também registrá-los segundo uma hierarquia. Além, disso permite que o registro e a recuperação dos objetos seja feito remotamente (em outra máquina) abrindo assim a opção de um registro centrar para aplicações distribuidas.

Discussão

O padrão Registry é estremamente importante em Orientação a Objetos porque permite que objetos colaborem sem que estejam fisicamente acoplados. A implementação pode ter muitas variantes, mas sob uma forma ou outra é utilizado em muitas tecnologias java como JNDI para encontrar objetos genéricos e Jini e OSGi para encontrar serviços.

Padrões relacionados

O padrão Registry é utilizando para sempre que uma abtração precisa ser relacionada à implementação de forma desacoplada. Portanto é natural associa-lo ao padrão Service. O Registry é também encontrado junto ao padrão Repository e Bridge

Porque o registro é global o uso de Registry é uma boa alternativa ao uso de Singleton já que provê a opção de acesso remoto sem forçar a opção de unicidade da instanciação.

O padrão Registry pode também se transforma no padrão Service Locator em que, básicamente, o registro não é feito através do mesmo objeto e é encontrado em tempo de execução através de uma chave (ou endereço).