Recentemente, li o artigo de Mark Seemann sobre o antipadrão do Service Locator.
O autor aponta duas razões principais pelas quais o ServiceLocator é um antipadrão:
Problema de uso da API (com o qual estou perfeitamente bem)
Quando a classe emprega um localizador de serviço, é muito difícil ver suas dependências, pois, na maioria dos casos, a classe possui apenas um construtor PARAMETERLESS. Ao contrário do ServiceLocator, a abordagem de DI expõe explicitamente dependências por meio de parâmetros do construtor, para que as dependências sejam facilmente vistas no IntelliSense.Problema de manutenção (o que me intriga)
Considere o seguinte exemplo
Temos uma classe 'MyType' que emprega uma abordagem de localizador de serviço:
public class MyType
{
public void MyMethod()
{
var dep1 = Locator.Resolve<IDep1>();
dep1.DoSomething();
}
}
Agora queremos adicionar outra dependência à classe 'MyType'
public class MyType
{
public void MyMethod()
{
var dep1 = Locator.Resolve<IDep1>();
dep1.DoSomething();
// new dependency
var dep2 = Locator.Resolve<IDep2>();
dep2.DoSomething();
}
}
E é aqui que começa meu mal-entendido. O autor diz:
Torna-se muito mais difícil dizer se você está introduzindo uma mudança de ruptura ou não. Você precisa entender todo o aplicativo em que o Localizador de Serviço está sendo usado e o compilador não irá ajudá-lo.
Mas espere um segundo, se estivéssemos usando a abordagem DI, introduziríamos uma dependência com outro parâmetro no construtor (no caso de injeção do construtor). E o problema ainda estará lá. Se podemos esquecer de configurar o ServiceLocator, podemos esquecer de adicionar um novo mapeamento em nosso contêiner de IoC e a abordagem DI teria o mesmo problema de tempo de execução.
Além disso, o autor mencionou dificuldades de teste de unidade. Mas, não teremos problemas com a abordagem de DI? Não precisamos atualizar todos os testes que estavam instanciando essa classe? Vamos atualizá-los para passar uma nova dependência zombada apenas para tornar nosso teste compilável. E não vejo nenhum benefício dessa atualização e do tempo gasto.
Não estou tentando defender a abordagem do Localizador de serviços. Mas esse mal-entendido me faz pensar que estou perdendo algo muito importante. Alguém poderia dissipar minhas dúvidas?
ATUALIZAÇÃO (RESUMO):
A resposta para minha pergunta "O Service Locator é um antipadrão" depende realmente das circunstâncias. E eu definitivamente não sugeriria riscá-lo da sua lista de ferramentas. Pode se tornar muito útil quando você começa a lidar com código legado. Se você tiver a sorte de estar no início do seu projeto, a abordagem de DI pode ser uma escolha melhor, pois possui algumas vantagens sobre o Service Locator.
E aqui estão as principais diferenças que me convenceram a não usar o Service Locator para meus novos projetos:
- O mais óbvio e importante: o Localizador de Serviço oculta as dependências de classe
- Se você estiver utilizando algum contêiner de IoC, provavelmente verificará todo o construtor na inicialização para validar todas as dependências e fornecer feedback imediato sobre os mapeamentos ausentes (ou configuração incorreta); isso não é possível se você estiver usando seu contêiner de IoC como localizador de serviço
Para detalhes, leia excelentes respostas que são dadas abaixo.