Làm thế nào để xử lý phụ thuộc vòng tròn


15

Tiêu đề nói "Phụ thuộc tròn", nhưng nó không phải là từ ngữ chính xác, bởi vì với tôi thiết kế có vẻ vững chắc.
Tuy nhiên, hãy xem xét kịch bản sau đây, trong đó các phần màu xanh được đưa ra từ đối tác bên ngoài và màu cam là triển khai của riêng tôi. Cũng giả sử có nhiều hơn một ConcreteMain, nhưng tôi muốn sử dụng một cái cụ thể. (Trong thực tế, mỗi lớp có một số phụ thuộc hơn, nhưng tôi đã cố gắng đơn giản hóa nó ở đây)

Kịch bản

Tôi muốn kích hoạt tất cả những điều này với Depency Injection (Unity), nhưng rõ ràng tôi nhận được một StackOverflowExceptionđoạn mã sau, bởi vì Runner cố gắng khởi tạo ConcreteMain và ConcreteMain cần một Runner.

IUnityContainer ioc = new UnityContainer();
ioc.RegisterType<IMain, ConcreteMain>()
   .RegisterType<IMainCallback, Runner>();
var runner = ioc.Resolve<Runner>();

Làm thế nào tôi có thể tránh điều này? Có cách nào để cấu trúc cái này để tôi có thể sử dụng nó với DI không? Kịch bản tôi đang làm bây giờ là thiết lập mọi thứ theo cách thủ công, nhưng điều đó đặt ra một sự phụ thuộc lớn vào ConcreteMainlớp tạo ra nó. Đây là những gì tôi đang cố gắng tránh (với đăng ký Unity trong cấu hình).

Tất cả các mã nguồn dưới đây (ví dụ rất đơn giản!);

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

Câu trả lời:


10

Những gì bạn có thể làm là tạo một nhà máy, MainFactory trả về một thể hiện của ConcreteMain là IMain.

Sau đó, bạn có thể đưa Nhà máy này vào công cụ xây dựng của bạn. Tạo Main với nhà máy và vượt qua chính inn như một tham số.

Bất kỳ sự phụ thuộc nào khác vào hàm tạo ConcreteMain đều có thể được chuyển vào MyMainFactory thông qua IOC và được đẩy đến hàm tạo cụ thể theo cách thủ công.

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

Sử dụng một thùng chứa IOC hỗ trợ kịch bản này. Tôi biết rằng AutoFac và những người khác có thể làm được. Khi sử dụng AutoFac, hạn chế là một trong các phụ thuộc phải có PropertiesAutoWired = true và sử dụng Thuộc tính cho phụ thuộc.


4

Một số bộ chứa IOC (ví dụ Spring hoặc Weld) có thể giải quyết vấn đề này bằng cách sử dụng proxy được tạo động. Proxy được tiêm ở cả hai đầu và đối tượng thực chỉ được khởi tạo khi proxy được sử dụng lần đầu tiên. Theo cách đó, các phụ thuộc vòng tròn không phải là vấn đề trừ khi hai đối tượng gọi các phương thức với nhau trong các hàm tạo của chúng (điều này dễ tránh).


4

Với Unity 3, giờ bạn có thể tiêm Lazy<T>. Điều này tương tự như việc chèn bộ đệm của Factory / object.

Chỉ cần chắc chắn rằng bạn không làm việc trong ctor yêu cầu giải quyết sự phụ thuộc của Lazy.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.