Usage examples: The Chain of Responsibility is pretty common in Java.
One of the most popular use cases for the pattern is bubbling events to the parent components in GUI classes. Another notable use case is sequential access filters.
Here are some examples of the pattern in core Java libraries:
Identification: The pattern is recognizable by behavioral methods of one group of objects that indirectly call the same methods in other objects, while all the objects follow the common interface.
Filtering access
This example shows how a request containing user data passes a sequential chain of handlers that perform various things such as authentication, authorization, and validation.
This example is a bit different from the canonical version of the pattern given by various authors. Most of the pattern examples are built on the notion of looking for the right handler, launching it and exiting the chain after that. But here we execute every handler until there’s one that can’t handle a request. Be aware that this still is the Chain of Responsibility pattern, even though the flow is a bit different.
packagerefactoring_guru.chain_of_responsibility.example.middleware;/** * Base middleware class. */publicabstractclassMiddleware {privateMiddleware next; /** * Builds chains of middleware objects. */publicstaticMiddlewarelink(Middleware first,Middleware... chain) {Middleware head = first;for (Middleware nextInChain: chain) {head.next= nextInChain; head = nextInChain; }return first; } /** * Subclasses will implement this method with concrete checks. */publicabstractbooleancheck(String email,String password); /** * Runs check on the next object in chain or ends traversing if we're in * last object in chain. */protectedbooleancheckNext(String email,String password) {if (next ==null) {returntrue; }returnnext.check(email, password); }}
middleware/ThrottlingMiddleware.java: Check whether the limit on the number of requests is reached
packagerefactoring_guru.chain_of_responsibility.example.middleware;/** * ConcreteHandler. Checks whether there are too many failed login requests. */publicclassThrottlingMiddlewareextendsMiddleware {privateint requestPerMinute;privateint request;privatelong currentTime;publicThrottlingMiddleware(int requestPerMinute) {this.requestPerMinute= requestPerMinute;this.currentTime=System.currentTimeMillis(); } /** * Please, not that checkNext() call can be inserted both in the beginning * of this method and in the end. * * This gives much more flexibility than a simple loop over all middleware * objects. For instance, an element of a chain can change the order of * checks by running its check after all other checks. */publicbooleancheck(String email,String password) {if (System.currentTimeMillis() > currentTime +60_000) { request =0; currentTime =System.currentTimeMillis(); } request++;if (request > requestPerMinute) {System.out.println("Request limit exceeded!");Thread.currentThread().stop(); }returncheckNext(email, password); }}
packagerefactoring_guru.chain_of_responsibility.example.server;importrefactoring_guru.chain_of_responsibility.example.middleware.Middleware;importjava.util.HashMap;importjava.util.Map;/** * Server class. */publicclassServer {privateMap<String,String> users =newHashMap<>();privateMiddleware middleware; /** * Client passes a chain of object to server. This improves flexibility and * makes testing the server class easier. */publicvoidsetMiddleware(Middleware middleware) {this.middleware= middleware; } /** * Server gets email and password from client and sends the authorization * request to the chain. */publicbooleanlogIn(String email,String password) {if (middleware.check(email, password)) {System.out.println("Authorization have been successful!");// Do something useful here for authorized users.returntrue; }returnfalse; }publicvoidregister(String email,String password) {users.put(email, password); }publicbooleanhasEmail(String email) {returnusers.containsKey(email); }publicbooleanisValidPassword(String email,String password) {returnusers.get(email).equals(password); }}
Demo.java: Client code
packagerefactoring_guru.chain_of_responsibility.example;importrefactoring_guru.chain_of_responsibility.example.middleware.Middleware;importrefactoring_guru.chain_of_responsibility.example.middleware.RoleCheckMiddleware;importrefactoring_guru.chain_of_responsibility.example.middleware.ThrottlingMiddleware;importrefactoring_guru.chain_of_responsibility.example.middleware.UserExistsMiddleware;importrefactoring_guru.chain_of_responsibility.example.server.Server;importjava.io.BufferedReader;importjava.io.IOException;importjava.io.InputStreamReader;/** * Demo class. Everything comes together here. */publicclassDemo {privatestaticBufferedReader reader =newBufferedReader(new InputStreamReader(System.in));privatestaticServer server;privatestaticvoidinit() { server =newServer();server.register("admin@example.com","admin_pass");server.register("user@example.com","user_pass");// All checks are linked. Client can build various chains using the same// components.Middleware middleware =Middleware.link(newThrottlingMiddleware(2),newUserExistsMiddleware(server),newRoleCheckMiddleware() );// Server gets a chain from client code.server.setMiddleware(middleware); }publicstaticvoidmain(String[] args) throwsIOException {init();boolean success;do {System.out.print("Enter email: ");String email =reader.readLine();System.out.print("Input password: ");String password =reader.readLine(); success =server.logIn(email, password); } while (!success); }}
OutputDemo.txt: Execution result
Enter email: admin@example.com
Input password: admin_pass
Hello, admin!
Authorization have been successful!
Enter email: wrong@example.com
Input password: wrong_pass
This email is not registered!
Enter email: wrong@example.com
Input password: wrong_pass
This email is not registered!
Enter email: wrong@example.com
Input password: wrong_pass
Request limit exceeded!