introduce
Composite Pattern, also known as Part Whole Pattern, belongs to structural design Pattern. The Composite Pattern is relatively simple. It treats a group of similar objects as one object and combines objects according to a tree structure. It then provides a unified way to access the corresponding objects, ignoring the differences between objects and collections of objects.
define
Objects are grouped into a tree structure to represent a partial-whole hierarchy so that users can consistently use whole objects and composite objects.
Usage scenarios
- Represents a partial-whole hierarchy of objects.
- A scenario in which some modules or functions can be separated from a whole.
UML class diagrams
- Component: Abstracts the root node, declaring interfaces for objects in the composition. Where appropriate, implement the default behavior of interfaces common to all classes. Declare an interface to access and manage Component child nodes. You can define an interface in a recursive structure to access a parent node and implement it as appropriate.
- Composite: Defines the behavior of branch nodes that have child nodes, stores child nodes, and implements child node related operations in the Component interface.
- Leaf: Represents a Leaf node object in a combination. A Leaf node has no child nodes and defines the behavior of the node object in the combination.
- Client: Objects that manipulate composite nodes through the Component interface.
Code sample
A simple example
Abstract root node:
public abstract class Component {
/** * Node name */
protected String name;
public Component(String name) {
this.name = name;
}
/** * The specific logical methods are implemented by subclasses */
public abstract void doSomething(a);
/** * Add child node *@param component
*/
public abstract void addChild(Component component);
/** * Remove child node *@param component
*/
public abstract void removeChild(Component component);
/** * get the child node *@param index
* @return* /
public abstract Component getChildren(int index);
}
Copy the code
Branch node:
public class Composite extends Component {
/** * Storage node container */
private List<Component> mLists = new ArrayList<>();
public Composite(String name) {
super(name);
}
@Override
public void doSomething(a) {
System.out.println("name: " + name);
if(mLists ! =null && mLists.size() > 0) {
for(Component component : mLists) { component.doSomething(); }}}@Override
public void addChild(Component component) {
mLists.add(component);
}
@Override
public void removeChild(Component component) {
mLists.remove(component);
}
@Override
public Component getChildren(int index) {
returnmLists.get(index); }}Copy the code
Leaf node:
public class Leaf extends Component {
public Leaf(String name) {
super(name);
}
@Override
public void doSomething(a) {
System.out.println("name: " + name);
}
@Override
public void addChild(Component component) {
throw new UnsupportedOperationException("Leaf node has no children");
}
@Override
public void removeChild(Component component) {
throw new UnsupportedOperationException("Leaf node has no children");
}
@Override
public Component getChildren(int index) {
throw new UnsupportedOperationException("Leaf node has no children"); }}Copy the code
test:
@Test
public void testComponent(a) {
// Construct a root node
Component root = new Composite("Root");
// Construct branch nodes
Component branchA = new Composite("branchA");
Component branchB = new Composite("branchB");
// Construct the leaf node
Component leafA = new Leaf("leafA");
Component leafB = new Leaf("LeafB");
// Add the leaf node to the branch node
branchA.addChild(leafA);
branchB.addChild(leafB);
// Add the stem node to the heel node
root.addChild(branchA);
root.addChild(branchB);
root.doSomething();
}
Copy the code
output:
name: Root
name: branchA
name: leafA
name: branchB
name: LeafB
Copy the code
In actual combat
Business background: use code to represent the combination of folders and files;
File and folder abstract classes:
public abstract class Dir {
/** * declare a List member variable to store all the elements under the folder */
protected List<Dir> mDirs = new ArrayList<>();
/** * Current file or folder name */
protected String name;
public Dir(String name) {
this.name = name;
}
/** * Add a file or folder *@param dir
*/
public abstract void addDir(Dir dir);
/** * Remove a file or folder *@param dir
*/
public abstract void removeDir(Dir dir);
/** * Clear all the elements under the folder */
public abstract void clear(a);
/** * outputs the directory structure under the folder */
public abstract void print(a);
/** * get all files or subfolders in the folder */
public abstract List<Dir> getFiles(a);
/** * Get the file or folder name *@return* /
public String getName(a){
returnname; }}Copy the code
Specific folder class:
public class Folder extends Dir {
public Folder(String name) {
super(name);
}
@Override
public void addDir(Dir dir) {
mDirs.add(dir);
}
@Override
public void removeDir(Dir dir) {
mDirs.remove(dir);
}
@Override
public void clear(a) {
mDirs.clear();
}
@Override
public void print(a) {
System.out.print(getName() + "(");
Iterator<Dir> iterator = mDirs.iterator();
while (iterator.hasNext()) {
Dir dir = iterator.next();
dir.print();
if (iterator.hasNext()) {
System.out.print(",");
}
}
System.out.print(")");
}
@Override
public List<Dir> getFiles(a) {
returnmDirs; }}Copy the code
Specific file classes:
public class File extends Dir {
public File(String name) {
super(name);
}
@Override
public void addDir(Dir dir) {
throw new UnsupportedOperationException("File object does not support this operation");
}
@Override
public void removeDir(Dir dir) {
throw new UnsupportedOperationException("File object does not support this operation");
}
@Override
public void clear(a) {
throw new UnsupportedOperationException("File object does not support this operation");
}
@Override
public void print(a) {
System.out.println("name: "+name);
}
@Override
public List<Dir> getFiles(a) {
throw new UnsupportedOperationException("File object does not support this operation"); }}Copy the code
test:
@Test
public void testComponent2(a){
// Construct a directory object representing the sdcard disk and directory
Dir diskSdcard = new Folder("sdcard");
// there is a file under sdcard
diskSdcard.addDir(new File("bug.log"));
// There is also a subdirectory android under sdcard
Dir android = new Folder("android");
// There is a directory in the android directory
Dir data = new Folder("data");
data.addDir(new File("2019-09-14.log"));
android.addDir(data);
diskSdcard.addDir(android);
// There is a subdirectory baidu under sdcard
Dir baidu = new Folder("baidu");
// There is a directory in the android directory
Dir cache = new Folder("cache");
cache.addDir(new File("offline.baidu"));
baidu.addDir(cache);
diskSdcard.addDir(baidu);
// There is also a subdirectory called downloads under sdcard
Dir downloads = new Folder("downloads");
// There is a directory in the android directory
Dir f360 = new Folder("360");
f360.addDir(new File("360.log"));
downloads.addDir(f360);
diskSdcard.addDir(downloads);
// Prints the file structure
diskSdcard.print();
}
Copy the code
output:
sdcard (
/ / file
name: bug.log,
/ / folder
android (data (name: 2019-09-14.log)),
baidu (cache (name: offline.baidu)),
downloads (360 (name: 360.log))
)
Copy the code
Here we use parentheses as the content range of a folder. As shown above, the folder under Adcard has three sub-folders, Android, Baidu, downloads, and a bug.log file, and the three sub-folders also contain sub-folders and files respectively. A typical tree-like nested decoupling is a composite pattern.
conclusion
Advantages:
- The composite pattern can clearly define the hierarchical complex object, representing all or part of the object’s hierarchy, it allows the high-level module to ignore the differences of the hierarchy, convenient control of the entire hierarchy.
- High-level modules can consistently use a composite structure or a single object within it, regardless of whether they are dealing with a single object or the entire composite structure, simplifying the code for high-level modules.
- It is convenient to add new branches and leaves to the composite pattern without any modifications to the existing libraries, following the “open closed principle”.
- The combination pattern provides a flexible solution for the object-oriented implementation of tree structure. Through the recursive combination of leaf object and trunk object, complex tree structure can be formed, but the control of tree structure is very simple.
Disadvantages:
You can’t rely on the type system to impose these constraints because in most cases, they come from the abstraction layer you want to hear, and you have to do type checking to implement them, which can be complicated.
Article code address
Special thanks to
Android source code design pattern analysis and combat
Thank you for reading, thank you!