책임 연쇄 패턴( Chain of Responsibility Pattern )
- 명령 객체와 일련의 처리 객체를 포함하는 패턴
- 각각의 처리 객체는 명령 객체를 처리할 수 있는 연산의 집합이며 체인 안의 처리 객체가 핸들링 할 수 없는 명령은 다음 처리 객체로 넘겨진다.
- 체인에 들어가는 객체를 바꾸거나 순서를 바꿈으로서 역할을 동적으로 추가/제거 및 상황에 따라 동적으로 핸들러를 추가하거나 제거 할 수 있으며 이러한 변화가 전체 구조에 아무런 영향을 주지 않도록 클래스들 간의 낮은 결합도로 구현되어있다.
- 명령에 대한 처리가 반드시 수행된다는 보장이 안되므로 반드시 명령이 처리 될 수 있도록 적절한 체인의 순서 구현 필요
※ Sample Code
public abstract class Middleware {
private Middleware next;
/**
* Builds chains of middleware objects.
*/
public Middleware linkWith(Middleware next) {
this.next = next;
return next;
}
/**
* Subclasses will implement this method with concrete checks.
*/
public abstract boolean check(String email, String password);
/**
* Runs check on the next object in chain or ends traversing if we're in
* last object in chain.
*/
protected boolean checkNext(String email, String password) {
if (next == null) {
return true;
}
return next.check(email, password);
}
}
▲ 추상클래스 선언 ( java 8 이상부터는 interface의 default method로 구현해도 될 듯 하다. )
public class ThrottlingMiddleware extends Middleware {
private int requestPerMinute;
private int request;
private long currentTime;
public ThrottlingMiddleware( 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.
*/
@Override
public boolean check(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();
}
return checkNext(email, password);
}
}
public class UserExistsMiddleware extends Middleware {
private Server server;
public UserExistsMiddleware(Server server) {
this.server = server;
}
@Override
public boolean check(String email, String password) {
if (!server.hasEmail(email)) {
System.out.println("This email is not registered!");
return false;
}
if (!server.isValidPassword(email, password)) {
System.out.println("Wrong password!");
return false;
}
return checkNext(email, password);
}
}
▲ 사용자가 입력한 email / passowrd의 인증 프로세스
public class RoleCheckMiddleware extends Middleware {
@Override
public boolean check(String email, String password) {
if (email.equals("admin@example.com")) {
System.out.println("Hello, admin!");
return true;
}
System.out.println("Hello, user!");
return checkNext(email, password);
}
}
▲ 사용자 인증이 끝난 뒤 역할 확인 프로세스
public class Server {
private Map<String, String> users = new HashMap<>();
private Middleware middleware;
/**
* Client passes a chain of object to server. This improves flexibility and
* makes testing the server class easier.
*/
public void setMiddleware(Middleware middleware) {
this.middleware = middleware;
}
/**
* Server gets email and password from client and sends the authorization
* request to the chain.
*/
public boolean logIn(String email, String password) {
if (middleware.check(email, password)) {
System.out.println("Authorization have been successful!");
// Do something useful here for authorized users.
return true;
}
return false;
}
public void register(String email, String password) {
users.put(email, password);
}
public boolean hasEmail(String email) {
return users.containsKey(email);
}
public boolean isValidPassword(String email, String password) {
return users.get(email).equals(password);
}
}
▲ 사용자의 로그인 정보를 가지고 있는 클래스
public class Client {
private static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
private static Server server;
private static void init() {
server = new Server();
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 = new ThrottlingMiddleware(2);
middleware.linkWith(new UserExistsMiddleware(server)).linkWith(new RoleCheckMiddleware());
// Server gets a chain from client code.
server.setMiddleware(middleware);
}
public static void main(String[] args) throws IOException {
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);
}
}
▶ 실행 결과
'Java > Design Pattern' 카테고리의 다른 글
[Design Pattern] Observer Pattern (0) | 2020.10.02 |
---|---|
[Design Pattern] Command Pattern (0) | 2020.09.26 |