寻找更好的设计:只读内存缓存机制

发布于 2024-09-01 09:20:15 字数 1956 浏览 2 评论 0原文

我有一个类别实体(类),它有零个或一个父类别和许多子类别——它是一个树结构。类别数据存储在 RDBMS 中,因此为了获得更好的性能,我想在启动应用程序时加载所有类别并将它们缓存在内存中。

我们的系统可以有插件,并且我们允许插件作者访问类别树,但他们不应该修改缓存的项目和树(我认为非只读设计可能会导致这个场景中的一些微妙的错误),只有系统知道何时以及如何刷新树。

以下是一些演示代码:

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; }
}

这是好是坏?还有一些更好的设计吗?多谢! :)

I have a Category entity (class), which has zero or one parent Category and many child Categories -- it's a tree structure. The Category data is stored in a RDBMS, so for better performance, I want to load all categories and cache them in memory while launching the applicaiton.

Our system can have plugins, and we allow the plugin authors to access the Category Tree, but they should not modify the cached items and the tree(I think a non-readonly design might cause some subtle bugs in this senario), only the system knows when and how to refresh the tree.

Here are some demo codes:

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
    }
}

So far so good. However, I want to make ITreeNode interface reuseable, and for some other types, the tree should not be readonly. We are not able to do this with the above readonly ITreeNode, so I want the ITreeNode to be like this:

public interface ITreeNode<T> {
    // has setter
    T Parent { get; set; }
    // use ICollection<T> instead of IEnumerable<T>
    ICollection<T> ChildNodes { get; }
}

But if we make the ITreeNode writable, then we cannot make the Category Tree readonly, it's not good.

So I think if we can do like this:

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; }
}

Is this good or bad? Are there some better designs? Thanks a lot! :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

云朵有点甜 2024-09-08 09:20:15

您可以尝试的一件事是对您想要只读的 IEnumerable 项目使用 List。然后,当您填充树结构时,您可以在列表中内部调用 AsReadOnly() 方法,该方法将返回 ReadOnlyCollection并且数据的使用者将无法修改集合的内容。

从界面的角度来看,此方法不是ReadOnly,但尝试在集合上调用Add 等方法将会失败并引发异常。

为了保护其他成员,您可以在类中的 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;
        }
    }
}

One thing you can try is to use List<T> for your IEnumerable items that you want to be read only. Then when you populate your tree structure you can internally call the AsReadOnly() method on your list which will return a ReadOnlyCollection<T> and the consumers of your of your data would not be able to modify the contents of the collection.

This approach isn't ReadOnly from the interface's point of view, but an attempt to call a method like Add on the collection would fail and throw an exception.

To protect other members of you can build some private read-only flag within the implementation class of ITreeNode within the class, then set your flag to read-only on the cached items.

Something like this...


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;
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文