我刚开始使用Unity应用程序块来解耦我的类并使单元测试更容易。但是,我遇到了循环依赖的问题。
我有一个Facade类型的聊天机器人。它是一个单例类,它处理所有类型的辅助类,并提供启动和配置机器人的中心位置。我还有一个名为AccessManager的类,它可以管理对bot命令和资源的访问。归结为本质,我设置了这样的类:
public class Bot
{
public string Owner { get; private set; }
public string WorkingDirectory { get; private set; }
private IAccessManager AccessManager;
private Bot()
{
// do some setup
// LoadConfig sets the Owner & WorkingDirectory variables
LoadConfig();
// init the access mmanager
AccessManager = new MyAccessManager(this);
}
public static Bot Instance()
{
// singleton code
}
...
}
和AccessManager类:
public class MyAccessManager : IAccessManager
{
private Bot botReference;
public MyAccesManager(Bot botReference)
{
this.botReference = botReference;
SetOwnerAccess(botReference.Owner);
}
private void LoadConfig()
{
string configPath = Path.Combine(
botReference.WorkingDirectory,
"access.config");
// do stuff to read from config file
}
...
}
我想将此设计更改为使用Unity应用程序块。我想使用Unity来生成Bot单例并在某种类型的引导方法中加载AccessManager接口,该方法在其他任何操作之前运行。
public static void BootStrapSystem()
{
IUnityContainer container = new UnityContainer();
// create new bot instance
Bot newBot = Bot.Instance();
// register bot instance
container.RegisterInstance<Bot>(newBot);
// register access manager
container.RegisterType<IAccessManager,MyAccessManager>(newBot);
}
当我想在Bot构造函数中获取对Access Manager的引用时,我可以这样做:
IAcessManager accessManager = container.Resolve<IAccessManager>();
在系统的其他地方获取对Bot单例的引用:
// do this
Bot botInstance = container.Resolve<Bot>();
// instead of this
Bot botInstance = Bot.Instance();
问题是方法BootStrapSystem()
会爆炸。当我创建一个bot实例时,它将尝试解析IAccessManager但是无法解决,因为我还没有注册类型(这是下一行)。但是我无法在Bot创建之前移动注册,因为作为注册的一部分,我需要将Bot作为参数传递!循环依赖!!尔加!!!
这告诉我,我有这种结构的方式存在缺陷。但是我该如何解决呢?帮助!
答案 0 :(得分:1)
首先,您应该让容器管理您的单身生命周期,而不是自己编写单例代码。要删除循环依赖项,可以从访问管理器构造函数中删除Bot。相反,您使用初始化方法。
container.RegisterType<Bot>(new ContainerControlledLifecycleManager()); // from my memory...
container.RegisterType<IAccessManager,MyAccessManager>();
var bot = container.Resolve<Bot>();
// Bot.cs
public Bot(IAccessManager manager)
{
manager.InitializeFor(this);
}
出于可测试性原因,您不应该从构造函数中调用您的IOC容器。
答案 1 :(得分:1)
您可以通过以下方式更改设计,让您的生活更轻松:
请注意,这些建议都不涉及Unity。 Unity(或任何其他DI容器)不是银弹,它会神奇地使您的代码松散耦合。您必须首先了解principles behind DI,然后您可以使用任何DI容器作为工具来帮助您连接依赖图。