У заголовку написано "Кругова залежність", але це не правильне формулювання, оскільки мені дизайн здається солідним.
Однак розглянемо наступний сценарій, коли сині частини подаються від зовнішнього партнера, а помаранчевий - це моя власна реалізація. Припустимо також, що існує більше одного ConcreteMain
, але я хочу використовувати конкретний. (Насправді кожен клас має ще кілька залежностей, але я намагався спростити його тут)
Я хотів би придумати все це за допомогою Depency Injection (Unity), але я, очевидно, отримую StackOverflowException
наступний код, тому що Runner намагається інстанціювати ConcreteMain, а ConcreteMain потребує Runner.
IUnityContainer ioc = new UnityContainer();
ioc.RegisterType<IMain, ConcreteMain>()
.RegisterType<IMainCallback, Runner>();
var runner = ioc.Resolve<Runner>();
Як я можу це зробити? Чи є спосіб це структурувати, щоб я міг використовувати це з DI? Сценарій, який я зараз роблю, - це налаштувати все вручну, але це ставить важку залежність від ConcreteMain
класу, який його створює . Цього я намагаюся уникати (з конфігурацією реєстрацій Unity).
Весь вихідний код нижче (дуже спрощений приклад!);
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));
}
}