What is composite mode
Composite pattern refers to combining multiple objects to form a tree structure to represent a hierarchy with a "whole-part" relationship. The Composite pattern is used consistently for both individual objects and composite objects.
The combination mode includes the following roles:
Component (abstract component): It can be an interface or an abstract class, which declares an interface for a leaf component object or a container component object, and this role can include the declaration and implementation of common behaviors of all subclasses.
Leaf (leaf component): implements the method declared in the abstract component, and the object has no subordinate objects.
Composite (container component): This role contains subordinate objects. Subordinate objects can be either leaf components or container components, providing a collection for storing child nodes, and implementing methods defined in abstract components.
The key to the composite pattern is to define an abstract component class, which represents both the leaf component and the container component. The client programes the abstract components, and can handle leaf and container components uniformly.
When using the composition mode, the composition mode can be divided into transparent composition mode and safe composition mode according to the definition form of the abstract component class:
Transparent composition mode: Declare all methods of managing member objects in the abstract component class to ensure that all subclasses have the same methods. The disadvantage of the transparent composition mode is that it is not safe enough, because the leaf object and the container object are essentially different, and the leaf object will not have subordinate objects, so it is meaningless to implement the method in the abstract component.
Safe composition mode: No method for managing member objects will be declared in the abstract component class, but declared and implemented in the composite component, so that the leaf objects do not need to manage these methods. The disadvantage of the safe composite pattern is that it is not transparent enough, so the leaf component and the composite component have different methods, and the methods used to manage member objects in the container component are not declared in the abstract component.
Composition mode is to use object-oriented thinking to realize the construction and processing of tree structure, and describes how to recursively combine container objects and leaf objects, which is simple to implement and has good flexibility.
Advantages and disadvantages of composite mode
advantage
- Combination mode can clearly define hierarchical complex objects, express all or part of the object hierarchy, and the client can ignore the difference in hierarchy, and conveniently control the entire hierarchy.
- Composite structures or individual objects can be handled uniformly by the client.
- Adding container components or leaf components in the combination mode does not require code modification, which conforms to the principle of opening and closing.
- Combination mode provides a flexible solution for the object-oriented implementation of tree structure. Through the recursive combination of leaf objects and container objects, complex tree structures can be formed, but the control of tree structures is very simple.
shortcoming
- It is difficult to restrict the type of components in the container when adding new components. When using the composition mode, you cannot rely on the type to add constraints because they all have the same abstraction layer. In this case, it must be done at runtime. Type checking implementation, the implementation of this process is more complicated.
Application Scenarios of Composite Mode
- In hierarchies with "whole-part" one would like a way to ignore "whole-part" differences.
- Need to deal with a tree structure.
- Leaf objects and container objects can be separated, and the types are not fixed, and some new types need to be added.
Examples of Composite Patterns
// abstract component @Data public abstract class OrganizationComponent { private String name; private String des; protected void add(OrganizationComponent organizationComponent) { throw new UnsupportedOperationException(); } protected void remove(OrganizationComponent organizationComponent) { throw new UnsupportedOperationException(); } public OrganizationComponent(String name, String des) { super(); this.name = name; this.des = des; } protected abstract void print(); } // container component public class University extends OrganizationComponent { List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>(); public University(String name, String des) { super(name, des); } @Override protected void add(OrganizationComponent organizationComponent) { organizationComponents.add(organizationComponent); } @Override protected void remove(OrganizationComponent organizationComponent) { organizationComponents.remove(organizationComponent); } @Override public String getName() { return super.getName(); } @Override public String getDes() { return super.getDes(); } @Override protected void print() { System.out.println("--------------" + getName() + "--------------"); //Iterate over organizationComponents for (OrganizationComponent organizationComponent : organizationComponents) { organizationComponent.print(); } } } public class College extends OrganizationComponent { List<OrganizationComponent> organizationComponents = new ArrayList<>(); public College(String name, String des) { super(name, des); } @Override protected void add(OrganizationComponent organizationComponent) { // In actual business in the future, College add and University add may not be exactly the same organizationComponents.add(organizationComponent); } @Override protected void remove(OrganizationComponent organizationComponent) { organizationComponents.remove(organizationComponent); } @Override public String getName() { return super.getName(); } @Override public String getDes() { return super.getDes(); } @Override protected void print() { System.out.println("--------------" + getName() + "--------------"); //Iterate over organizationComponents for (OrganizationComponent organizationComponent : organizationComponents) { organizationComponent.print(); } } } // leaf component public class Department extends OrganizationComponent { public Department(String name, String des) { super(name, des); } @Override public String getName() { return super.getName(); } @Override public String getDes() { return super.getDes(); } @Override protected void print() { System.out.println(getName()); } }
Application of combination mode in source code
HashMap
// abstract component public abstract class AbstractMap<K,V> implements Map<K,V> { public V put(K key, V value) { throw new UnsupportedOperationException(); } public abstract Set<Entry<K,V>> entrySet(); ...... } public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable { public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; if ((p = tab[i = (n - 1) & hash]) == null) ...... } public Set<Map.Entry<K,V>> entrySet() { Set<Map.Entry<K,V>> es; return (es = entrySet) == null ? (entrySet = new EntrySet()) : es; } ...... }