Составной шаблон для структуры файлов и папок с родительской ссылкой в ​​C # - PullRequest
4 голосов
/ 29 марта 2012

В настоящее время я борюсь с реализацией набора классов файловой системы. Я предполагаю, что это требует составного паттерна, если я не ошибаюсь. Поэтому я настроил следующие классы:

Абстрактный класс Node, который имеет ссылку на свою родительскую папку и два класса Folder и File, которые реализуют Node. Папка содержит коллекцию всех своих дочерних элементов и методов для добавления и удаления дочерних элементов.

Дело в том, что я не могу понять, как правильно реализовать все методы. Во всех примерах, которые я видел, нет ссылки на родителя у детей. Как метод AddChild может гарантировать, что родительская ссылка на ребенка установлена ​​правильно? Я решил это, проверив, был ли child.Parent уже установлен в папку или он выдает ArgumentException. Ситуация усложняется еще и тем, что AddChild может также выдавать исключение, например DuplicateNameException или что-то в этом роде. Теперь мои методы выглядят так:

File.AddTo(Folder folder) {
    this.Parent = folder;
    try {
        folder.AddChild(this);
    } catch {
        this.Parent = null;
        throw;
    }
}

Folder.AddChild(Node child)
{
    if(child.Parent != this)
        throw new ArgumentException(...);
    ...
}

Теперь у меня есть этот уродливый AddTo метод, и я не могу сделать что-то вроде someFolder.AddChild(new File(...)). Интересно, как это было реализовано с ListViewItem например. Там я могу просто сделать someListView.Items.Add(new ListViewItem(...)).

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

РЕДАКТИРОВАТЬ : минимальные полные определения классов ниже.

abstract class Node
{
    public Folder Parent { get; protected set; }
    public string Name { get; private set; }

    public Node(string name) {
        Parent = null;
        Name = name;
    }
}

class Folder : Node {
    private Dictionary<string, Node> _children;

    public Folder(string name) : base(name) {
        // Other initializations here...
    }

    public void AddChild(Node child) {
        if(child is Folder)
            ((Folder)child).Parent = this; // Damn, doesn't work for files!!!
        else if(child.Parent != this)
            throw new ArgumentException();

        if(_children.ContainsKey(child.Name))
            throw new DuplicateNameException();

        _children[child.Name] = child;
    }
}

class File : Node {
    public File(string name) : base(name) {
        // Other initializations here...
    }

    public void AddTo(Folder folder) {
        Parent = folder;
        try {
            folder.AddChild(this);
        } catch {
            Parent = null;
        }
    }
}

Ответы [ 4 ]

1 голос
/ 29 марта 2012

Когда я внедряю двунаправленные ассоциации, я обычно перемещаю все обслуживание ассоциаций в одну из сторон. В этом случае я бы выбрал папку.

public abstract class Node
{
    public Folder Parent { get; set; }
    public string Name { get; set; }
    public abstract long Size { get; }
}

public class File : Node
{        
    private long _size;

    public override long Size
    {
        get { return _size; }
    }

    public void AddTo(Folder folder)
    {
        folder.Add(this);
    }

    public void RemoveFrom(Folder folder)
    {
        folder.Remove(this);
    }
}

public class Folder : Node
{
    private List<Node> _children = new List<Node>();

    public void Add(Node node)
    {
        if (node.Parent == this)
            return; // already a child of this folder

        _children.Add(node);
        node.Parent = this;
    }

    public void Remove(Node node)
    {
        if (node.Parent != this)
            return; // not a child of this folder

        _children.Remove(node);
        node.Parent = null;
    }

    public override long Size
    {
        get { return _children.Sum(node => node.Size); }
    }
}

PS попробуйте устранить двунаправленную ассоциацию, это добавляет много головной боли.

UPDATE С однонаправленной ассоциацией у вас есть простой код, без уродливого поля Folder в классе Node (я ненавижу, когда базовый класс зависит от его потомка). Также нет головной боли при добавлении / удалении файлов.

public abstract class Node
{
    public string Name { get; set; }
    public abstract long Size { get; }
}

public class File : Node
{        
    private long _size;

    public override long Size
    {
        get { return _size; }
    }
}

public class Folder : Node
{
    private List<Node> _children = new List<Node>();

    public void Add(Node node)
    {
        if (_children.Contains(node))
            return;

        _children.Add(node);
    }

    public void Remove(Node node)
    {
        if (!_children.Contains(node))
            return;

        _children.Remove(node);
    }        

    public override long Size
    {
        get { return _children.Sum(node => node.Size); }
    }
}
1 голос
/ 29 марта 2012

Как насчет того, чтобы сделать это наоборот:

Folder.AddChild(Node child) 
{
   child.Parent = this;
   this._children.Add(child); // or what ever your doing to store the children
   ...
}
1 голос
/ 29 марта 2012

Если вы добавляете дочернего элемента к родителю, это должно быть сделано с помощью метода для родительского элемента. Затем родитель может подтвердить / подтвердить свое собственное состояние и то, что его предварительные условия выполнены. Узлу не нужно выяснить, действителен ли его родитель - пусть это сделает родитель.

Итак, с помощью кода у вас есть что-то вроде:

public class Node
{
    public string Name { get; set; }
    public abstract void Add(Node child);
    protected abstract void CreateOnDisk();
}

public class File
{
    public override void Add(Node child)
    {
         //No op, since you can't add a child to a file
    }

    protected override void CreateOnDisk()
    {
        File.Create(this.Name);
    }
}

public class Directory
{
    public override void Add(Node child)
    {
        child.Name = Path.Combine(this.Name, child.Name);
        child.CreateOnDisk();
    }

    protected override CreateOnDisk()
    {
        Directory.Create(this.Name);
    }
}

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

0 голосов
/ 29 марта 2012

AddChild() - это метод для родителя.

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

public abstract class Node 
{
    private Node parent;

    internal void SetParent(Node parent)
    {
        this.parent = parent;
    }
}

public class Folder : Node
{
    void AddChild(Node child) 
    {
        this.children.Add(child);
        child.SetParent(this); // or, you could use a C# Property
    }
}


public class File : Node
{
}

Ребенок знает, как установить своего родителя;родитель знает, как усыновить ребенка.

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