In real life, there are many “parts-whole” relationships, such as departments and colleges in universities, departments and branches in head offices, books and bags in school supplies, clothes and wardrobe in daily necessities, and POTS and pans in the kitchen. The same is true in software development, for example, files and folders in the file system, simple controls and container controls in Windows programs, and so on. The processing of these simple and composite objects is very convenient if implemented in a composite pattern.

The definition and characteristics of combination pattern

Definition of Composite Pattern: sometimes called whole-whole Pattern, it is a Pattern that combines objects into tree-like hierarchical structure, which is used to represent the relationship of “whole-part” and enables users to have consistent access to single objects and combined objects. It belongs to structural design Pattern.

The combination mode is generally used to describe the relationship between whole and part, and it organizes objects into a tree structure. The top node is called the root node. The root node can contain branch nodes and leaf nodes, and the branch node can also contain branch nodes and leaf nodes.

As can be seen from the figure above, root node and branch node are essentially the same data type and can be used as containers. Leaf nodes and branch nodes do not belong to the same semantic type. In the composite pattern, however, the branch and leaf nodes are treated as belonging to the same data type (defined with a unified interface), allowing them to behave consistently.

In this way, in the combined mode, objects in the whole tree structure belong to the same type, which brings the advantage that users do not need to distinguish whether it is a branch node or a leaf node, and can directly operate, which brings great convenience to users.

The main advantages of the combined mode are:

  1. The composite pattern simplifies client code by allowing it to work consistently with both single and composite objects without caring whether it is working with a single object or a composite object.
  2. It is easier to add new objects into the composition body, and the client will not change the source code because of adding new objects, which meets the “open and close principle”.

Its main disadvantages are:

  1. The design is more complex, and the client needs to spend more time sorting out the hierarchical relationships between classes.
  2. It is not easy to restrict components in containers;
  3. It is not easy to add new functions to components by inheritance.

The structure and realization of combination pattern

The structure of the composite pattern is not very complex, and its structure and implementation are analyzed below.

1. Pattern structure

The composite pattern contains the following primary roles.

  1. Abstract Component role: Its main role is to declare common interfaces for leaf and branch components and implement their default behavior. Abstract artifacts also declare interfaces to access and manage subclasses in a transparent composition pattern; Interfaces that access and manage subclasses are not declared in the secure composite mode; management is done by branch components. (General abstract class or interface, define some common methods, such as add, delete)
  2. Leaf role: A Leaf node object in a composite that has no child nodes and is used to inherit or implement abstract artifacts.
  3. Composite role/intermediary: is a branch node object in a Composite that has child nodes for inheriting and implementing abstract artifacts. Its main function is to store and manage child widgets, usually including methods like Add(), Remove(), GetChild(), and so on.

The combination mode is divided into transparent combination mode and safe combination mode.

Transparent way

In this way, because the abstract component declares all methods in all subclasses, the client does not need to distinguish between leaf and branch objects and is transparent to the client. The downside, however, is that implementing the Add(), Remove(), and GetChild() methods of the leaf component without them (empty implementation or throwing exceptions) poses some security issues. Its structure is shown in Figure 1.

Safe way

In this way, the management method of child components will be moved to the branch component, component abstract component and leaves no child object management method, a way on this avoids the security issues, but as a result of leaf and branch has a different interface, the client should know when calling the existence of the leaves and branches object, so lost its transparency. Its structure is shown in Figure 2.

 

2. Realization of patterns

If you want to access elements in the set C0 ={leaf1,{leaf2,leaf3}}, the corresponding tree diagram is shown in Figure 3.

Transparent composition mode

The following is the implementation code of the transparent composite pattern.

public class CompositePattern { public static void main(String[] args) { Component c0 = new Composite(); Component c1 = new Composite(); Component leaf1 = new Leaf("1"); Component leaf2 = new Leaf("2"); Component leaf3 = new Leaf("3"); c0.add(leaf1); c0.add(c1); c1.add(leaf2); c1.add(leaf3); c0.operation(); } // Interface Component {public void add(Component c); public void remove(Component c); public Component getChild(int i); public void operation(); } class Leaf implements Component {private String name; public Leaf(String name) { this.name = name; } public void add(Component c) { } public void remove(Component c) { } public Component getChild(int i) { return null; } public void operation() {system.out.println (" "+ name +" ":" "); ); {private ArrayList<Component> children = new ArrayList<Component>(); // Implements Component {private ArrayList<Component> children = new ArrayList<Component>(); public void add(Component c) { children.add(c); } public void remove(Component c) { children.remove(c); } public Component getChild(int i) { return children.get(i); } public void operation() { for (Object obj : children) { ((Component) obj).operation(); }}}Copy the code

The running results of the program are as follows:

Leaf 1: Be visited! Leaf 2: Be visited! Leaf 3: Be visited!Copy the code

Security combination mode

The code for the secure composite pattern is similar to that for the transparent composite pattern, with simple modifications, as shown below.

First modify the Component code to preserve only the hierarchical public behavior.

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

Then modify the client code to change the branch component type to Composite to get a method for managing subclass operations.

public class CompositePattern { public static void main(String[] args) { Composite c0 = new Composite(); Composite c1 = new Composite(); Component leaf1 = new Leaf("1"); Component leaf2 = new Leaf("2"); Component leaf3 = new Leaf("3"); c0.add(leaf1); c0.add(c1); c1.add(leaf2); c1.add(leaf3); c0.operation(); }}Copy the code

An application example of the composite pattern

[Example 1] Realize the function of displaying the information of the selected goods and calculating the total price of the selected goods after the user shops in the combination mode.

Description: If Mr. Li goes shopping in shaoguan “Day Street E Corner” daily necessities store, he uses a small red bag to pack 2 packs of Wuyuan specialties (7.9 yuan per unit) and 1 map of Wuyuan (9.9 yuan per unit); Two bags of Shaoguan fragrant tea (68 yuan) and three bags of Shaoguan black tea (180 yuan) were packed in a small white bag. A small red bag in front and a Jingdezhen porcelain (380 yuan) were packed in a middle bag. With a big bag in front of the middle bag, a small white bag and a pair of Li Ning brand sports shoes (198 yuan).

Finally, the contents of the “big bag” are: {1 pair of Li Ning sneakers (RMB 198), white bag {2 bags of Shaoguan mushroom (RMB 68), 3 bags of Shaoguan black tea (RMB 180)}, medium bag {1 jingdezhen porcelain (RMB 380), red bag {2 bags of Wuyuan specialty (RMB 7.9), 1 map of Wuyuan (RMB 7.9) 9.9 yuan)}}}, now requires programming to display the information of all the goods Mr. Li put in the big bag and calculate the total price to pay.

This example can be designed according to the security combination mode, and its structure is shown in Figure 4.

 

The code is as follows:

package composite; import java.util.ArrayList; public class ShoppingTest { public static void main(String[] args) { float s = 0; Bags BigBag, mediumBag, smallRedBag, smallWhiteBag; Goods sp; BigBag = new bag; MediumBag = new bag (" middle bag "); SmallRedBag = new bag (" red bag "); SmallWhiteBag = new bag (" smallWhiteBag "); Sp = New Goods(" Wuyuan Specialty ", 2, 7.9f); smallRedBag.add(sp); Sp = New Goods(" Wuyuan Map ", 1, 9.9f); smallRedBag.add(sp); Sp = new Goods(" Shaoguan Mushroom ", 2, 68); smallWhiteBag.add(sp); Sp = new Goods(" Shaoguan Black Tea ", 3, 180); smallWhiteBag.add(sp); Sp = new Goods(" Jingdezhen Porcelain ", 1, 380); mediumBag.add(sp); mediumBag.add(smallRedBag); Sp = new Goods(" Li Ning brand sneakers ", 1, 198); BigBag.add(sp); BigBag.add(smallWhiteBag); BigBag.add(mediumBag); System.out.println(" "); BigBag.show(); s = BigBag.calculation(); System.out.println(" the total price to pay is: "+ s + "); Interface Articles {public float calculation(); Public void show(); } // implements Articles {private String name; // name private int quantity; Private float unitPrice; Public Goods(String name, int quantity, float unitPrice) {this.name = name; this.quantity = quantity; this.unitPrice = unitPrice; } public float calculation() { return quantity * unitPrice; } public void show() {system.out.println (name + "(quantity + ", unitPrice + ") "); }} // Class Bags implements Articles {private String name; // name private ArrayList<Articles> bags = new ArrayList<Articles>(); public Bags(String name) { this.name = name; } public void add(Articles c) { bags.add(c); } public void remove(Articles c) { bags.remove(c); } public Articles getChild(int i) { return bags.get(i); } public float calculation() { float s = 0; for (Object obj : bags) { s += ((Articles) obj).calculation(); } return s; } public void show() { for (Object obj : bags) { ((Articles) obj).show(); }}}Copy the code

The running results of the program are as follows:

The products you choose are: Li Ning sports shoes (Quantity: 1, unit price: 198.0 yuan) Shaoguan mushroom (quantity: 2, unit price: 68.0 yuan) Shaoguan black tea (quantity: 3, unit price: 180.0 Yuan) Jingdezhen porcelain (quantity: 1, unit price: 380.0 Yuan) Wuyuan specialty (quantity: 2, unit price: The total price of Wuyuan Map (Quantity: 1, unit price: 9.9 yuan) is 1279.7 yuanCopy the code

Application scenarios of the composite mode

The structure and characteristics of the composite pattern have been analyzed. The following application scenarios are analyzed.

  1. Where a hierarchy of whole and parts of an object needs to be represented.
  2. Where the differences between a composite object and a single object are required to be hidden from the user, and the user can use all objects in the composite structure with a unified interface.

Extensions to the composite pattern

If you abstract the leaf and branch nodes from the composition pattern introduced earlier, that is, leaf and branch nodes and child nodes, then the composition pattern expands into a complex composition pattern. For example, the simple JTextComponent in Java AWT/Swing has subclasses JTextField and JTextArea, and the Container component has subclasses Window and Panel. The structure diagram of the complex composite pattern is shown in Figure 5. \

View and ViewGroup in Android

The most classic use of composite mode in Android is the use of Views and Viewgroups. We all know that views and Textview and Button they all inherit from views. They are like leaves. And a ViewGroup is a container, and a ViewGroup can add a View, but a View can’t add a ViewGroup. This is the relationship between Leaf and Node in safe combination mode. So it’s an application of the safe combination model.

ViewGroup container implementation

Interface add operation subview method:

public abstract class ViewGroup extends View implements ViewParent, ViewManager {

First, a ViewGroup inherits from a View, and all viewgroups have the View’s public methods. Has the properties of view. The ViewGroup then inherits two interfaces. So let’s look at the ViewManager interface

public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}
Copy the code

Just like our previous Composite, we added add/remove pairs of subviews.

Watching ViewParent:

public interface ViewParent { public void requestLayout(); public void invalidateChild(View child, Rect r); public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset); public void requestChildFocus(View child, View focused); . }Copy the code

By omitting the ViewParent interface, we see many of the familiar methods of requesting relayouts, getting subview focus, and so on. So here are some actions to subviews as well.

ViewGroup is an abstract class, the entire ViewGroup code, ViewGroup is a View to reset the onLayout method to abstract method. Why is the View doing this? So we know that the View’s onLayout method is, the View calls onLayout after layout in the parent View, and onLayout is just an empty implementation because the View has been laid out. Onlayout has no other implementation meaning. The ViewGroup is a view container, and the ViewGroup calls layout after it finishes laying itself out. You also need layout subviews. So View resets onLayout to an abstract method that subclasses must implement.

 

Refer to the article

Combination mode (Detailed version)

Android Combination mode (View and ViewGroup)