I. Introduction of models
1.1 definition
Also known as part whole mode, it organizes a group of objects into a tree structure to represent a whole part hierarchy. Combination allows the client to unify the processing logic of single object and combined object. The client refers to the user of the code.
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. Under the root node can contain branch nodes and leaf nodes, and under the branch node can contain branch nodes and leaf nodes.
It can be seen from the figure that 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 in semantics. However, in the composite mode, the branch node and leaf node will be regarded as 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 the user does not need to distinguish whether it is a branch node or a leaf node, and can operate directly, which brings great convenience to the user.
1.2 advantages
- The combination mode enables the client code to deal with single objects and composite objects consistently, without caring whether they are dealing with single objects or composite objects, which simplifies the client code
- It is easier to add new objects into the assembly. The client will not change the source code because of adding new objects, which meets the opening and closing principle
1.3 disadvantages
- 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 use the method of inheritance to add new functions of components
2, Structure and Implementation
2.1 structure
- Abstract Component role: declare public interfaces for leaf and branch components and implement their default behavior. The general abstract class or interface defines some general methods, such as adding, deleting, updating, etc
- Leaf component role: it is a leaf node object in the composition. It has no child nodes and is used to inherit or implement abstract components
- Branch component role: 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 sub components, which usually includes methods such as add(), remove(), getChild(), etc
The combination mode is divided into transparent combination mode and safe combination mode
- In the transparent composition mode, the abstract construction also declares the interface to access and manage subclasses, and the management work is completed by the branch component
- In the safe composition mode, the abstract component role does not declare the interface to access and manage the subclass, and the management is completed by the branch component.
Transparent combination mode
Safe combination mode
2.2 realization
2.2.1 class diagram
2.2.2,MenuComponent
package com.erlang.composite; /** * @description: Menu components * @author: erlang * @since: 2022-02-15 21:45 */ public abstract class MenuComponent { public void add(MenuComponent menuComponent) { throw new UnsupportedOperationException(); } public void remove(MenuComponent menuComponent) { throw new UnsupportedOperationException(); } public MenuComponent getChild(int index) { throw new UnsupportedOperationException(); } public String getName() { throw new UnsupportedOperationException(); } public String getDescription() { throw new UnsupportedOperationException(); } public double getPrice() { throw new UnsupportedOperationException(); } public boolean isVegetarian() { throw new UnsupportedOperationException(); } public void print() { throw new UnsupportedOperationException(); } }
2.2.3,MenuItem
package com.erlang.composite; /** * @description: Menu element * @author: erlang * @since: 2022-02-15 21:45 */ public class MenuItem extends MenuComponent { String name; String description; boolean vegetarian; double price; public MenuItem(String name, String description, boolean vegetarian, double price) { this.name = name; this.description = description; this.vegetarian = vegetarian; this.price = price; } @Override public String getName() { return name; } @Override public String getDescription() { return description; } @Override public double getPrice() { return price; } @Override public boolean isVegetarian() { return vegetarian; } @Override public void print() { System.out.print(" " + getName()); // Is it vegetarian if (isVegetarian()) { System.out.print("(Vegetarian)"); } System.out.println(", " + getPrice()); System.out.println(" -- " + getDescription()); } }
2.2.4,Menu
package com.erlang.composite; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * @description: menu * @author: erlang * @since: 2022-02-15 21:46 */ public class Menu extends MenuComponent { private List<MenuComponent> children = new ArrayList<>(); private String name; private String description; public Menu(String name, String description) { this.name = name; this.description = description; } @Override public void add(MenuComponent menuComponent) { children.add(menuComponent); } @Override public void remove(MenuComponent menuComponent) { children.remove(menuComponent); } @Override public MenuComponent getChild(int index) { return children.get(index); } @Override public String getName() { return name; } @Override public String getDescription() { return description; } @Override public void print() { System.out.print(" " + getName()); System.out.println(", " + getDescription()); System.out.println("-----------------------------"); Iterator<MenuComponent> iterator = children.iterator(); while (iterator.hasNext()) { iterator.next().print(); } } }
2.2.5,Waitress
package com.erlang.composite; /** * @description: waiter * @author: erlang * @since: 2022-02-15 21:45 */ public class Waitress { MenuComponent menus; public Waitress(MenuComponent menus) { this.menus = menus; } public void printMenu() { menus.print(); } }
2.2.6,MenuTestDrive
package com.erlang.composite; /** * @description: * @author: erlang * @since: 2022-02-15 22:04 */ public class MenuTestDrive { public static void main(String[] args) { MenuComponent pancakeHouseMenu = new Menu("PANCAKE HOUSE", "Breakfast"); MenuComponent dinnerMenu = new Menu("DINNER MENU", "Lunch"); MenuComponent cafeMenu = new Menu("CAFE MENU", "Dinner"); MenuComponent dessertMenu = new Menu("DESSERT MENU", "Dessert of course"); MenuComponent menus = new Menu("ALl MENUS", "All menus combined"); menus.add(pancakeHouseMenu); menus.add(dinnerMenu); menus.add(cafeMenu); dinnerMenu.add(new MenuItem("pasta", "This is spaghetti", true, 1.1)); dinnerMenu.add(dessertMenu); dessertMenu.add(new MenuItem("Pizza", "This is pizza", true, 1.2)); Waitress waitress = new Waitress(menus); waitress.printMenu(); } }
2.2.7 implementation results
ALl MENUS, All menus combined ----------------------------- PANCAKE HOUSE, Breakfast ----------------------------- DINNER MENU, Lunch ----------------------------- Spaghetti (Vegetarian), 1.1 -- This is spaghetti DESSERT MENU, Dessert of course ----------------------------- Pizza (Vegetarian), 1.2 -- This is pizza CAFE MENU, Dinner -----------------------------