Combination mode of design mode

Posted by watts on Sat, 01 Jan 2022 08:44:14 +0100

In real life, there are many "part whole" relationships, such as departments and colleges in universities, departments and branches in head offices, books and schoolbags in school supplies, clothes and wardrobes in daily necessities, pots and pans in kitchens, etc. The same is true in software development, such as files and folders in the file system, simple controls and container controls in form programs, etc. The processing of these simple objects and composite objects will be very convenient if they are implemented in combination mode.

Definition and characteristics of combination mode

Definition of Composite Pattern: sometimes called part whole pattern, it is a pattern that combines objects into a tree hierarchy, which is used to represent the "whole part" relationship, so that users have consistent access to single objects and composite objects. It belongs to structural design pattern.

The combination mode is generally used to describe the relationship between the whole and the part. It organizes the objects into a tree structure. The top-level node is called the root node. The root node can contain branch nodes and leaf nodes, and the branch node and leaf nodes can be contained below the branch node. The tree structure is shown below.

As can be seen from the above figure, the 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 type semantically. However, in the composite mode, the branch node and leaf node will be regarded as belonging to the same data type (defined with a unified interface), so that they have consistent behavior.

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

The main advantages of the combination mode are:
The combination mode enables the client code to deal with single objects and combined objects consistently without caring whether they are dealing with single objects or combined objects, which simplifies the client code;
It is easier to add new objects to the assembly, and the client will not change the source code because of the addition of new objects, meeting the "opening and closing principle";

Its main disadvantages are:
The design is complex, and the client needs to spend more time to clarify the hierarchical relationship between classes;
It is not easy to limit the components in the container;
It is not easy to add new functions of components by inheritance;

Structure and implementation of composite pattern

The structure of composite pattern is not very complex. Its structure and implementation are analyzed below.

  1. Pattern structure
    The composite pattern contains the following main roles.
    Abstract Component role: its main role is to declare public interfaces for leaf components and branch components and implement their default behavior. In the transparent composition mode, the abstract Component also declares the interface to access and manage subclasses; In the safe composite mode, the interface for accessing and managing subclasses is not declared, and the management is completed by the branch Component. (general abstract classes or interfaces, defining some general methods, such as adding and deleting)
    Leaf role: it is a leaf node object in the composition. It has no child nodes and is used to inherit or implement abstract components.
    Composite role / intermediate component: it is a branch node object in the composition. It has child nodes, which are used to inherit and implement abstract components. Its main function is to store and manage subassemblies, usually including Add(), Remove(), GetChild(), etc.

The combination mode is divided into transparent combination mode and safe combination mode.
(1) Transparent way
In this way, because the abstract component declares all methods in all subclasses, the client does not need to distinguish between leaf objects and branch objects, which is transparent to the client. But its disadvantage is that the leaf component does not have Add(), Remove() and GetChild() methods, but needs to implement them (empty implementation or throwing exceptions), which will bring some security problems. Its structure is shown in Figure 1.

(2) Safety mode
In this way, the method of managing sub components is moved to the branch component, and the abstract component and leaf component have no management method for sub objects, which avoids the security problem of the previous method. However, because the leaf and branch have different interfaces, the client needs to know the existence of leaf object and branch object when calling, so it loses transparency. Its structure is shown in Figure 2.

  1. Implementation of pattern
    If you want to access the elements in the set c0={leaf1,{leaf2,leaf3}}, the corresponding tree view is shown in Figure 3.

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();
    }
}
//Abstract component
interface Component {
    public void add(Component c);
    public void remove(Component c);
    public Component getChild(int i);
    public void operation();
}
//Leaf component
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("leaf" + name + ": Be visited!");
    }
}
//Branch member
class Composite 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();
        }
    }
}

Application examples of composite mode

[example 1] the combination mode is used to realize the function of displaying the information of the selected goods and calculating the total price of the selected goods after the user purchases in the store.

Note: if Mr. Li goes to Shaoguan "Tianjie e Jiao" daily necessities store for shopping, he uses a small red bag to pack 2 packages of Wuyuan specialties (unit price 7.9 yuan) and 1 Wuyuan map (unit price 9.9 yuan); Two packages of Shaoguan fragrant tea (unit price 68 yuan) and three packages of Shaoguan black tea (unit price 180 yuan) were packed in a small white bag; A small red bag in front and a Jingdezhen porcelain (unit price 380 yuan) were packed in a medium bag; The front middle bag, small white bag and a pair of Li Ning Sports shoes (unit price 198 yuan) are packed in a large bag.

Finally, the contents of the "big bag" include: {1 pair of Li Ning brand sports shoes (unit price 198 yuan), small white bag {2 bags of Shaoguan mushroom (unit price 68 yuan), 3 bags of Shaoguan black tea (unit price 180 yuan)}, medium bag {1 Jingdezhen porcelain (unit price 380 yuan), small red bag {2 bags of Wuyuan specialty (unit price 7.9 yuan), 1 Wuyuan map (unit price 9.9 yuan)}}, Now it is required to program and display all the commodity information put by Mr. Li in the big bag and calculate the total price to be paid.

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

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 Bags("big bag");
        mediumBag = new Bags("Medium bag");
        smallRedBag = new Bags("Small red bag");
        smallWhiteBag = new Bags("Small white bag");
        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 sports shoes", 1, 198);
        BigBag.add(sp);
        BigBag.add(smallWhiteBag);
        BigBag.add(mediumBag);
        System.out.println("The items you choose are:");
        BigBag.show();
        s = BigBag.calculation();
        System.out.println("The total price to be paid is:" + s + "element");
    }
}
//Abstract components: Items
interface Articles {
    public float calculation(); //calculation
    public void show();
}
//Leaf components: Commodities
class Goods implements Articles {
    private String name;     //name
    private int quantity;    //quantity
    private float unitPrice; //Unit Price
    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 + "(number:" + quantity + ",Unit Price:" + unitPrice + "element)");
    }
}
//Branch components: bags
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();
        }
    }
}

Application scenario of combination mode

The structure and characteristics of the combination mode are analyzed above, and the following application scenarios are analyzed below.
When it is necessary to represent the hierarchy of the whole and part of an object.
It is required to hide the difference between composite objects and single objects. Users can use all objects in the composite structure with a unified interface.

Topics: Java Design Pattern