Pular para o conteúdo

Registry

“A well-known object that other objects can use to find common objects and services.”

Em vez de passar uma dependência de objeto em objeto, o cliente pergunta ao Registry: “me dê o serviço X”. O Registry guarda referências globalmente acessíveis.

Como um objeto encontra um serviço compartilhado sem recebê-lo explicitamente? O Registry oferece um endereço global. Historicamente resolvia a ausência de um contêiner de injeção: bastava um Registry.get(Servico.class).

“Vantagem” Custo real
Acesso global fácil Dependências ocultas: a assinatura não revela o que a classe usa
Sem “passar” dependências Acoplamento a um singleton global
Simples de chamar Testabilidade ruim: difícil substituir por dublês
Estado global compartilhado (problemas de concorrência)
Fere o DIP - Dependency Inversion Principle e o SRP - Single Responsibility Principle
  • Pontos de entrada onde não há contêiner de DI (bootstrap, integração com frameworks legados).
  • Como implementação interna de um contêiner de DI (o próprio contêiner é, no fundo, um registry sofisticado) — mas aí o acesso não é global e disperso.

A injeção de dependência inverte o fluxo: em vez do objeto buscar suas dependências no Registry (Service Locator), ele as recebe de fora (via construtor). As dependências ficam explícitas e substituíveis.

graph TD
    subgraph "Registry / Service Locator (cheiro)"
        C1[Cliente] -->|get Servico| R[(Registry global)]
        R --> S1[Servico]
    end
    subgraph "Injeção de Dependência (preferível)"
        Cont[Contêiner Spring] -->|injeta no construtor| C2[Cliente]
        C2 --> S2[Servico]
    end

Registry clássico (anti-exemplo a ser evitado):

public final class ServiceRegistry {
private static final Map<Class<?>, Object> SERVICES = new ConcurrentHashMap<>();
private ServiceRegistry() {}
public static <T> void register(Class<T> tipo, T instancia) {
SERVICES.put(tipo, instancia);
}
@SuppressWarnings("unchecked")
public static <T> T get(Class<T> tipo) {
T s = (T) SERVICES.get(tipo);
if (s == null) {
throw new IllegalStateException("Serviço não registrado: " + tipo);
}
return s;
}
}
public class PedidoService {
public void confirmar(Long id) {
var repo = ServiceRegistry.get(PedidoRepository.class);
repo.findById(id).orElseThrow().confirmar();
}
}

Problemas: PedidoService esconde que depende de PedidoRepository; testá-lo exige manipular o estado global estático; qualquer classe pode alterar o registry.

Forma moderna equivalente, com injeção de dependência (preferir):

@Service
public class PedidoService {
private final PedidoRepository repo;
public PedidoService(PedidoRepository repo) {
this.repo = repo;
}
@Transactional
public void confirmar(Long id) {
Pedido pedido = repo.findById(id).orElseThrow();
pedido.confirmar();
repo.save(pedido);
}
}

A dependência é explícita no construtor, o Spring a resolve, e o teste injeta um mock diretamente — sem estado global.

  • Service Locator disfarçado: usar ApplicationContext.getBean(...) espalhado pelo código em vez de injeção por construtor — recria o Registry dentro do Spring.
  • Dependências ocultas: classes cuja assinatura não revela suas dependências.
  • Estado global mutável: register/set chamáveis em runtime, criando acoplamento temporal e problemas de concorrência.
  • Dificuldade de teste: precisar montar/desmontar estado estático entre testes.
  • Justificar Registry “por simplicidade” onde já existe um contêiner de DI disponível.

1. O que é o Registry e a que padrão de objeto ele equivale?

Resposta

Um ponto global e conhecido para localizar objetos/serviços. Equivale, na essência, a um Singleton global (uma forma de Service Locator).

2. Por que o Registry é considerado um cheiro?

Resposta

Porque torna as dependências implícitas (não aparecem na assinatura), introduz estado global, dificulta testes e viola o DIP — o objeto busca suas dependências em vez de recebê-las.

3. Como a injeção de dependência resolve isso?

Resposta

Invertendo o fluxo: as dependências são injetadas (via construtor) e ficam explícitas e substituíveis, sem acoplamento a um singleton global. O contêiner (ex.: Spring) resolve o grafo.

4. Usar ApplicationContext.getBean(...) espalhado é aceitável?

Resposta

Não como prática geral: é um Service Locator disfarçado dentro do Spring, recriando os problemas do Registry. Prefira injeção por construtor; reserve getBean para casos de bootstrap/integração muito específicos.