Composite template for file and folder structure with parent link in C #

I'm currently struggling with implementing a set of file system classes. I suppose this requires a composite diagram if I am not mistaken. Therefore, I created the following classes:

An abstract Node class that has a link to its parent folder and two Folder and File classes that implement Node . A folder contains a collection of all its children and methods for adding and removing children.

The fact is that I can’t understand how to correctly implement all the methods. In all the examples that I saw, children do not have a reference to the parent. How does the AddChild method ensure that the parent link of the child is set correctly? I decided that by checking if child.Parent already installed in the folder or child.Parent ArgumentException . The matter is complicated by the fact that AddChild can also throw an exception like DuplicateNameException or something else. So my methods look like this:

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

Now I have this ugly AddTo method and can't do something like someFolder.AddChild(new File(...)) . I wonder how this was implemented using ListViewItem , for example. There I can just do someListView.Items.Add(new ListViewItem(...)) .

My solution works, but I'm not sure if this is the right way to do this. Maybe someone has a better solution or can point to a good example. Thanks in advance.

EDIT . The following are the minimum complete class definitions.

 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
source share
4 answers

How to do it the other way around:

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

If you add a child to the parent, this must be done using the parent method. Then the parent can confirm / confirm his own condition and make sure that his premises are fulfilled. It is not up to node to find out if its parent is valid - let the parent do it.

So, with the code, you have something like:

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

I just freelanced a little from the head, but it gave an idea. I really think that there is no need to keep track of your parent, and I think that in the end it will be a rather cumbersome decision.

+1
source

When I implement bi-directional associations, I usually move all association services to one of the parties. In this case, I selected the folder.

 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 try to eliminate bidirectional communication, it adds a lot of headache.

UPDATE With unidirectional association, you have simple code, without the ugly Folder field in the Node class (I hate it when the base class depends on its child). Also there is no headache when adding / deleting files.

 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
source

AddChild() is the parent method.

When thinking about the purpose of a method and your desire to maintain a reference to a parent in a child, you need to set a property for the child that can be set by the parent, presumably in the AddChild method.

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

The child knows how to establish a parent; the parent knows how to adopt a child.

0
source

Source: https://habr.com/ru/post/1404006/


All Articles