Suponha que eu tenha o Service
que recebe dependências via construtor, mas também precise ser inicializado com dados personalizados (contexto) antes de poder ser usado:
public interface IService
{
void Initialize(Context context);
void DoSomething();
void DoOtherThing();
}
public class Service : IService
{
private readonly object dependency1;
private readonly object dependency2;
private readonly object dependency3;
public Service(
object dependency1,
object dependency2,
object dependency3)
{
this.dependency1 = dependency1 ?? throw new ArgumentNullException(nameof(dependency1));
this.dependency2 = dependency2 ?? throw new ArgumentNullException(nameof(dependency2));
this.dependency3 = dependency3 ?? throw new ArgumentNullException(nameof(dependency3));
}
public void Initialize(Context context)
{
// Initialize state based on context
// Heavy, long running operation
}
public void DoSomething()
{
// ...
}
public void DoOtherThing()
{
// ...
}
}
public class Context
{
public int Value1;
public string Value2;
public string Value3;
}
Agora - os dados de contexto não são conhecidos de antemão, portanto, não posso registrá-los como uma dependência e usar o DI para injetá-los no serviço
É assim que o exemplo de cliente se parece:
public class Client
{
private readonly IService service;
public Client(IService service)
{
this.service = service ?? throw new ArgumentNullException(nameof(service));
}
public void OnStartup()
{
service.Initialize(new Context
{
Value1 = 123,
Value2 = "my data",
Value3 = "abcd"
});
}
public void Execute()
{
service.DoSomething();
service.DoOtherThing();
}
}
Como você pode ver - existem acoplamentos temporais e odores de código de método de inicialização envolvidos, porque primeiro eu preciso ligar service.Initialize
para poder ligar service.DoSomething
e service.DoOtherThing
depois.
Quais são as outras abordagens nas quais posso eliminar esses problemas?
Esclarecimentos adicionais sobre o comportamento:
Cada instância do cliente precisa ter sua própria instância do serviço inicializada com dados de contexto específicos do cliente. Portanto, esses dados de contexto não são estáticos ou são conhecidos antecipadamente, portanto, não podem ser injetados por DI no construtor.
Service
tiver dependências diferentes deContext
, que não seriam fornecidas peloClient
, elas podem ser fornecidas via DIServiceFactory
para serem transmitidas aoService
quandocreateService
for chamado.ServiceBuilder partial = new ServiceBuilder().dependency1(dependency1_1).dependency2(dependency2_1).dependency3(dependency3_1);
e ficar com o seu serviço parcialmente configurado, e depois fazerService s = partial.context(context).build()
O
Initialize
método deve ser removido daIService
interface, pois esse é um detalhe da implementação. Em vez disso, defina outra classe que pegue a instância concreta do Service e chame o método initialize nela. Então essa nova classe implementa a interface IService:Isso mantém o código do cliente ignorante do procedimento de inicialização, exceto onde a
ContextDependentService
classe é inicializada. Você pelo menos limita as partes do seu aplicativo que precisam saber sobre esse procedimento de inicialização complicado.fonte
Parece-me que você tem duas opções aqui
por exemplo.
por exemplo.
Injetar uma fábrica é bom se você quiser evitar passar o contexto como parâmetro. Digamos que apenas essa implementação específica precise de um contexto e você não queira adicioná-lo à Interface
Mas você basicamente tem o mesmo problema, e se a fábrica ainda não tiver um contexto inicializado.
fonte
Você não deve depender sua interface para qualquer contexto de banco de dados e método de inicialização. Você pode fazer isso no construtor de classe concreto.
E, uma resposta para sua pergunta principal seria Injeção de propriedades .
Dessa forma, você pode chamar todas as dependências por Injeção de propriedades . Mas poderia ser um número enorme. Nesse caso, você pode usar a Injeção de construtor para eles, mas pode definir seu contexto por propriedade, verificando se é nulo.
fonte
Misko Hevery tem uma postagem de blog muito útil sobre o caso que você enfrentou. Vocês dois precisam ser renováveis e injetáveis para a sua
Service
turma, e esta postagem no blog pode ajudá-lo.fonte