Pergunta

Algo que está me incomodando desde que li uma resposta em outra pergunta do StackOverflow (a precisa me ilude agora), onde um usuário declarou algo como "Se você está ligando para o localizador de serviços, está fazendo errado."

Era alguém com alta reputação (nas centenas de milhares, eu acho), então eu acho que essa pessoa poderia saber do que está falando. Eu uso o DI para meus projetos desde que comecei a aprender sobre isso e quão bem ele se relaciona com os testes de unidade e o que não. É algo com o qual estou bastante confortável agora e eu acho Eu sei o que estou fazendo.

No entanto, existem muitos lugares em que eu tenho usado o localizador de serviços para resolver dependências no meu projeto. Uma vez que o excelente exemplo vem das minhas implementações do ModelBinder.

Exemplo de um fichário de modelo típico.

public class FileModelBinder : IModelBinder {
    public object BindModel(ControllerContext controllerContext,
                            ModelBindingContext bindingContext) {
        ValueProviderResult value = bindingContext.ValueProvider.GetValue("id");

        IDataContext db = Services.Current.GetService<IDataContext>();
        return db.Files.SingleOrDefault(i => i.Id == id.AttemptedValue);
    }
}

Não é uma implementação real - apenas um exemplo rápido

Como a implementação do Modelbinder requer uma nova instância quando um fichário é primeiro Solicitado, é impossível usar a injeção de dependência no construtor para esta implementação específica.

É assim em muitas das minhas aulas. Outro exemplo é o de um processo de expiração do cache que executa um método sempre que um objeto de cache expira no meu site. Eu corro um monte de chamadas de banco de dados e o que não. Lá também estou usando um localizador de serviço para obter a dependência necessária.

Outra edição que tive recentemente (sobre a qual publiquei uma pergunta aqui) foi que todos os meus controladores exigiram uma instância de idatacontext para a qual eu usei DI - mas 1 O método de ação exigia uma instância diferente do idatacontext. Felizmente, o Ninject veio em socorro com uma dependência nomeada. No entanto, isso parecia um kludge e não uma solução real.

Eu pensei que, pelo menos, entendi o conceito de separação de preocupações razoavelmente bem, mas parece haver algo fundamentalmente errado com a maneira como entendo a injeção de dependência e o padrão do localizador de serviços - e não sei o que é isso.

A maneira como eu o entendo atualmente - e isso também pode estar errado - é que, pelo menos no MVC, o ControllerFactory procura um construtor para um controlador e chama o próprio localizador de serviço para obter as dependências necessárias e depois as passa. No entanto, , Consigo entender que nem todas as classes e o que não tem uma fábrica para criá -las. Então, parece -me que algum padrão do localizador de serviços é aceitável ... mas ...

  1. Quando não é aceitável?
  2. Que tipo de padrão devo estar atento quando devo repensar como estou usando o padrão do localizador de serviço?
  3. Minha implementação do Modelbinder está errada? Se sim, o que preciso aprender a consertar?
  4. Em outra pergunta, de acordo com este usuário Mark Seemann Recomendou uma fábrica abstrata - como isso se relaciona?

Eu acho que é isso - eu realmente não consigo pensar em nenhuma outra pergunta para ajudar meu entendimento, mas qualquer informação extra é muito apreciada.

Entendo que o DI pode não ser a resposta para tudo e eu posso estar exagerando na maneira como a implemento, no entanto, parece funcionar da maneira que espero com os testes de unidade e o que não.

Não estou procurando código para corrigir minha implementação de exemplo - estou procurando aprender, procurando uma explicação para corrigir meu entendimento defeituoso.

Eu gostaria que o Stackoverflow.com tivesse a capacidade de salvar perguntas de rascunho. Também espero que quem responda a essa pergunta obtenha a quantidade apropriada de reputação de responder a essa pergunta, pois acho que estou pedindo muito. Desde já, obrigado.

Foi útil?

Solução

Considere o seguinte:

public class MyClass
{
  IMyInterface _myInterface;
  IMyOtherInterface _myOtherInterface;

  public MyClass(IMyInterface myInterface, IMyOtherInterface myOtherInterface)
  {
    // Foo

    _myInterface = myInterface;
    _myOtherInterface = myOtherInterface;
  }
}

Com este design, sou capaz de expressar os requisitos de dependência para o meu tipo. O tipo em si não é responsável por saber como instanciar nenhuma das dependências, eles são dados a ele (injetados) por qualquer que seja o mecanismo de resolução [normalmente um contêiner do IOC]. Enquanto:

public class MyClass
{
  IMyInterface _myInterface;
  IMyOtherInterface _myOtherInterface;

  public MyClass()
  {
    // Bar

    _myInterface = ServiceLocator.Resolve<IMyInterface>();
    _myOtherInterface = ServiceLocator.Resolve<IMyOtherInterface>();
  }
}

Nossa classe agora depende da criação das instâncias específicas, mas por meio de delegação em um localizador de serviço. Nesse sentido, o local do serviço pode ser considerado um Anti-padrão Porque você não está expondo dependências, mas está permitindo problemas que podem ser capturados através da compilação para borbulhar no tempo de execução. (Uma boa leitura é aqui). Você esconde complexidades.

A escolha entre um ou outro realmente depende do que seu edifício no topo e dos serviços que ele fornece. Normalmente, se você estiver criando um aplicativo do zero, eu escolheria o DI o tempo todo. Melhora a manutenção, promove a modularidade e facilita muito os tipos de teste. Mas, tomando o ASP.NET MVC3 como exemplo, você pode implementar facilmente o SL como assado no design.

Você sempre pode optar por um design composto, onde pode usar o IOC/DI com SL, assim como usar o Localizador de serviços comuns. Suas peças componentes podem ser conectadas através de DI, mas expostas através do SL. Você pode até lançar a composição na mistura e usar algo como a estrutura de extensibilidade gerenciada (que suporta DI, mas também pode ser conectada a outros contêineres do COI ou localizadores de serviços). É uma grande opção de design a ser feita, geralmente minha recomendação seria para o IOC/DI sempre que possível.

Seu design específico que eu não diria que está errado. Nesse caso, seu código não é responsável por criar uma instância do próprio fichário do modelo, isso depende da estrutura, para que você não tenha controle sobre isso mas Seu uso do localizador de serviço provavelmente poderia ser facilmente alterado para acessar um contêiner do IOC. Mas a ação de chamar resolução no contêiner do COI ... você não consideraria esse local de serviço?

Com um padrão abstrato de fábrica, a fábrica é especializada na criação de tipos específicos. Você não registra tipos de resolução, registra essencialmente uma fábrica abstrata e que cria quaisquer tipos necessários. Com um localizador de serviço, ele foi projetado para Localize serviços e devolver essas instâncias. Semelhante do ponto de vista da convenção, mas muito diferente em comportamento.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top