Le titre dit "Circular Dependency", mais ce n'est pas le bon libellé, car pour moi le design semble solide.
Cependant, considérez le scénario suivant, où les parties bleues sont fournies par un partenaire externe, et l'orange est ma propre implémentation. Supposons également qu'il y en ait plus d'un ConcreteMain
, mais je veux en utiliser un spécifique. (En réalité, chaque classe a plus de dépendances, mais j'ai essayé de la simplifier ici)
Je voudrais instancier tout cela avec Depency Injection (Unity), mais je reçois évidemment un StackOverflowException
sur le code suivant, car Runner essaie d'instancier ConcreteMain, et ConcreteMain a besoin d'un Runner.
IUnityContainer ioc = new UnityContainer();
ioc.RegisterType<IMain, ConcreteMain>()
.RegisterType<IMainCallback, Runner>();
var runner = ioc.Resolve<Runner>();
Comment puis-je éviter cela? Existe-t-il un moyen de structurer cela afin que je puisse l'utiliser avec DI? Le scénario que je fais maintenant est de tout configurer manuellement, mais cela met une forte dépendance surConcreteMain
dans la classe qui l'instancie. C'est ce que j'essaie d'éviter (avec les enregistrements Unity dans la configuration).
Tout le code source ci-dessous (exemple très simplifié!);
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));
}
}