Composite Pattern

The composite pattern is a structural pattern that combines objects into a tree structure to represent a partial-whole hierarchy. The composite pattern enables users to use a single object and a composite object consistently.

The composite pattern, sometimes called the partial-whole pattern, allows our tree structure problem to blur the concept of simple and complex elements. Clients can treat complex elements as if they were simple elements, thus decoupling the internal structure of the complex elements from the client.

role

1. Abstract Component

It is the common parent of all leaf and container artifacts, which declare all methods of leaf and container artifacts.

2. Leaf component (Leaf)

Represents the leaf node object in the combination, the leaf node has no children, for the container component method inherited from the parent class, because it can not be implemented, can throw an exception;

3. Composite

Defines branch point behavior that stores subwidgets and implements subwidget related operations such as Attach and Detach in the Component interface.

The sample



The CompositePattern namespace contains the FileSystem base class as the abstract component class, the File File class as the leaf component, the Folder class as the container component, and the FileInvalidException class for File invalid operations for exception handling. This example attempts to illustrate how the composite pattern behaves in terms of consistency in the use of callers using a simple file system.

public abstract class FileSystem {

    protected string _name = null;

    protected const char SPLIT_CHAR_FILE = ' ';

    protected const char SPLIT_CHAR_DIR = "▼";

    public FileSystem(string name) {
        this._name = name;
    }

    public abstract FileSystem Attach(FileSystem component);

    public abstract FileSystem Detach(FileSystem component);

    public abstract void Print(int depth = 0);

}
Copy the code

Abstract component, FileSystem class.

public class File : FileSystem {

    public File(string name) : base(name){}public override FileSystem Attach(FileSystem component) {
        throw new FileInvalidException(
            "You can not attach a component in a file!");
    }

    public override FileSystem Detach(FileSystem component) {
        throw new FileInvalidException(
            "You can not detach a component from a file!");
    }

    public override void Print(int depth = 0) {
        Console.WriteLine(new string(SPLIT_CHAR_FILE, depth) + _name); }}Copy the code

Leaf component, File File class.

public class Folder : FileSystem {

    private List<FileSystem> _childrens = null;

    public Folder(string name) : base(name) {
        _childrens = new List<FileSystem>();
    }

    public override FileSystem Attach(FileSystem component) {
        _childrens.Add(component);
        return this;
    }

    public override FileSystem Detach(FileSystem component) {
        _childrens.Remove(component);
        return this;
    }

    public override void Print(int depth = 0) {
        Console.WriteLine(new string(SPLIT_CHAR_DIR, depth) + _name);
        foreach (var component in _childrens) {
            component.Print(depth + 1); }}}Copy the code

Container component, Folder class.

public class FileInvalidException : Exception {

    public FileInvalidException(string message) : base(message){}public FileInvalidException(string message, Exception innerException)
        : base(message, innerException){}}Copy the code

Invalid file operation FileInvalidException class for simple exception handling.

public class Program {

    public static void Main(string[] args) {
        try {
            var root = new Folder("Root");

            var music = new Folder("My Music");
            music.Attach(new File("Heal the world.mp3"))
                    .Attach(new File("When You Say Nothing At All.mp3"))
                    .Attach(new File("Long Long Way to Go.mp3"))
                    .Attach(new File("Beautiful In White.mp3"));

            var video = new Folder("My Video");
            video.Attach(new File("The Shawshank Redemption.avi"))
                    .Attach(new File("Schindler's List.avi"))
                    .Attach(new File("Brave Heart.avi"));

            var png = new File("missing.png");
            root.Attach(png)
                .Detach(png)
                .Detach(png);

            root.Attach(new File("readme.txt"))
                .Attach(new File("index.html"))
                .Attach(new File("file.cs"))
                .Attach(music)
                .Attach(video)
                .Print();

            png.Attach(new File("error.json")); } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.ReadKey(); }}Copy the code

This is the caller’s code, and here is the output of the case:

Root readme.txt index.html file.cs ▼My Music Heal the world.mp3 When You Say Nothing At all.mp3 Long Long Way to go.mp3 My Video The Shawshank Redemption. Avi Schindler's List.avi Brave Heart.avi You can not attach a component in a file!Copy the code

advantages

1. Hierarchical complex objects can be clearly defined to represent all or part of the hierarchy of objects, making it easier to add new components; 2, the client is easy to call, the client can consistently use the composite structure or a single object; 3. Define a class hierarchy that contains leaf objects and container objects. Leaf objects can be combined into more complex container objects, and this container object can be combined, so that the recursion continues, can form complex tree structure; 4. It is easier to add object components into the composition body, and the client does not have to change the original code because of adding new object components.

disadvantages

1. Make the design more abstract, it can be challenging to implement composite patterns if the business rules of objects are complex, and not all methods are associated with leaf object subclasses; 2. There may be problems when adding new artifacts, and it is difficult to restrict the types of artifacts in the container.

Usage scenarios

1. You want to represent a partial-whole hierarchy of objects; 2. You want the user to ignore the difference between a composite object and a single object, and the user will use all objects in the composite structure uniformly. 3. Object structures are dynamic and vary in complexity, but customers need to deal with them consistently.