我让自己陷入了两个类之间的循环依赖,我正在尝试提出一个干净的解决方案。
这是基本结构:
class ContainerManager {
Dictionary<ContainerID, Container> m_containers;
void CreateContainer() { ... }
void DoStuff(ContainerID containerID) { m_containers[containerID].DoStuff(); }
}
class Container {
private Dictionary<ItemID, Item> m_items;
void SetContainerResourceLimit(int limit) { ... }
void DoStuff() {
itemID = GenerateNewID();
item = new Item();
m_items[itemID] = item;
// Need to call ResourceManager.ReportNewItem(itemID);
}
}
class ResourceManager {
private List<ItemID> m_knownItems;
void ReportNewItem(ItemID itemID) { ... }
void PeriodicLogic() { /* need ResourceLimit from container of each item */ }
}
ContainerManager 作为 WCF 服务公开:客户端可以通过它创建项目和容器的外部点。ResourceManager 需要知道创建的新项目。它进行后台处理,有时它需要来自项目容器的信息。
现在,Container 需要有 ResourceManager(调用 ReportNewItem),它将从 ContainerManager 传递。ResourceManager 需要来自 Container 的信息,它只能通过 ContainerManager 获取。这会产生循环依赖。
我更喜欢使用接口(而不是具体对象)初始化对象,以便以后可以为单元测试创建模拟对象(例如创建模拟 ResourceManager),但我仍然遇到 CM 需要 RM 的问题它的 ctor,而 RM 的 ctor 中需要一个 CM。
显然,这是行不通的,所以我正在尝试提出创造性的解决方案。到目前为止,我有:
1)将要使用的Container传递给ReportNewItem,让ResourceManager直接使用。这很痛苦,因为 ResourceManager 会持续存储它知道的 ItemID。这意味着当在崩溃之后初始化 ResourceManager 时,我必须重新为其提供所需的所有容器。
2) 分两个阶段初始化 CM 或 RM:例如:RM = new RM(); CM = 新CM(RM);RM.SetCM(CM); 但我认为这很丑陋。
3) 使ResourceManager 成为ContainerManager 的成员。因此CM可以用“this”构造RM。这会起作用,但是当我想创建一个 RM 模拟时,在测试期间会很痛苦。
4) 用 IResourceManagerFactory 初始化 CM。让 CM 调用 Factory.Create(this),它将使用“this”初始化 RM,然后存储结果。为了测试,我可以创建一个模拟工厂,它将返回一个模拟 RM。我认为这将是一个很好的解决方案,但是为此创建一个工厂有点尴尬。
5) 将 ResourceManager 逻辑分解为 Container-specific 逻辑,并在每个 Container 中拥有不同的实例。不幸的是,逻辑确实是跨容器的。
我认为“正确”的方法是将一些代码提取到 CM 和 RM 都依赖的第三类中,但我想不出一个优雅的方法来做到这一点。我想出了要么封装“报告项目”逻辑,要么封装组件信息逻辑,这两种方法似乎都不对。
任何见解或建议将不胜感激。