1

我有一个场景,我需要下层由上层控制,就像木偶大师拉弦一样。

由于不时产生一些内部事件,下层也会回调上层。

我正在使用 SimpleInjector,我将 ILower 注入到 Upper 构造函数中。我不能将 Upper 注入到 Lower 中,因为它会导致循环引用。

相反,我有一个注册回调函数来链接两个层。但是,我必须使用空检查来分散我的代码。

有没有更好的方法或不同的架构来实现对象的这种链接?

// an interface that transport can callback from transport to client
public interface ILowerToUpperCallback
{
    void ReplyA();
    void ReplyB();
}

// transport interface that client calls
public interface ILower
{
    void Test1();
    void Test2();
    void RegisterCallback(ILowerToUpperCallback callback);
}

public class Upper : ILowerToUpperCallback
{
    private readonly ILower lower;

    public Upper(ILower lower)
    {
        this.lower = lower;
        this.lower.RegisterCallback(this);
    }

    void ReplyA()
    {
    }

    void ReplyB()
    {
    }
}

public class Lower : ILower
{
    private ILowerToUpperCallback callback;

    /* this is not possible, would cause a circular reference
    public Lower(ILowerToUpperCallback callback)
    {
        this.callback = callback;
    }
    */

    // set by different method instead, what happens if this is never set?!
    void RegisterCallback(ILowerToUpperCallback callback)
    {
        this.callback = callback;
    }

    void OnTimer()
    {
        // some timer function

        if(this.callback != null) // these null checks are everywhere :(
            this.callback.ReplyA();
    }
}
4

4 回答 4

1

一种可能的方法是在您的最低层之一中拥有一个消息传递子系统,以便下层和上层都可以参与发布/订阅模型。

例如,消息传递子系统可以是一个EventAggregator. 它在将发布者与订阅者分离方面做得很好。使用可从两个层访问的专用事件模型,您几乎可以使用另一个对象控制任何对象。

不过,我并不反对你的做法。循环依赖没有错。例如,MVP(Model-View-Presenter)基于 Views 和 Presenters 之间的依赖关系。

于 2012-11-19T19:48:40.630 回答
1

我认为您的设计没有任何问题,尽管您已经注意到它不是最容易配置的东西。问题不在于您的 DI 框架的限制,而更多在于您必须执行的心理体操。

这是一个想法。将您的课程更改为以下内容:

public class Upper : IUpper, ILowerToUpperCallback
{
   public Upper(/* all depedencies except ILower */) { }

    // Promote ILower to property dependency
    public ILower Lower { get; set; }
}

public class Lower : ILower
{
    // Use the Null Object Pattern for default implementation to prevent
    // null checks.
    private ILowerToUpperCallback callback = new NullCallback();

    public Upper(/* all dependencies except ILowerToUpperCallback */)
    {
        this.callback = callback;
    }

    // Allow overriding the default implementation using a method, just
    // as you are already did.
    public SetCallback(ILowerToUpperCallback callback)
    {
        if (callback == null) throw new ArgumentNullException("callback");
        this.callback = callback;
    }
}

使用这种设计,您可以按如下方式连接所有内容:

container.Register<ILower, Lower>();
container.Register<IUpper, Upper>();

container.RegisterInitializer<Upper>(upper =>
{
    var lower = (Lower)container.GetInstance<ILower>();
    lower.SetCallback(upper);
    upper.Lower = lower;
});

由于LowerUpper是正常服务,您可以照常解决它们。通过为 注册一个初始化委托Upper,您可以在创建容器后进行一些额外的初始化Upper。这个初始化器连接Lower在一起Upper。由于 Composition Root 知道ILowerand Lower,我们可以安全地从ILowerto 转换Lower,而不会违反任何规则。这种设计的最佳之处在于ILower接口保持干净,并且忽略了ILowerToUpperCallback实际上是实现细节的 .

于 2012-11-19T20:03:33.163 回答
1

您的示例有点抽象,难以理解您的架构,但您似乎可以从域事件的概念中受益。看起来你的回调想法也是一样的。我最近实现了一个简单版本的域事件模式,它可以让我的主要业务层组件协同工作,而无需组件相互引用。正如您所说,Upper 可以引用 ILower,但随后使用 Lower 引用 IUpper 会导致循环引用并可能导致问题。

这是帮助我解决问题的 SO 问题。

业务层外观与混合业务组件

我还发现这两个链接有助于理解领域事件的概念和实现。

http://jasondentler.com/blog/2009/11/simple-domain-events/

http://blog.robustsoftware.co.uk/2009/08/better-domain-event-raiser.html

于 2012-11-19T19:54:27.263 回答
1

首先,您的代码对我来说看起来不错。

但是,这看起来像观察者模式

您的第二个问题(null检查)可以通过使用Null 对象IObserver(这是/的无操作实现ILowerToUpperCallback)初始化回调/观察者字段来处理。

于 2012-11-19T19:56:21.790 回答