This article is excerpted from Tan Yongde (Tom), author of "design patterns should be learned in this way"
1. Use the responsibility chain mode to design hot plug permission control
First, create an entity class Member.
public class Member { private String loginName; private String loginPass; private String roleName; public Member(String loginName, String loginPass) { this.loginName = loginName; this.loginPass = loginPass; } public String getLoginName() { return loginName; } public String getLoginPass() { return loginPass; } public String getRoleName() { return roleName; } public void setRoleName(String roleName) { this.roleName = roleName; } @Override public String toString() { return "Member{" + "loginName='" + loginName + '\'' + ", loginPass='" + loginPass + '\'' + '}'; } }
Then let's look at a piece of code we often write.
public class MemberService { public void login(String loginName,String loginPass){ if(StringUtils.isEmpty(loginName) || StringUtils.isEmpty(loginPass)){ System.out.println("The user name and password are verified successfully. You can proceed"); return; } System.out.println("The user name and password cannot be empty. You can proceed"); Member member = checkExists(loginName,loginPass); if(null == member){ System.out.println("user does not exist"); return; } System.out.println("Login succeeded!"); if(!"administrators".equals(member.getRoleName())){ System.out.println("You are not an administrator and do not have operation permission"); return; } System.out.println("Allow operation"); } private Member checkExists(String loginName,String loginPass){ Member member = new Member(loginName,loginPass); member.setRoleName("administrators"); return member; } public static void main(String[] args) { MemberService service = new MemberService(); service.login("tom","666"); } }
In the above code, the data verification before login is mainly done. The judgment logic is sequential. First, make a non empty judgment, then check whether the account is valid, and finally obtain the user role. Match whether there are operation permissions according to the permissions owned by the user role. Such inspection codes are generally essential, but they are very bloated when written in specific business codes. Therefore, we can use the responsibility chain mode to connect these inspection steps in series without affecting the beauty of the code, which can make us pay more attention to a specific business logic processing when coding. Let's optimize the code with the responsibility chain pattern. First, create a Handler class.
public abstract class Handler { protected Handler chain; public void next(Handler handler){ this.chain = handler; } public abstract void doHandle(Member member); }
Then create the non null validation ValidateHandler class, login validation LoginHandler class and permission validation AuthHandler class respectively. The code of the ValidateHandler class is as follows.
public class ValidateHandler extends Handler { public void doHandle(Member member) { if(StringUtils.isEmpty(member.getLoginName()) || StringUtils.isEmpty(member.getLoginPass())){ System.out.println("User name or password is empty"); return; } System.out.println("The user name and password are verified successfully. You can proceed"); chain.doHandle(member); } }
The code of the LoginHandler class is as follows.
public class LoginHandler extends Handler { public void doHandle(Member member) { System.out.println("Login succeeded!"); member.setRoleName("administrators"); chain.doHandle(member); } }
The code of AuthHandler class is as follows.
public class AuthHandler extends Handler { public void doHandle(Member member) { if(!"administrators".equals(member.getRoleName())){ System.out.println("You are not an administrator and do not have operation permission"); return; } System.out.println("You are an administrator and are not allowed to operate"); } }
Then modify the code in MemberService. In fact, you only need to connect the previously defined handlers in series according to business requirements to form a chain.
public class MemberService { public void login(String loginName,String loginPass){ Handler validateHandler = new ValidateHandler(); Handler loginHandler = new LoginHandler(); Handler authHandler = new AuthHandler(); validateHandler.next(loginHandler); loginHandler.next(authHandler); validateHandler.doHandle(new Member(loginName,loginPass)); } }
Finally, write the client call code.
public class Test { public static void main(String[] args) { MemberService service = new MemberService(); service.login("tom","666"); } }
The operation results are shown in the figure below.
In fact, many permission verification frameworks we usually use use use this principle to decouple the permission processing of various dimensions and then connect them in series to only deal with their respective related responsibilities. If the responsibility is not related to yourself, throw it to the next Handler in the chain, commonly known as "kicking the ball".
2. The responsibility chain mode is combined with the builder mode
Because the responsibility chain mode has a chain structure, and in the above code, the role responsible for assembling the chain structure is MemberService. When the chain structure is long, the work of MemberService will be very cumbersome, and the code of MemberService is relatively cumbersome, and subsequent changes to the Handler or message type must be modified in MemberService, which does not comply with the opening and closing principle. The reason for these problems is that the assembly of chain structure is too complex. For the creation of complex structure, we naturally think of builder mode. Using builder mode, we can automatically chain assemble the processing node object specified by MemberService. Customers only need to specify the processing node object and don't care about anything else, Moreover, the order of processing node objects specified by the customer is different, and the constructed chain structure is also different. Let's transform it. First, modify the code of the Handler.
public abstract class Handler<T> { protected Handler chain; public void next(Handler handler){ this.chain = handler; } public abstract void doHandle(Member member); public static class Builder<T> { private Handler<T> head; private Handler<T> tail; public Builder<T> addHandler(Handler<T> handler) { if (this.head == null) { this.head = this.tail = handler; return this; } this.tail.next(handler); this.tail = handler; return this; } public Handler<T> build() { return this.head; } } }
Then modify the code of MemberService.
public class MemberService { public void login(String loginName,String loginPass){ Handler.Builder builder = new Handler.Builder(); builder.addHandler(new ValidateHandler()) .addHandler(new LoginHandler()) .addHandler(new AuthHandler()); builder.build().doHandle(new Member(loginName,loginPass)); } }
Because the Builder mode is to build the node Handler, we take the Builder as the static internal class of the Handler, and because the client does not need chain assembly, we can also set the chain assembly method next() method to private to make the Handler more cohesive. The code is as follows.
public abstract class Handler<T> { protected Handler chain; private void next(Handler handler){ this.chain = handler; } ... }
Through this case, the partners should have felt the essence of the combination of responsibility chain and builder.
Design pattern series
Finally, someone analyzed the agency model thoroughly
If this article is helpful to you, you are welcome to pay attention and praise; If you have any suggestions, you can also leave comments or private letters. Your support is the driving force for me to adhere to my creation. If you need relevant learning materials such as design mode and source code, you can click the small plug-in below to get the materials for free