Combination mode--processing the tree structure relationship between objects

Introduction to Combination Mode

 

The combination mode is mainly used to solve the parent-child relationship of the tree structure between objects. Typical application scenarios are: menu management on web pages (multi-level menu); and xml file parsing of parent-child structure (such as Dom4J). The composition pattern is generally used in conjunction with the "iterator pattern" to solve complex tree-structured object relationship problems.

 

The class diagram of the composition pattern is very simple, but it is a very powerful design pattern:



 

 

There are only three roles in this mode:

A. Component is an abstract interface (or an abstract class) that defines some public methods;

B. MenuItem specific menu item;

C. Menu The specific menu (or submenu) has a collection inside that contains multiple MenuItems (menu items) or sub-Menu (submenus).

 

As far as this class diagram is concerned, I can't see the power of this mode. The following is an explanation of an actual typical scenario.

 

Menu permission management

 

Today's large-scale e-commerce websites will have their own management back-end systems for the management of various data: such as users, commodities, rights management, etc. There will be many menu items in this management system, and some administrator role types will have different menu permissions for different role types. Leaving aside characters, let's take a look at a simplified menu list:



 

You can see that this menu list has three levels, in which the "red dotted box" represents the specific "menu item" (MenuItem), and the "black implementation box" represents the specific "submenu" (Menu).

 

Let's look at roles again. Now we require different roles to see different menu lists (each user is associated with a role). As an example, only two roles are designed here: super administrator and ordinary administrator. Super administrators have all the viewing permissions for the above menu lists, and are generally assigned to "R&D" to troubleshoot problems; ordinary administrators are generally assigned to "operation staff", who do not need "cache management", "menu management", etc., only need For some business-related functions, such as online and offline of a certain store, the menu categories they see after logging in to the system are as follows:



 

 

The specific requirements have been analyzed, and it is time for the "combination mode" to appear. For the object relationship of this tree structure, it is a typical application scenario of the "combination mode". Let's take a look at how to use the combination mode and how to achieve it. Since the combination mode has three roles, here are three steps to explain:

 

1. The abstract class Component defines some public abstract methods for menus or menu items, as well as some implemented public methods:

public abstract class Component {
    // list of roles
    List<String> roles = new ArrayList<String>();
 
    // menu name
    private String name;
 
    //Determine whether it is a leaf node or a directory node
    public abstract boolean hasChildren();
 
    //add child node
    public void addChildren(Component component){
        throw new UnsupportedOperationException();
    }
 
    //add role
    public void addRole(String role){
        this.roles.add(role);
    }
 
    //Determine whether the menu or menu item has a specified "role"
    public boolean hasRole(String role){
        return this.roles.contains(role);
    }
 
    //Print the menu list of the specified role
    public abstract void getMenuByRole(String role);
 
    // print all menu items
    public void printMenu(){
        throw new UnsupportedOperationException();
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
}
 

 

Main member variable or method description:

 

Stirng name member variable: All menus or menu items have a name field, and this field is extracted into the Component class;

 

List<String> roles member variable: represents the list of roles corresponding to each menu (or menu item). If a role is included in the list, it means that the role has access rights to the menu (or menu item). The corresponding methods are: "Add role" method addRole and "judgment role method" hasRole;

 

addChildren method: If it is a "menu" type, you need to implement this method to add a "menu" or "menu item" to its own list.

 

2. "Menu" implements the class Menu :

/**
 * Menu, root node or branch node, can contain submenus or menu items
 * Created by gantianxing on 2017/11/3.
 */
public class Menu extends Component{
    // list of submenus (or menu items)
    List<Component> childrens = new ArrayList<Component>();
 
    public Menu(String name){
        this.setName(name);
    }
 
    @Override
    public boolean hasChildren() {
        return true;
    }
 
    @Override
    public void addChildren(Component component){
        childrens.add(component);
    }
 
    @Override
    public void getMenuByRole(String role) {
        if(hasRole(role)){
            System.out.println("Start printing: "+this.getName());
        }
 
        Iterator<Component> iterator = childrens.iterator();
        while (iterator.hasNext()){
            Component children = iterator.next();
            children.getMenuByRole(role);
        }
 
 
    }
 
    @Override
    public void printMenu(){
        System.out.println("Start printing: " + this.getName());
        Iterator<Component> iterator = childrens.iterator();
        while (iterator.hasNext()){
            Component children = iterator.next();
            children.printMenu();
        }
    }
 
}
 

 

Main member variable or method description:

 

List<Component> childrens member variable: The list of sub "menus" or "menu items" that the menu belongs to.

 

addChildren method: Add child "menu" or "menu item" to List<Component> childrens.

 

getMenuByRole method: Get the menu list of the specified role, which will call the iterator of the List, and "recursively" call the getMenuByRole method of all its own Children (in fact, another mode "iterator mode" is used here).

 

3. The "menu item" implements the class MenuItem :

public class MenuItem extends Component {
    // menu item link
    private String url;
 
    public MenuItem(String name,String url){
        this.setName(name);
        this.setUrl(url);
    }
 
    @Override
    public boolean hasChildren() {
        return false;
    }
 
    @Override
    public void getMenuByRole(String role) {
        if(hasRole(role)){
            System.out.println("Menu name: "+this.getName()+" Menu link: "+this.getUrl());
        }
    }
 
    @Override
      public void printMenu(){
        System.out.println("Menu name: "+this.getName()+" Menu link: "+this.getUrl());
    }
 
    public String getUrl() {
        return url;
    }
 
    public void setUrl(String url) {
        this.url = url;
    }
}
 

 

Key members and method descriptions:

 

String url member variable: Each specific "menu item" corresponds to a clickable "link" address.

 

getMenuByRole method: If the role list corresponding to the "menu item" contains the specified role, print the menu item.

 

Well, a simple "menu authority management system" has been implemented using "combination mode", but there may be more "menus" and "roles" in the real scene.

 

Let's take a look at the test method and witness the miracle:

public static void main(String[] args) {
        //Create "Cache Management" submenu
        MenuItem pageCache = new MenuItem("Page Cache","/cache/pageCache.html");//Page Cache
        MenuItem dateCache = new MenuItem("Data cache","/cache/dataCache.html");//Data cache
        Menu cache = new Menu("cache management"); //cache management
        cache.addChildren(pageCache);
        cache.addChildren(dateCache);
 
        //Create "Operation Management" submenu
        MenuItem actManager = new MenuItem("Activity Management","/manager/actManager.html");//Activity Management
        MenuItem shopManager = new MenuItem("shop management","/manager/shopManager.html");//shop management
        Menu manager = new Menu("Operation Management");
        manager.addChildren(actManager);
        manager.addChildren(shopManager);
 
        //Create "User Management" submenu
        MenuItem superManager = new MenuItem("Administrator management","/user/supermanager.html");//Administrator management
        MenuItem supplierManager = new MenuItem("Supplier Management","/user/supplierManager.html");//Supplier Management
        Menu user = new Menu("User Management");
        user.addChildren(superManager);
        user.addChildren(supplierManager);
 
        //Create a "menu management" menu item
        MenuItem menuManager = new MenuItem("Menu Management","/menuManager.html");
 
        //create top level menu
        Menu background = new Menu("Manage Background");
        background.addChildren(cache);
        background.addChildren(manager);
        background.addChildren(user);
        background.addChildren(menuManager);
 
        // print all menus
        System.out.println("----------List of all menus------------");
        background.printMenu();
 
        // Grant the super admin role to each menu and menu item
        pageCache.addRole(SUPER_ROLE);
        dateCache.addRole(SUPER_ROLE);
        cache.addRole(SUPER_ROLE);
        actManager.addRole(SUPER_ROLE);
        shopManager.addRole(SUPER_ROLE);
        manager.addRole(SUPER_ROLE);
        superManager.addRole(SUPER_ROLE);
        supplierManager.addRole(SUPER_ROLE);
        user.addRole(SUPER_ROLE);
        menuManager.addRole(SUPER_ROLE);
        background.addRole(SUPER_ROLE);
 
        //Add "normal administrator role" to some menus
        actManager.addRole(NORMAL_ROLE);
        shopManager.addRole(NORMAL_ROLE);
        manager.addRole(NORMAL_ROLE);
        supplierManager.addRole(NORMAL_ROLE);
        user.addRole(NORMAL_ROLE);
        background.addRole(NORMAL_ROLE);
 
        //Print "Supplier Admin" menu list
        System.out.println("----------Ordinary administrator menu list------------");
        background.getMenuByRole(NORMAL_ROLE);
 
        //Print the "Super Admin" menu list (the result is the same as all menu lists)
        System.out.println("----------Super administrator menu list----------");
        background.getMenuByRole(SUPER_ROLE);
 
    }
 

 

The specific code logic is very simple. It is the initialization of some data. If you don't understand the code comments, you can finally build a top-level menu: background.

 

Execute the mian method, view the results, and witness the miracle:

------------ List of all menus -------------
Start printing: management background
Start Printing: Cache Management
Menu Name: Page Cache Menu Link: /cache/pageCache.html
Menu Name: Data Cache Menu Link: /cache/dataCache.html
Start Printing: Operations Management
Menu Name: Activity Management Menu Link: /manager/actManager.html
Menu Name: Shop Management Menu Link: /manager/shopManager.html
Start Printing: User Management
Menu Name: Admin Management Menu Link: /user/supermanager.html
Menu Name: Supplier Management Menu Link: /user/supplierManager.html
Menu Name: Menu Manager Menu Link: /menuManager.html
----------Common Admin Menu List-----------
Start printing: management background
Start Printing: Operations Management
Menu Name: Activity Management Menu Link: /manager/actManager.html
Menu Name: Shop Management Menu Link: /manager/shopManager.html
Start Printing: User Management
Menu Name: Supplier Management Menu Link: /user/supplierManager.html
-------Super Admin Menu List -------------
Start printing: management background
Start Printing: Cache Management
Menu Name: Page Cache Menu Link: /cache/pageCache.html
Menu Name: Data Cache Menu Link: /cache/dataCache.html
Start Printing: Operations Management
Menu Name: Activity Management Menu Link: /manager/actManager.html
Menu Name: Shop Management Menu Link: /manager/shopManager.html
Start Printing: User Management
Menu Name: Admin Management Menu Link: /user/supermanager.html
Menu Name: Supplier Management Menu Link: /user/supplierManager.html
Menu Name: Menu Manager Menu Link: /menuManager.html

 

 

The application result is divided into three parts: all the menu lists; the menu list of the "normal administrator" role; the menu list of the "super administrator" (same as all menu lists). Here we focus on the menu list of the "normal administrator" role:

----------Common Admin Menu List-----------
Start printing: management background
Start Printing: Operations Management
Menu Name: Activity Management Menu Link: /manager/actManager.html
Menu Name: Shop Management Menu Link: /manager/shopManager.html
Start Printing: User Management
Menu Name: Supplier Management Menu Link: /user/supplierManager.html

Comparing the above-mentioned "normal administrator" menu list display requirements is completely consistent:



 

 

If you want to add other roles and menus, there is no need to make any changes to the three categories corresponding to the "combination mode". It can be seen that the "combination pattern" satisfies the "open-closed principle" in the OO design pattern.

 

Application in real environment

 

Using the above code in a real environment, some adjustments will be made:

 

1. In a real environment, first create a "menu" or "menu item" through "menu management", and assign its corresponding "role list", and then save it to the database.

 

2. Assign the "role" corresponding to the user in "User Management".

 

3. The menu is only linked to the role, not the specific user, that is to say, the "menu list" corresponding to each role is fixed. At this time, multiple "top-level menu" objects can be initialized according to different roles (using the combination mode) , into the cache.

 

4. After a specific user (ordinary administrator or super administrator) successfully logs in, according to different roles, they can obtain different "top menu" objects in the cache and return them to the front-end page, without querying the database every time.

 

5. The front-end page traverses the "top-level menu" object for display.

 

Through the above process, you can complete the display of "different menu lists" for different roles.

 

Finally , let me briefly mention the verification of "menu permissions", which is also very simple to implement:

1. Create an interceptor in the back-end system to obtain the role information of the logged in user.

2. Determine whether the role list of the link (a MenuItem object) accessed by the user contains the role in step 1 (just call its hasRole method). If it is included, the verification is passed, otherwise the verification fails and returns illegal access.

 

Of course, if you use Spring mvc, you can also use Spring Security for authorization verification, that is, "combination mode" is used for menu management and display, and Spring Security is used for menu authorization verification. If you are interested in Spring Security, you can click here .

 

summary

 

The composite mode provides a tree-structured composite object, which can accommodate both individual objects and sub-combination objects, and allows clients to operate individual objects and composite objects equally in most cases (transparency); ), for example, the add method cannot be executed on an individual, and a trade-off needs to be made according to the specific situation. Supports the "Open-Closed Principle", but violates the "Single Responsibility Principle": both performing menu-related operations and managing hierarchies.

 

It cannot be said that the "composition pattern" violates some of the OO design principles, and the pattern is not desirable. It can only be said that for specific business needs, trade-offs are often made. This is called the golden mean, and so is programming.

 

The use of "combination mode" is summarized here. The keywords of this mode are "tree structure" and "parent-child relationship". When these words are in your needs, you can consider whether you can use "combination mode".

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326685474&siteId=291194637