Judulnya bertuliskan "Circular Dependency", tetapi itu bukan kata-kata yang tepat, karena bagi saya desainnya tampak solid.
Namun, pertimbangkan skenario berikut ini, di mana bagian biru diberikan dari mitra eksternal, dan oranye adalah implementasi saya sendiri. Juga asumsikan ada lebih dari satu ConcreteMain
, tetapi saya ingin menggunakan yang spesifik. (Pada kenyataannya, setiap kelas memiliki lebih banyak dependensi, tetapi saya mencoba menyederhanakannya di sini)
Saya ingin instanciate semua ini dengan Depency Injection (Unity), tetapi saya jelas mendapatkan StackOverflowException
kode berikut, karena Runner mencoba untuk membuat Instantiate, dan ConcreteMain membutuhkan Runner.
IUnityContainer ioc = new UnityContainer();
ioc.RegisterType<IMain, ConcreteMain>()
.RegisterType<IMainCallback, Runner>();
var runner = ioc.Resolve<Runner>();
Bagaimana saya bisa menghindari ini? Apakah ada cara untuk menyusun ini sehingga saya bisa menggunakannya dengan DI? Skenario yang saya lakukan sekarang adalah mengatur semuanya secara manual, tapi itu menempatkan ketergantungan yang sulit di ConcreteMain
dalam kelas yang instantiate itu. Inilah yang saya coba hindari (dengan registrasi Unity di konfigurasi).
Semua kode sumber di bawah ini (contoh sangat sederhana!);
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));
}
}