Generic Interface For Hierarchical Collection
I want to create a hierarchical structure that will closely follow the following structure:
(1) INodeBase - contains a Children property that is a Collection of itself (INodeBase)
(2) INodeFolder - derives from INodeBase.
(3) NodeFolder - implements INodeFolder. The implementation of the Children property should allow it to contain items of both the INodeFolder type and INodeItem type.
(4) INodeItem - derives from INodeBase.
(5) NodeItem - implements INodeItem. The implementation开发者_如何学Python of the Children property should allow it to contain items of the NodeResult type.
(6) INodeResult - derives from INodeBase.
(7) NodeResult - implements INodeResult - I don't need/want a children property in this class, but, I am willing to have an empty collection hang off of the class if the overall hierarchy can be structured properly.
Essentially, I want one base type for node - and the assumption should be that a node can contain other nodes. I am thinking that generics is the way to go - but - the problem is that if I query off of the overall hierarchical collection, or rather, if I filter on it, when I am recursively going through each child's children collection (i.e. flattening my collection), I want to know what is what - i.e., if I am in the Children collection of a folder, I want to know when I hit a FolderNode and when I hit an ItemNode, and then when I recurse through the Children property of an ItemNode, I want to know that I am just dealing with the NodeResult type.
So, what is the best approach for the above?
Chris
I think the below code all follows the rules you wanted, particularly with all node implementing INodeBase
and with the bonus that NodeResult
doesn't have a Children
collection.
Here are the interfaces required:
public interface INodeBase
{
}
public interface INodeContainer : INodeBase
{
}
public interface INodeContainer<C> : INodeContainer
where C : INodeBase
{
IList<C> Children { get; }
}
public interface INodeFolder : INodeContainer<INodeContainer>
{
}
public interface INodeItem : INodeContainer<INodeResult>
{
}
public interface INodeResult : INodeBase
{
}
I have added a couple of INodeContainer
interfaces - one non-generic & the other generic.
Here are the class definitions:
public abstract class NodeBase : INodeBase
{
}
public abstract class NodeBase : INodeBase
{
}
public abstract class NodeContainer : NodeBase, INodeContainer
{
}
public abstract class NodeContainer<C> : NodeContainer, INodeContainer<C>
where C : INodeBase
{
public NodeContainer() { this.Children = new List<C>(); }
public IList<C> Children { get; private set; }
}
public class NodeFolder : NodeContainer<INodeContainer>, INodeFolder
{
}
public class NodeItem : NodeContainer<INodeResult>, INodeItem
{
}
public class NodeResult : INodeResult
{
}
Now you can use the code like this:
var nf = new NodeFolder();
nf.Children.Add(new NodeFolder()); // Add `INodeFolder`
var ni = new NodeItem();
nf.Children.Add(ni); // or add `INodeFolder`, but nothing else.
var nr = new NodeResult();
ni.Children.Add(nr); // Only add `INodeResult`.
// nr does not have "Children" collection.
You can restrict INodeContainer<C>
on the concrete types rather than the interfaces if you wish. It depends on if you want to reference the objects by interface or by concrete class.
Let me know if this works for you.
Altought, is not the main answer, why don't you design your object hierarchy, directly with classes and objects, instead of interfaces. Later you may turn it into an interface hierarchy.
enum NodeTypes { Unknown, Folder, File };
public abstract class CNodeBase {
protected List<CNodeBase> FItems;
public List<CNodeBase> Items();
public virtual NodeTypes NodeType() {
return NodeTypes.Unknown;
}
}
public class CNodeFolder: CNodeBase {
// ...
public override NodeTypes NodeType() {
return NodeTypes.Folder;
}
}
public class CNodeFile: CNodeBase {
// ...
public override NodeTypes NodeType() {
return NodeTypes.File;
}
}
public class CDemo {
void main () {
// root node is ALWAYS A folder
CNodeFolder RootNode = new CNodeFolder("\\");
CNodeBase AnyNode = null;
AnyNode = new CNodeFolder("c:");
RootNode.Add(AnyNode);
AnyNode = new CNodeFolder("d:");
RootNode.Add(AnyNode);
AnyNode = new CNodeFile("readme.txt");
RootNode.Add(AnyNode);
}
}
Superclasses (both abstract or concrete) work very similar to interfaces, but have the advantage that you can instantiate objects from them, see the code working.
Its seems to me, that you are trying to do something very conceptual. Maybe if you step back a little, going to classes that can be instantiated, you may get a more practical view of your idea.
Interfaces are a very good feature, yet, its difficult to understand, at first. Sometimes, is a good idea to "take one step back, to go five steps forward".
Why do you need to have folders and items implement the same interface?
It seems to me that a simpler structure like this would work better...
public class Folder
{
public List<Folder> Folders { get; }
public List<Item> Items { get; }
}
public class Item
{
public List<Result> Results { get; }
}
public class Result
{
}
精彩评论