我有一个Category实体(类),它有零个或一个父类别和许多子类别 - 它是一个树结构。类别数据存储在RDBMS中,所以为了获得更好的性能,我想在启动应用程序时加载所有类别并将它们缓存在内存中。
我们的系统可以有插件,我们允许插件作者访问类别树,但是他们不应该修改缓存的项目和树(我认为非readonly设计可能会在这个场景中引起一些微妙的错误),只有系统知道何时以及如何刷新树。
以下是一些演示代码:
public interface ITreeNode<T>
where T : ITreeNode<T>
{
// No setter
T Parent { get; }
IEnumerable<T> ChildNodes { get; }
}
// This class is generated by O/R Mapping tool (e.g. Entity Framework)
public class Category : EntityObject
{
public string Name { get; set; }
}
// Because Category is not stateless, so I create a cleaner view class for Category.
// And this class is the Node Type of the Category Tree
public class CategoryView : ITreeNode<CategoryView>
{
public string Name { get; private set; }
#region ITreeNode Memebers
public CategoryView Parent { get; private set; }
private List<CategoryView> _childNodes;
public IEnumerable<CategoryView> ChildNodes {
return _childNodes;
}
#endregion
public static CategoryView CreateFrom(Category category) {
// here I can set the CategoryView.Name property
}
}
到目前为止一切顺利。 但是,我想让ITreeNode接口可重用,而对于其他一些类型,树不应该是只读的。我们无法使用上面的只读ITreeNode执行此操作,因此我希望ITreeNode是这样的:
public interface ITreeNode<T> {
// has setter
T Parent { get; set; }
// use ICollection<T> instead of IEnumerable<T>
ICollection<T> ChildNodes { get; }
}
但是如果我们使ITreeNode可写,那么我们就不能使类别树只读,这不好。
所以我想如果我们可以这样做:
public interface ITreeNode<T> {
T Parent { get; }
IEnumerable<T> ChildNodes { get; }
}
public interface IWritableTreeNode<T> : ITreeNode<T> {
new T Parent { get; set; }
new ICollection<T> ChildNodes { get; }
}
这是好还是坏?有更好的设计吗?非常感谢! :)
答案 0 :(得分:1)
您可以尝试的一件事是将列表&lt; T&gt; 用于您想要只读的IEnumerable项目。然后,当您填充树结构时,您可以在内部调用列表中的 AsReadOnly()方法,该方法将返回 ReadOnlyCollection&lt; T&gt; ,并且您的数据的使用者将会无法修改集合的内容。
从界面的角度来看,这种方法不是 ReadOnly ,但尝试在集合上调用添加等方法会失败并抛出异常。< / p>
为了保护其他成员,可以在类中的ITreeNode实现类中构建一些私有只读标志,然后将标志设置为只读缓存项。
像这样......
public class TreeNode : ITreeNode
{
private bool _isReadOnly;
private List<ITreeNode> _childNodes = new List<ITreeNode>();
public TreeNode Parent { get; private set; }
public IEnumerable<ITreeNode> ChildNodes
{
get
{
return _isReadOnly ? _childNodes.AsReadOnly() : _childNodes;
}
}
}