使用IoC链接层,将较低的回调设置为较高并避免循环引用

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

由于一些内部事件不时生成,因此下层也将回调上层。

我正在使用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(); } } 

我认为您的设计没有任何问题,尽管您已经注意到它不是最简单的配置。 问题不在于您的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(); container.Register(); container.RegisterInitializer(upper => { var lower = (Lower)container.GetInstance(); lower.SetCallback(upper); upper.Lower = lower; }); 

由于LowerUpper是正常服务,您可以像往常一样解决它们。 通过为Upper注册初始化程序委托,您可以在容器创建Upper之后进行一些额外的初始化。 该初始化器将LowerUpper连接在一起。 由于组合根知道ILowerLower ,我们可以安全地从ILowerLower ,而不会破坏任何规则。 最好的设计是ILower接口保持干净,并且忘记了ILowerToUpperCallback ,这实际上是一个实现细节。

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

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

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

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

这是帮我努力解决问题的SO问题。

业务层门面与明确的业务组件

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

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

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

首先,您的代码对我来说很好。

但是,这看起来像Observer模式 。

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