Estou tentando fazer com que o Unity gerencie a criação dos meus objetos e quero ter alguns parâmetros de inicialização que não são conhecidos até o tempo de execução:
No momento, a única maneira de pensar em como fazer isso é ter um método Init na interface.
interface IMyIntf {
void Initialize(string runTimeParam);
string RunTimeParam { get; }
}
Então, para usá-lo (no Unity), eu faria o seguinte:
var IMyIntf = unityContainer.Resolve<IMyIntf>();
IMyIntf.Initialize("somevalue");
Nesse cenário, o runTimeParam
parâmetro é determinado no tempo de execução com base na entrada do usuário. O caso trivial aqui simplesmente retorna o valor de, runTimeParam
mas, na realidade, o parâmetro será algo como o nome do arquivo e o método de inicialização fará algo com o arquivo.
Isso cria vários problemas, a saber, que o Initialize
método está disponível na interface e pode ser chamado várias vezes. Definir um sinalizador na implementação e lançar exceção em chamadas repetidas para Initialize
parece muito desajeitado.
No momento em que resolvo minha interface, não quero saber nada sobre a implementação do IMyIntf
. O que eu quero, no entanto, é o conhecimento de que essa interface precisa de determinados parâmetros de inicialização únicos. Existe uma maneira de anotar (atributos?) A interface com essas informações e transmiti-las à estrutura quando o objeto é criado?
Edit: Descreveu a interface um pouco mais.
fonte
runTimeParam
é uma dependência que é determinada no tempo de execução com base em uma entrada do usuário. A alternativa para isso deve ser dividida em duas interfaces - uma para inicialização e outra para armazenar valores?Respostas:
Qualquer local em que você precise de um valor em tempo de execução para construir uma dependência específica, o Abstract Factory é a solução.
A inicialização de métodos nas interfaces cheira a uma abstração com vazamento .
No seu caso, eu diria que você deve modelar a
IMyIntf
interface sobre como precisa usá-la - e não sobre como pretende criar implementações. Esse é um detalhe de implementação.Assim, a interface deve ser simplesmente:
Agora defina a Fábrica abstrata:
Agora você pode criar uma implementação concreta
IMyIntfFactory
que cria instâncias concretasIMyIntf
como esta:Observe como isso nos permite proteger os invariantes da classe usando a
readonly
palavra - chave. Nenhum método de inicialização fedorento é necessário.Uma
IMyIntfFactory
implementação pode ser tão simples quanto isto:Em todos os seus clientes em que você precisa de uma
IMyIntf
instância, basta assumir uma dependênciaIMyIntfFactory
solicitando-a através da Injeção de Construtor .Qualquer contêiner de DI que se preze poderá ligar automaticamente uma
IMyIntfFactory
instância para você, se você a registrar corretamente.fonte
MyIntf
implementação da fábrica exigir mais do querunTimeParam
(leia-se: outros serviços que alguém gostaria que fossem resolvidos por uma IoC), você ainda terá que resolver essas dependências na sua fábrica. Gosto da resposta do @PhilSandler de passar essas dependências para o construtor da fábrica para resolver isso - essa é a sua opinião também?Geralmente, quando você encontra essa situação, precisa revisitar seu design e determinar se está misturando seus objetos de estado / dados com seus serviços puros. Na maioria dos casos (não todos), você deseja manter esses dois tipos de objetos separados.
Se você precisar de um parâmetro específico do contexto passado no construtor, uma opção é criar uma fábrica que resolva suas dependências de serviço por meio do construtor e use o parâmetro de tempo de execução como um parâmetro do método Create () (ou Generate ( ), Build () ou qualquer que seja o nome dos métodos de fábrica).
Ter setters ou um método Initialize () geralmente é considerado um design ruim, pois você precisa "lembrar" de chamá-los e garantir que eles não abram muito o estado da sua implementação (ou seja, o que impede alguém de chamada inicializando ou o levantador?).
fonte
Também me deparei com essa situação algumas vezes em ambientes em que estou criando dinamicamente objetos ViewModel baseados em objetos Model (descritos muito bem por essa outra postagem do Stackoverflow ).
Gostei de como a extensão Ninject, que permite criar fábricas dinamicamente com base em interfaces:
Bind<IMyFactory>().ToFactory();
Não encontrei nenhuma funcionalidade semelhante diretamente no Unity ; então escrevi minha própria extensão no IUnityContainer, que permite registrar fábricas que criarão novos objetos com base em dados de objetos existentes, mapeando essencialmente de uma hierarquia de tipos para uma hierarquia de tipos diferente: UnityMappingFactory @ GitHub
Com o objetivo de simplicidade e legibilidade, terminei com uma extensão que permite especificar diretamente os mapeamentos sem declarar classes ou interfaces individuais de fábrica (economia de tempo real). Você acabou de adicionar os mapeamentos exatamente onde registra as classes durante o processo normal de inicialização ...
Em seguida, basta declarar a interface da fábrica de mapeamento no construtor para CI e usar seu método Create () ...
Como um bônus adicional, quaisquer dependências adicionais no construtor das classes mapeadas também serão resolvidas durante a criação do objeto.
Obviamente, isso não resolverá todos os problemas, mas me serviu muito bem até agora, então pensei que deveria compartilhá-lo. Há mais documentação no site do projeto no GitHub.
fonte
Não consigo responder com terminologia específica do Unity, mas parece que você está apenas aprendendo sobre injeção de dependência. Nesse caso, sugiro que você leia o guia do usuário breve, claro e informativo do Ninject .
Isso o guiará pelas várias opções disponíveis ao usar o DI e como explicar os problemas específicos que você enfrentará ao longo do caminho. No seu caso, você provavelmente desejaria usar o contêiner de DI para instanciar seus objetos e fazer com que esse objeto obtenha uma referência a cada uma de suas dependências por meio do construtor.
A explicação passo a passo também detalha como anotar métodos, propriedades e até parâmetros usando atributos para distingui-los no tempo de execução.
Mesmo se você não usar o Ninject, o passo a passo fornecerá os conceitos e a terminologia da funcionalidade adequada ao seu objetivo, e você poderá mapear esse conhecimento para o Unity ou outras estruturas de DI (ou convencê-lo a experimentar o Ninject) .
fonte
Eu acho que resolvi e parece bastante saudável, então deve estar meio certo :))
Dividi-me
IMyIntf
nas interfaces "getter" e "setter". Assim:Então a implementação:
IMyIntfSetter.Initialize()
ainda pode ser chamado várias vezes, mas usando bits do paradigma Service Locator , podemos agrupá-lo bastante bem, de modo queIMyIntfSetter
é quase uma interface interna distintaIMyIntf
.fonte