views:

80

answers:

1

Hi all,

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! :)

+1  A: 

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;
        }
    }
}
Wallace Breza
Thanks. Category Tree should be readonly, but in some other senarios, for example, a custom asp.net Tree Control, control users should be able to Add/Remove Nodes from the tree, so the tree should not be readonly. According to you answer, I think we can do like this:(1) Make ITreeNode readonly, that is, no setter for Parent, and return IEnumerable<T> for ChildNodes.(2) For Category Tree, we make Category implement ITreeNode just like you do in your example.(3) For Tree Control, we use "new" keyword to hide the ChildNodes property and let ChildNodes return a non-readonly collection.Right?
Dylan Lin
@Dylan Lin - Yes, that would work. But on the Tree Control you should make the ChildNodes return IEnumerable<ITreeNode> so you can control access to the collection based on the implementation.
Wallace Breza
I got it. Many thanks.
Dylan Lin