This paper mainly introduces the principle and usage of composite pattern, as well as the transparent and secure implementation of composite pattern.

Model background

An example is given in the book: we take an antivirus on a given folder. Folders and subfolders need to be searched recursively, and they need to be searched differently depending on whether the file is an image or text.

This is a common business scenario. This is equivalent to recursively traversing a tree structure, but there are several types of nodes in the tree. We naturally create classes for each type of node, but the problem is the folder class that contains subfolders and files (i.e., non-leaf nodes). For such nodes, we need to differentiate between the file types below ourselves and adopt different antivirus strategies.

This leads to a problem: 1. The processing logic of non-leaf nodes is very complex, and we need to distinguish each file type before calling kill. 2. Once a new file type is added, you need to modify the folder class code.

So we hope to make the system antivirus operation, no matter what files and folders, they all use the same set of external interface to operate, folder code can simplify the operation of the same set of standard interface.

Definitions & Concepts

The composite pattern abstracts a single object and a composite object (leaf node and non-leaf node) to make its use consistent. The composite pattern is also called “whole-part” pattern. Is an object structured pattern. Useful for groups of tree-like object structures. Composite pattern is to use object-oriented thought to realize the construction and processing of tree structure.

The principle of

As mentioned earlier, the main problem is that the folder class needs to distinguish between files and call the kill method. Now you want to have a unified invocation interface. That’s simple: abstract! We abstracted the public part from the text file, image file, or folder. And then operations are done through this abstraction. Any new file types that are added later need only be added with a new abstract implementation.

Therefore, it can be understood as: the main core idea of the composite pattern is to abstract the nodes in the tree structure into an abstract construction class.

A necessary condition for

  • Component (Abstract node class)
    • This is the key to the composite pattern, which abstracts both leaf and non-leaf nodes in the tree into a parent (either an interface or an abstract class). This abstraction contains both leaf and non-leaf properties and methods. Can broadly represent all nodes in the tree system. (The combination of patterns depends on whether the structure is abstracted out of such a common abstraction)
  • Leaf node
    • Is an implementation of the components in the tree that do not contain child nodes.
  • Composite (Container node)
    • The non-leaf node section contains a List of its children.

Because the types of nodes in the system are different, they generally have their own methods. For example, the folder node has add and remove methods, but the leaf node does not. We have some control over the level of Component abstraction, which we divide into transparent mode and secure mode:

  • Transparent mode
    • The abstract build class defines all the methods. It’s the combination we’ve been talking about.
    • It is not safe because if you call the add and remove methods of the leaf node at run time (these methods themselves have no meaning to the leaf) you may get an error.
  • Safe mode
    • Only generic methods are defined in the abstract build class. Leaves or intermediate nodes each need their own method, which they implement themselves.

Of course, we can also provide a default implementation of a method in an abstract build class that needs to be overridden with that method, which is also opaque. Both serve to reduce writing useless code, but do not provide strong constraints.

UML

Transparent composition mode

Security combination mode

implementation

Transparent composition mode

The abstraction layer has all the methods that maybe leaf nodes shouldn’t have. Such as the add and remove child nodes.

abstract

public interface Component {
    void add(Component c);
    void remove(Component c);
    List<Component> getChild(a);
    void operation(a);
}
Copy the code

leaves

class Leaf implements Component {
    private String name;
    public Leaf(String name) {
        this.name = name;
    }
    @Override
    public void add(Component c) {
        System.out.println("can't add");
    }
    @Override
    public void remove(Component c) {
        System.out.println("can't remove");
    }
    @Override
    public List<Component> getChild(a) {
        return null;
    }
    @Override
    public void operation(a) {
        System.out.println("Leaf node:"+ name); }}Copy the code

Not a leaf

class Composite implements Component {
    private List<Component> componentList = new ArrayList<>();
    private String name;
    public Composite(String name) {
        this.name = name;
    }
    @Override
    public void add(Component c) {
        componentList.add(c);
    }
    @Override
    public void remove(Component c) {
        componentList.remove(c);
    }
    @Override
    public List<Component> getChild(a) {
        return componentList;
    }
    @Override
    public void operation(a) {
        System.out.println("Non-leaf node:"+ name); componentList.forEach(Component::operation); }}Copy the code

use

Component c1, c2, c3, f1, f2, f3;
c1 = new Leaf("1");
c2 = new Leaf("2");
c3 = new Leaf("3");
f1 = new Composite("1");
f2 = new Composite("2");
f3 = new Composite("3");
f1.add(c1);
f1.add(c2);
f2.add(f1);
f3.add(c3);
f1.add(f3);
f1.operation();
Copy the code

advantages

  • Simple, ensure that the leaf node and non-leaf node have external consistency, in the recursive processing of the tree node can be treated equally.

disadvantages

  • Unsafe, leaf features (no child nodes can be added) can be broken, problems can occur if called incorrectly, no code constraints are guaranteed.
  • Secondly, every time the leaf node implements abstraction, we need to implement all the methods, add and remove, which are not necessary to implement, which increases the coding complexity.

Security combination mode

The safe composition pattern is a way for the abstraction layer to strip off unwanted leaf nodes and let the Composite implement itself.

To achieve 1

We can use the default adapter of the adapter pattern to accommodate an abstraction that implements the unwanted default methods of the leaf. Then let the leaf node implement the class.

public abstract class AbstractComponent implements Component {
    @Override
    public void add(Component c) {
        System.out.println("no support");
    }
    @Override
    public void remove(Component c) {
        System.out.println("no support");
    }
    @Override
    public List<Component> getChild(a) {
        System.out.println("no support");
        return null;
    }
    @Override
    public abstract void operation(a);
}
Copy the code

The 2

Abstract builds define only public methods; they define the rest themselves.

public interface ComponentSafe {
    void operation(a);
}
Copy the code

leaves

class LeafSafe implements ComponentSafe {
    private String name;

    public LeafSafe(String name) {
        this.name = name;
    }

    @Override
    public void operation(a) {
        System.out.println("Leaf:"+ name); }}Copy the code

Not a leaf

class CompositeSafe implements ComponentSafe {
    private String name;
    private List<ComponentSafe> componentSafeList = new ArrayList<>();

    public CompositeSafe(String name) {
        this.name = name;
    }

    @Override
    public void operation(a) {
        System.out.println("Non-leaf node" + name);
        componentSafeList.forEach(ComponentSafe::operation);
    }

    public void add(ComponentSafe c) {
        componentSafeList.add(c);
    }

    public void remove(ComponentSafe c) {
        componentSafeList.remove(c);
    }

    public List<ComponentSafe> getChild(a) {
        returncomponentSafeList; }}Copy the code

use

ComponentSafe c1, c2, c3;
c1 = new LeafSafe("1");
c2 = new LeafSafe("2");
c3 = new LeafSafe("3");
CompositeSafe f1, f2, f3;
f1 = new CompositeSafe("1");
f2 = new CompositeSafe("2");
f3 = new CompositeSafe("3");
//build
f1.add(c1);
f1.add(c2);
f1.add(c3);
f2.add(f3);
f1.add(f2);
f1.operation();
Copy the code

advantages

  • Safe. The leaf node doesn’t have to worry about methods that don’t belong to it. There are strong constraints that ensure that the leaf node decides not to call the Add Remove method.

disadvantages

  • But opaque, building leaves and building containers requires a different approach to building. We can’t program with abstraction alone.

Usage scenarios

This applies to hierarchies with whole and parts, where whole and parts refer to classes in the system whose general functions are the same, with small parts differing. If we want to ignore the differences between whole and part in a way that allows clients to program directly toward abstraction and treat them with uniform standards.

There are tree structure of use scenarios, such as tree menu, file, folder management.

conclusion

In general, the core of the composite pattern is the system of similar classes to do abstract! It is convenient for the client to program directly to the abstract. Choose the most appropriate combination of transparency and security scenarios.

The attached

Related code: github.com/zhaohaoren/…

If there are code and article problems, also please correct! Thank you!