The composite pattern means including other objects within one object. These included objects may be terminal objects (no longer containing other objects), or they may be non-terminal objects (their interior still contains other objects, or they are called group objects). We call the objects nodes, that is, a root node contains many child nodes; some of these child nodes no longer contain child nodes, while others still contain child nodes, and so on. Clearly, this is a tree structure. The terminal nodes are called leaf nodes, the non-terminal nodes (group nodes) are called branch nodes, and the first node is called the root node. It is also similar to the structural form of a file directory: files can be called terminal nodes, and directories can be called non-terminal nodes (group nodes).


Ordinary Implementation

1. Let's first look at an ordinary implementation of a directory structure:

Directory node: Noder

import java.util.ArrayList; import java.util.List; /** * Directory node * Contains: * 1. Directory name * 2. List of lower-level files * 3. List of lower-level directories * 4. Method to add a file * 5. Method to add a directory * 6. Method to display lower-level content*/ public class Noder { String nodeName;//Directory name //Name the directory through the constructor public Noder(String nodeName){ this.nodeName = nodeName; } List<Noder> nodeList = new ArrayList<Noder>();//The list of lower-level directories of the directory List<Filer> fileList = new ArrayList<Filer>();//The list of lower-level files of the directory //Add a lower-level directory public void addNoder(Noder noder){ nodeList.add(noder); } //Add a file public void addFiler(Filer filer){ fileList.add(filer); } //Display lower-level directories and files public void display(){ for(Noder noder:nodeList){ System.out.println(noder.nodeName); noder.display();//Recursively display the directory list } for(Filer filer:fileList){ filer.display(); } } }

File node: Filer

/** * File node * The file node is a terminal node and has no lower-level nodes * Contains: * 1. File name * 2. File display method*/ public class Filer { String fileName;//File name public Filer(String fileName){ this.fileName = fileName; } //File display method public void display(){ System.out.println(fileName); } }

Test class: Clienter

import java.io.File; public class Clienter { public static void createTree(Noder node){ File file = new File(node.nodeName); File[] f = file.listFiles(); for(File fi : f){ if(fi.isFile()){ Filer filer = new Filer(fi.getAbsolutePath()); node.addFiler(filer); } if(fi.isDirectory()){ Noder noder = new Noder(fi.getAbsolutePath()); node.addNoder(noder); createTree(noder);//Use recursion to generate the tree structure } } } public static void main(String[] args) { Noder noder = new Noder("E://ceshi"); createTree(noder);//Create a directory tree structure noder.display();//Display directories and files } }

Running result:

E:\ceshi\目录1
E:\ceshi\目录1\目录3
E:\ceshi\目录1\文件2.txt
E:\ceshi\目录2
E:\ceshi\目录2\文件3.txt
E:\ceshi\文件1.txt

2. Composite Pattern

From the above code, it can be seen that we defined the file node object and the directory node object separately. This is because the operations between files and directories are different: files have no lower-level nodes, while directories can have lower-level nodes. But can we think of it this way: since both files and directories can exist as lower-level nodes of a node, can we abstract both into one type of object? Although the operations of the two are different, we can define them concretely in the method implementations of the implementation classes. For example, a file has no method for adding a lower-level node, so we can throw an exception in this method of the file without implementing it concretely, while in the directory we concretely implement the add operation. Both have the display operation, and they can implement it separately. Moreover, since we abstract files and directories into one type, combined with polymorphism, we can implement it as follows:

Abstract class: Node

/** * Treat files and directories uniformly as one type of node, create an abstract class to define this node, and then use its implementation classes to distinguish files from directories, defining their respective concrete implementation contents in the implementation classes.*/ public abstract class Node { protected String name;//Name //Assign name via constructor public Node(String name){ this.name = name; } //Add node: the file node does not have this method, the directory node overrides this method public void addNode(Node node) throws Exception{ throw new Exception("Invalid exception"); } //Display node: both file and directory implement this method abstract void display(); }

File implementation class: Filter

/** * Implement file node*/ public class Filer extends Node { //Name the file node through the constructor public Filer(String name) { super(name); } //Display file node @Override public void display() { System.out.println(name); } }

Directory implementation class: Noder

import java.util.*; /** * Implement directory node*/ public class Noder extends Node { List<Node> nodeList = new ArrayList<Node>();//Internal node list (including files and subdirectories) //Assign a name to the current directory node through the constructor public Noder(String name) { super(name); } //Add node public void addNode(Node node) throws Exception{ nodeList.add(node); } //Recursively loop to display lower-level nodes @Override void display() { System.out.println(name); for(Node node:nodeList){ node.display(); } } }

Test class: Clienter

import java.io.File; public class Clienter { public static void createTree(Node node) throws Exception{ File file = new File(node.name); File[] f = file.listFiles(); for(File fi : f){ if(fi.isFile()){ Filer filer = new Filer(fi.getAbsolutePath()); node.addNode(filer); } if(fi.isDirectory()){ Noder noder = new Noder(fi.getAbsolutePath()); node.addNode(noder); createTree(noder);//Use recursion to generate the tree structure } } } public static void main(String[] args) { Node noder = new Noder("E://ceshi"); try { createTree(noder); } catch (Exception e) { e.printStackTrace(); } noder.display(); } }

Execution output result:

E://ceshi
E:\ceshi\文件1.txt
E:\ceshi\目录1
E:\ceshi\目录1\文件2.txt
E:\ceshi\目录1\目录3
E:\ceshi\目录2
E:\ceshi\目录2\文件3.txt

From the above implementation, it can be seen that the so-called composite pattern actually refers to the problem of objects containing objects. It is laid out through composition (referencing objects inside objects). I think this composition is different from inheritance. Another meaning refers to the abstraction of child nodes in a tree structure (abstracting leaf nodes and branch nodes as child nodes), which is different from the ordinary way of separately defining leaf nodes and branch nodes.


3. Composite Pattern Application Scenarios

This composite pattern was born precisely for tree structures, so the usage scenario of the composite pattern is wherever tree structures appear. For example, operations on tree-structured data such as file directory display, multi-level directory presentation, and so on.

Original address: https://www.cnblogs.com/V1haoge/p/6489827.html