introduce
Composite combines objects into a tree structure to represent a "part-total" hierarchy. The combination mode makes users consistent with the use of individual objects and combined objects.
Common scenarios include the control mechanism in asp.net (that is, the control can contain child control, which can recursively operate, add, and delete child control), and similarly, there is a DOM mechanism. A DOM node can contain child nodes. Whether it is a parent node or a child node, it has the common functions of adding, deleting, and traversing child nodes. Therefore, the key to the combination pattern is to have an abstract class, which can represent both child elements and parent elements.
text
For example, there is a restaurant that provides a variety of dishes. Each table has a menu. The menu lists the dishes that the restaurant is all about, including breakfast pastries, lunch, dinner, etc. Each meal has a variety of menu items. Assuming that both the menu items and the entire menu should be printed, and sub-items can be added, such as new dishes can be added for lunch, and sugar can be added for menu items such as coffee.
In this case, we can use the combination to represent these contents as hierarchies. Let’s break down our implementation steps one by one.
The first step is to implement our "abstract class" function MenuComponent:
The code copy is as follows:
var MenuComponent = function () {
};
MenuComponent.prototype.getName = function () {
throw new Error("This method must be rewritten!");
};
MenuComponent.prototype.getDescription = function () {
throw new Error("This method must be rewritten!");
};
MenuComponent.prototype.getPrice = function () {
throw new Error("This method must be rewritten!");
};
MenuComponent.prototype.isVegetarian = function () {
throw new Error("This method must be rewritten!");
};
MenuComponent.prototype.print = function () {
throw new Error("This method must be rewritten!");
};
MenuComponent.prototype.add = function () {
throw new Error("This method must be rewritten!");
};
MenuComponent.prototype.remove = function () {
throw new Error("This method must be rewritten!");
};
MenuComponent.prototype.getChild = function () {
throw new Error("This method must be rewritten!");
};
This function provides two types of methods, one is to obtain information, such as price, name, etc., and the other is a general operation method, such as printing, adding, deleting, and obtaining submenu.
The second step is to create basic dishes:
The code copy is as follows:
var MenuItem = function (sName, sDescription, bVegetarian, nPrice) {
MenuComponent.apply(this);
this.sName = sName;
this.sDescription = sDescription;
this.bVegetarian = bVegetarian;
this.nPrice = nPrice;
};
MenuItem.prototype = new MenuComponent();
MenuItem.prototype.getName = function () {
return this.sName;
};
MenuItem.prototype.getDescription = function () {
return this.sDescription;
};
MenuItem.prototype.getPrice = function () {
return this.nPrice;
};
MenuItem.prototype.isVegetarian = function () {
return this.bVegetarian;
};
MenuItem.prototype.print = function () {
console.log(this.getName() + ": " + this.getDescription() + ", " + this.getPrice() + "euros");
};
As can be seen from the code, we only re-prototyped 4 methods for obtaining information and print methods, and did not overload the other 3 operating methods, because the basic dishes do not include ways to add, delete, and obtain sub-dishes.
Step 3: Create the dishes:
The code copy is as follows:
var Menu = function (sName, sDescription) {
MenuComponent.apply(this);
this.aMenuComponents = [];
this.sName = sName;
this.sDescription = sDescription;
this.createIterator = function () {
throw new Error("This method must be overwritten!");
};
};
Menu.prototype = new MenuComponent();
Menu.prototype.add = function (oMenuComponent) {
// Add substitute
this.aMenuComponents.push(oMenuComponent);
};
Menu.prototype.remove = function (oMenuComponent) {
// Delete substitutes
var aMenuItems = [];
var nMenuItem = 0;
var nLenMenuItems = this.aMenuComponents.length;
var oItem = null;
for (;nMenuItem < nLenMenuItems; ) {
oItem = this.aMenuComponents[nMenuItem];
if (oItem !== oMenuComponent) {
aMenuItems.push(oItem);
}
nMenuItem = nMenuItem + 1;
}
this.aMenuComponents = aMenuItems;
};
Menu.prototype.getChild = function (nIndex) {
//Get the specified substitute
return this.aMenuComponents[nIndex];
};
Menu.prototype.getName = function () {
return this.sName;
};
Menu.prototype.getDescription = function () {
return this.sDescription;
};
Menu.prototype.print = function () {
// Print current dishes and all sub-dishes
console.log(this.getName() + ": " + this.getDescription());
console.log("--------------------------------------------");
var nMenuComponent = 0;
var nLenMenuComponents = this.aMenuComponents.length;
var oMenuComponent = null;
for (;nMenuComponent < nLenMenuComponents; ) {
oMenuComponent = this.aMenuComponents[nMenuComponent];
oMenuComponent.print();
nMenuComponent = nMenuComponent + 1;
}
};
Note that in addition to implementing the methods of adding, deleting, and obtaining, the printing print method is to first print the current dish information, and then loop through the printing of all sub-dish information.
Step 4: Create the specified dish:
We can create several real dishes, such as dinner, coffee, pastries, etc. They all use Menu as their prototype, and the code is as follows:
The code copy is as follows:
var DinnerMenu = function () {
Menu.apply(this);
};
DinnerMenu.prototype = new Menu();
var CafeMenu = function () {
Menu.apply(this);
};
CafeMenu.prototype = new Menu();
var PancakeHouseMenu = function () {
Menu.apply(this);
};
PancakeHouseMenu.prototype = new Menu();
Step 5: Create the top menu container - Menu Book:
The code copy is as follows:
var Mattress = function (aMenus) {
this.aMenus = aMenus;
};
Mattress.prototype.printMenu = function () {
this.aMenus.print();
};
This function takes a menu array as a parameter, and the value provides the printMenu method for printing all menu contents.
Step 6, call method:
The code copy is as follows:
var oPanCakeHouseMenu = new Menu("Pancake House Menu", "Breakfast");
var oDinnerMenu = new Menu("Dinner Menu", "Lunch");
var oCoffeeMenu = new Menu("Cafe Menu", "Dinner");
var oAllMenus = new Menu("ALL MENUS", "All menus combined");
oAllMenus.add(oPanCakeHouseMenu);
oAllMenus.add(oDinnerMenu);
oDinnerMenu.add(new MenuItem("Pasta", "Spaghetti with Marinara Sauce, and a slice of sourdough bread", true, 3.89));
oDinnerMenu.add(oCoffeeMenu);
oCoffeeMenu.add(new MenuItem("Express", "Coffee from machine", false, 0.99));
var oMattress = new Mattress(oAllMenus);
console.log("---------------------------------------------");
oMattress.printMenu();
console.log("---------------------------------------------");
Do students who are familiar with asp.net control development look familiar?
Summarize
The usage scenario of the combination mode is very clear:
When you want to represent the part of the object - the overall hierarchy;
You want the user to ignore the difference between a combined object and a single object, and the user will use all objects in the combined structure uniformly (methods)
In addition, this pattern is often used with decorators. They usually have a common parent class (that is, prototype), so the decoration must support component interfaces with add, remove, and getChild operations.