Универсальный интерфейс для иерархической коллекции - PullRequest
0 голосов
/ 02 апреля 2011

Я хочу создать иерархическую структуру, которая будет тесно следовать следующей структуре:

(1) INodeBase - содержит свойство Children, представляющее собой коллекцию (INodeBase)

(2) INodeFolder - происходит от INodeBase.

(3) NodeFolder - реализует INodeFolder. Реализация свойства Children должна позволять ему содержать элементы как типа INodeFolder, так и типа INodeItem.

(4) INodeItem - наследуется от INodeBase.

(5) NodeItem - реализует INodeItem. Реализация свойства Children должна позволять ему содержать элементы типа NodeResult.

(6) INodeResult - наследуется от INodeBase.

(7) NodeResult - реализует INodeResult - мне не нужно / не нужно дочернее свойство в этом классе, но я хочу, чтобы пустая коллекция зависала от класса, если общая иерархия может быть правильно структурирована.

По сути, я хочу один базовый тип для узла - и предполагается, что узел может содержать другие узлы. Я думаю, что дженерики - это путь, но проблема в том, что если я запрашиваю всю иерархическую коллекцию, или, скорее, если я фильтрую ее, когда я рекурсивно просматриваю дочернюю коллекцию каждого ребенка (т.е. коллекции), я хочу знать, что к чему - то есть, если я нахожусь в коллекции Children папки, я хочу знать, когда я нажимаю FolderNode и когда я нажимаю ItemNode, а затем, когда я повторяю через свойство Children ItemNode, я хочу знать, что я имею дело только с типом NodeResult.

Итак, каков наилучший подход к вышесказанному?

Chris

Ответы [ 3 ]

1 голос
/ 02 апреля 2011

Altought, это не главный ответ, почему бы вам не спроектировать иерархию объектов, напрямую с классами и объектами, вместо интерфейсов.Позже вы можете превратить его в иерархию интерфейсов.

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

Суперклассы (как абстрактные, так и конкретные) работают очень похоже на интерфейсы, но имеют то преимущество, что вы можете создавать из них объекты, см. Работающий код.

Мне кажется, что вы пытаетесь сделать что-то очень концептуальное.Возможно, если вы сделаете небольшой шаг назад, перейдя к классам, которые могут быть реализованы, вы можете получить более практичное представление о вашей идее.

Интерфейсы - очень хорошая функция, но сначала ее трудно понять.Иногда хорошей идеей является «сделать один шаг назад, сделать пять шагов вперед».

1 голос
/ 04 апреля 2011

Я думаю, что весь приведенный ниже код следует тем правилам, которые вы хотели, особенно для всех узлов, реализующих INodeBase и с бонусом, что NodeResult не имеет Children коллекции.

Вот необходимые интерфейсы:

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

Я добавил пару INodeContainer интерфейсов - один не универсальный, а другой универсальный.

Вот определения классов:

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

Теперь вы можете использовать код, подобный следующему:

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.

Вы можете ограничить INodeContainer<C> для конкретных типов, а не для интерфейсов, если хотите. Это зависит от того, хотите ли вы ссылаться на объекты по интерфейсу или по конкретному классу.

Дайте мне знать, если это работает для вас.

0 голосов
/ 02 апреля 2011

Зачем вам нужно, чтобы папки и элементы реализовывали один и тот же интерфейс?

Мне кажется, что более простая структура, как эта, будет работать лучше ...

public class Folder
{
    public List<Folder> Folders { get; }
    public List<Item> Items { get; }
}

public class Item
{
    public List<Result> Results { get; }
}

public class Result
{

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...