El título dice "Dependencia circular", pero no es la redacción correcta, porque el diseño me parece sólido.
Sin embargo, considere el siguiente escenario, donde las partes azules provienen de un socio externo y el naranja es mi propia implementación. También suponga que hay más de uno ConcreteMain
, pero quiero usar uno específico. (En realidad, cada clase tiene algunas dependencias más, pero traté de simplificarlo aquí)
Me gustaría instanciar todo esto con Depency Injection (Unity), pero obviamente obtengo un StackOverflowException
código siguiente, porque Runner intenta crear una instancia de ConcreteMain, y ConcreteMain necesita un Runner.
IUnityContainer ioc = new UnityContainer();
ioc.RegisterType<IMain, ConcreteMain>()
.RegisterType<IMainCallback, Runner>();
var runner = ioc.Resolve<Runner>();
¿Cómo puedo evitar esto? ¿Hay alguna forma de estructurar esto para que pueda usarlo con DI? El escenario que estoy haciendo ahora es configurar todo manualmente, pero eso pone una gran dependencia ConcreteMain
en la clase que lo instancia. Esto es lo que estoy tratando de evitar (con los registros de Unity en la configuración).
Todo el código fuente a continuación (¡ejemplo muy simplificado!);
public class Program
{
public static void Main(string[] args)
{
IUnityContainer ioc = new UnityContainer();
ioc.RegisterType<IMain, ConcreteMain>()
.RegisterType<IMainCallback, Runner>();
var runner = ioc.Resolve<Runner>();
Console.WriteLine("invoking runner...");
runner.DoSomethingAwesome();
Console.ReadLine();
}
}
public class Runner : IMainCallback
{
private readonly IMain mainServer;
public Runner(IMain mainServer)
{
this.mainServer = mainServer;
}
public void DoSomethingAwesome()
{
Console.WriteLine("trying to do something awesome");
mainServer.DoSomething();
}
public void SomethingIsDone(object something)
{
Console.WriteLine("hey look, something is finally done.");
}
}
public interface IMain
{
void DoSomething();
}
public interface IMainCallback
{
void SomethingIsDone(object something);
}
public abstract class AbstractMain : IMain
{
protected readonly IMainCallback callback;
protected AbstractMain(IMainCallback callback)
{
this.callback = callback;
}
public abstract void DoSomething();
}
public class ConcreteMain : AbstractMain
{
public ConcreteMain(IMainCallback callback) : base(callback){}
public override void DoSomething()
{
Console.WriteLine("starting to do something...");
var task = Task.Factory.StartNew(() =>{ Thread.Sleep(5000);/*very long running task*/ });
task.ContinueWith(t => callback.SomethingIsDone(true));
}
}