(我将它标记为C#和
Java,因为它在两种语言中都是相同的问题.)
说我有这些课程
interface IKernel { // Useful members,e.g. AvailableMemory,TotalMemory,etc. } class Kernel : IKernel { private /*readonly*/ FileManager fileManager; // Every kernel has 1 file manager public Kernel() { this.fileManager = new FileManager(this); /* etc. */ } // implements the interface; members are overridable } class FileManager { private /*readonly*/ IKernel kernel; // Every file manager belongs to 1 kernel public FileManager(IKernel kernel) { this.kernel = kernel; /* etc. */ } }
这种设计的问题是,只要FileManager尝试在其构造函数内部执行任何内容(它可能合理地需要),它就会在潜在的子类实例上调用一个虚方法,该实例的构造函数尚未被调用.
在你可以定义真正的构造函数(而不是初始化程序,如C#/ Java)的语言中不会出现这个问题,因为在它们的构造函数被调用之前,子类甚至都不存在……但是在这里,会出现这个问题.
那么什么是最佳/适当的设计/实践,以确保不会发生这种情况?
编辑:
我不一定说我需要循环引用,但事实是内核和FileManager都相互依赖.如果您有关于如何在不使用循环引用的情况下缓解此问题的建议,那么这也很棒!
解决方法
就个人而言,我不喜欢循环引用.但是如果你决定离开它们,你可能会增加一些懒惰:
interface IKernel { // Useful members,etc. } class Kernel : IKernel { private readonly Lazy<FileManager> fileManager; // Every kernel has 1 file manager public Kernel() { this.fileManager = new Lazy<FileManager>(() => new FileManager(this)); /* etc. */ } // implements the interface; members are overridable } class FileManager { private /*readonly*/ IKernel kernel; // Every file manager belongs to 1 kernel public FileManager(IKernel kernel) { this.kernel = kernel; /* etc. */ } }
这里的懒惰让我们确保在查询FileManager实例时将完全初始化IKernel实现.