วิธีจัดการกับ“ การพึ่งพาแบบวงกลม” ในการฉีดแบบพึ่งพา


15

ชื่อกล่าวว่า "การพึ่งพาหนังสือเวียน" แต่มันไม่ได้เป็นถ้อยคำที่ถูกต้องเพราะสำหรับฉันการออกแบบดูเหมือนจะแข็ง
อย่างไรก็ตามให้พิจารณาสถานการณ์ต่อไปนี้ซึ่งชิ้นส่วนสีน้ำเงินได้รับจากคู่ค้าภายนอกและสีส้มคือการนำไปปฏิบัติของฉันเอง นอกจากนี้สมมติว่ามีมากกว่าหนึ่งConcreteMainแต่ฉันต้องการใช้เฉพาะ (ในความเป็นจริงแต่ละชั้นมีการพึ่งพาอาศัยกันมากกว่านี้ แต่ฉันพยายามทำให้มันง่ายขึ้นที่นี่)

สถานการณ์

ฉันต้องการเริ่มต้นทั้งหมดนี้ด้วย Depency Injection (Unity) แต่เห็นได้ชัดว่าฉันได้รับStackOverflowExceptionรหัสต่อไปนี้เนื่องจาก Runner พยายามยกตัวอย่าง ConcreteMain และ ConcreteMain ต้องการนักวิ่ง

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));
    }
}

คำตอบ:


10

สิ่งที่คุณสามารถทำได้คือการสร้างโรงงาน MainFactory ที่ส่งคืนอินสแตนซ์ของ ConcreteMain เป็น IMain

จากนั้นคุณสามารถฉีดโรงงานนี้ลงในคอนสตรัคเตอร์ Runner ของคุณ สร้างหลักพร้อมด้วยโรงงานและผ่านตัวเองเป็นพารามิเตอร์

การพึ่งพาอื่น ๆ ใน Constructor ConcreteMain สามารถส่งผ่านไปยัง MyMainFactory ผ่าน IOC และผลักไปที่ Constructor คอนกรีตด้วยตนเอง

public class MyMainFactory
{
    MyOtherDependency _dependency;

    public MyMainFactory(MyOtherDependency dependency)
    {
        _dependency = dependency;
    }

    public IMain Create(Runner runner)
    {
        return new ConcreteMain(runner, _dependency);
    }
}

public class Runner
{
    IMain _myMain;
    public Runner(MyMainFactory factory)
    {
        _myMain = factory.Create(this)
    }
}

4

ใช้คอนเทนเนอร์ IOC ที่รองรับสถานการณ์นี้ ฉันรู้ว่า AutoFac และอื่น ๆ ที่เป็นไปได้ทำ เมื่อใช้ AutoFac ข้อ จำกัด คือหนึ่งในการอ้างอิงต้องมี PropertiesAutoWired = true และใช้คุณสมบัติสำหรับการพึ่งพา


4

คอนเทนเนอร์ IOC บางตัว (เช่น Spring หรือ Weld) สามารถแก้ปัญหานี้ได้โดยใช้พร็อกซีที่สร้างขึ้นแบบไดนามิก พร็อกซีจะถูกฉีดที่ปลายทั้งสองและวัตถุจริงจะสร้างอินสแตนซ์ก็ต่อเมื่อมีการใช้พรอกซีครั้งแรกเท่านั้น ด้วยวิธีนี้การพึ่งพาแบบวนรอบไม่ใช่ปัญหาเว้นแต่ว่าทั้งสองวัตถุจะเรียกวิธีการซึ่งกันและกันในตัวสร้าง (ซึ่งง่ายต่อการหลีกเลี่ยง)


4

ด้วย Unity 3 ตอนนี้คุณสามารถฉีดLazy<T>ได้ สิ่งนี้คล้ายกับการฉีดแคชโรงงาน / วัตถุ

เพียง แต่ต้องแน่ใจว่าคุณไม่ได้ทำงานใน ctor ที่ต้องการแก้ไขการพึ่งพา Lazy

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.