C#中具有父引用的文件和文件夹结构的复合模式

时间:2012-03-28 21:34:12

标签: c# design-patterns

我目前正在努力实现一组文件系统类。如果我没有弄错的话,我想这需要复合模式。所以我设置了以下类:

抽象类Node,其中包含对其父文件夹的引用以及实现Folder的两个类FileNode。文件夹包含其所有子项的集合以及添加和删除子项的方法。

问题是,我无法弄清楚如何正确实施所有方法。在我看到的所有例子中,没有提到孩子的父母。 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 个答案:

答案 0 :(得分:1)

如何以相反的方式做到这一点:

Folder.AddChild(Node child) 
{
   child.Parent = this;
   this._children.Add(child); // or what ever your doing to store the children
   ...
}

答案 1 :(得分:1)

如果要将子项添加到父项,则应通过父项上的方法完成。然后,父母可以确认/验证自己的状态,并满足其前提条件。它不是由一个节点来确定它的父节点是否有效 - 让父节点这样做。

因此,通过代码,你有类似的东西:

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

我只是自由职业者,但这是一个想法。我真的认为没有必要跟踪你的父母,我认为最终会变成一个相当麻烦的解决方案。

答案 2 :(得分:1)

当我实现双向关联时,我通常会将所有关联维护移动到其中一方。在这种情况下,我选择了文件夹。

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 使用单向关联,您可以使用简单的代码,而不会在Node类中使用丑陋的Folder字段(我讨厌基类依赖于它的子代)。添加/删除文件也没什么问题。

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

答案 3 :(得分:0)

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

孩子知道如何建立父母;父母知道如何收养孩子。