Web application menu display condition design / code example

The story of the system in business. After logging in, when I investigated the conditions under which the menu changes depending on the company and people, I once thought, "Isn't it better to use a class?", So make a note of your thoughts.

Current code

The following company classes and user classes exist, and views that use those information are implemented. Here, getters and setters are assumed to be implemented automatically by @Data. Also, the magic number should not be used originally, but the constantization is omitted.

Company class

Company.java



/**
 *Class to store company information
 */
@Data
public class Company {
    private String companyId; //Company ID
    private int option1Flg;   //Option 1 flag (0 or 1)
    private int option2Flg;   //Option 2 flag (0 or 1)
}

User class

User.java


/**
 *Class to store user information
 */
@Data
public class User {
    private String userId;  //User ID
    private int userType;   //User type (0: general user, 1: administrator)
}

Menu view

menu.jsp


<% if (company.getOption1Flg() == 1) { %>
    //Show menu 1
<% } %>
<% if (company.getOption2Flg() == 1) { %>
    //Show menu 2
<% } %>
<% if (user.getUserType() == 1
        && (company.getOption1Flg() == 1 || company.getOption2Flg() == 1)) { %>
    //Show administrator menu
<% } %>

Think of company as an object of the Company class and user as an object of the User class.

Basically, if each option is 1, it just displays the respective menu. Only the admin menu is special, it will be displayed if the user is an admin and one of the options is 1.

Isn't it a code that you have seen for a while? At present, there is no problem at first glance.

Code after specification change (bad example)

I had to change the specifications as follows.

--Add a reader to the user type --Add options 3-5 --Only the reader can display menus 3 to 5 corresponding to options 3 to 5. --The administrator menu is displayed if the user is an administrator and any of options 1 to 4 is 1 (5 is irrelevant).

Let's code this again, but first let's follow the original format.

Company class

Company.java



/**
 *Class to store company information
 */
@Data
public class Company {
    private String companyId; //Company ID
    private int option1Flg;   //Option 1 flag (0 or 1)
    private int option2Flg;   //Option 2 flag (0 or 1)
    private int option3Flg;   //Option 3 flag (0 or 1)
    private int option4Flg;   //Option 4 flag (0 or 1)
    private int option5Flg;   //Option 5 flag (0 or 1)
}

User class

User.java


/**
 *Class to store user information
 */
@Data
public class User {
    private String userId;  //User ID
    private int userType;   //User type (0: general user, 1: administrator, 2: reader)
}

Menu view

menu.jsp


<% if (company.getOption1Flg() == 1) { %>
    //Show menu 1
<% } %>
<% if (company.getOption2Flg() == 1) { %>
    //Show menu 2
<% } %>
<% if (user.getUserType() == 2 && company.getOption3Flg() == 3) { %>
    //Show menu 3
<% } %>
<% if (user.getUserType() == 2 && company.getOption4Flg() == 4) { %>
    //Show menu 4
<% } %>
<% if (user.getUserType() == 2 && company.getOption5Flg() == 5) { %>
    //Show menu 5
<% } %>
<% if (user.getUserType() == 1
        && (company.getOption1Flg() == 1 || company.getOption2Flg() == 1) 
            || company.getOption3Flg() == 1) || company.getOption4Flg() == 1)) { %>
    //Show administrator menu
<% } %>

The menu view can still be identified, but you can see that the code has become quite troublesome. If the specifications are changed in the future, I think it will be bad.

Solution: Add a menu class

I think there are various solutions, but I will design it to add a menu class. At the same time, the existing class will also add a state determination method.

Company class

Company.java



/**
 *Class to store company information
 */
@Data
public class Company {
    private String companyId; //Company ID
    private int option1Flg;   //Option 1 flag (0 or 1)
    private int option2Flg;   //Option 2 flag (0 or 1)
    private int option3Flg;   //Option 3 flag (0 or 1)
    private int option4Flg;   //Option 4 flag (0 or 1)
    private int option5Flg;   //Option 5 flag (0 or 1)

    public boolean isEnabledOption1() {
        return option1Flg == 1;
    }
    public boolean isEnabledOption2() {
        return option2Flg == 1;
    }
    public boolean isEnabledOption3() {
        return option3Flg == 1;
    }
    public boolean isEnabledOption4() {
        return option4Flg == 1;
    }
    public boolean isEnabledOption5() {
        return option5Flg == 1;
    }
    public boolean isEnabledAdminOption() {
        return isEnabledOption1() 
            || isEnabledOption2() 
            || isEnabledOption3() 
            || isEnabledOption4();
    }
}

User class

User.java


/**
 *Class to store user information
 */
@Data
public class User {
    private String userId;  //User ID
    private int userType;   //User type (0: general user, 1: administrator, 2: reader)

    public boolean isNormalUser() {
        return userType == 0;
    }
    public boolean isAdmin() {
        return userType == 1;
    }
    public boolean isLeader() {
        return userType == 2;
    }
}

Menu class

User.java


/**
 *Menu class
 */
@RequiredArgsConstructor
@Getter
public class Menu {
    private final Company company;
    private final User user;

    public boolean isEnabledMenu1() {
        return company.isEnabledOption1();
    }

    public boolean isEnabledMenu2() {
        return company.isEnabledOption2();
    }

    public boolean isEnabledMenu3() {
        return user.isLeader() && company.isEnabledOption3();
    }

    public boolean isEnabledMenu4() {
        return user.isLeader() && company.isEnabledOption4();
    }

    public boolean isEnabledMenu5() {
        return user.isLeader() && company.isEnabledOption5();
    }

    public boolean isEnabledAdminMenu() {
        return user.isAdmin() && company.isEnabledAdminOption();
    }
}

Menu view

menu.jsp



<% if (menu.isEnabledMenu1()) { %>
    //Show menu 1
<% } %>
<% if (menu.isEnabledMenu2()) { %>
    //Show menu 2
<% } %>
<% if (menu.isEnabledMenu3()) { %>
    //Show menu 3
<% } %>
<% if (menu.isEnabledMenu4()) { %>
    //Show menu 4
<% } %>
<% if (menu.isEnabledMenu5()) { %>
    //Show menu 5
<% } %>
<% if (menu.isEnabledAdminMenu()) { %>
    //Show administrator menu
<% } %>

After the improvement, I think that the display conditions of the menu and the status of the company and users have become very easy to understand. In a real application, it is possible to create a class that corresponds to one menu such as MenuItem and let that class make a decision, but this article will stop here.

Finally: Be careful when the user starts to judge the value

In this example, there was a problem with starting to directly determine the state of the company or user in the menu view. However, in object orientation,

** Don't ask, order **

Is the basic idea. It depends on the responsibility of the class, but let's leave the processing to the side with the value.

You'll often see this kind of code in legacy projects. Mostly out of control anymore, it's usually the result of starting with these small breach and leaving it unrefactored. If possible, be proactive in refactoring.

Recommended Posts

Web application menu display condition design / code example
Application example of design pattern (No. 1)