반응형
책임 연쇄 패턴이란?
요청을 사슬처럼 여러 독립적인 핸들러 객체를 순차적으로 지나치면서 각자 가지고 있는 책임을 해결하는 방법이다.
마치 공장에서 각 장치별로 담당하는 부분만 처리하는 것과 같다.
대표적으로 스프링의 필터가 이러한 책임 연쇄 패턴을 이용하고 있다.
왜 사용해야 하는가?
- 요청의 처리 순서를 제어할 수 있다.
- 단일 책임 원칙을 지킬 수 있다. (작업하는 각각의 객체들의 책임 분리)
- 개방/폐쇄 원칙을 지킬 수 있다. (새로운 핸들러추가시 기존 코드 수정하지 않는다.
언제 사용해야 하는가?
- 요청 처리가 여러 단계로 이루어져야 하는 경우(하지만 요청 유형과 순서들을 미리 알 수 없는 경우)
- 런타임시에 동적으로 핸들러를 관리해야 하는 경우
- 핸들러를 특정 순서로 실행해야 하는 경우
구현 예시 (자바)
구현의 예시로 인증, 인가 체크를 하는 핸들러를 구현해보자.
먼저 기본 핸들러를 만들어 준다.
package com.example.designpattern.behavioral.cor.handler;
public abstract class Handler {
private Handler next;
public static Handler link(Handler first, Handler... chain) {
Handler head = first;
for (Handler nextInChain : chain) {
head.next = nextInChain;
head = nextInChain;
}
return first;
}
public abstract boolean check(String email, String password);
protected boolean checkNext(String email, String password) {
if (next == null) {
return true;
}
return next.check(email, password);
}
}
- link메서드를 통해서 한 번에 핸들러들을 묶을 수 있도록 만든다.
- check는 실제 객체들이 수행할 메서드로 abstract로 지정하여 각 핸들러에서 메서드를 구현하도록 한다.
- checkNext에는 핸들러에 체인이 등록되어 있으면 다음 핸들러의 check메서드를 수행할 수 있도록 한다
먼저 인증 처리를 담당하는 핸들러를 만들어 보자
package com.example.designpattern.behavioral.cor.handler;
import com.example.designpattern.behavioral.cor.server.Server;
public class UserExistsHandler extends Handler {
private Server server;
public UserExistsHandler(Server server) {
this.server = server;
}
@Override
public boolean check(String email, String password) {
if (!server.hasEmail(email)) {
System.out.println("해당 이메일은 가입되지 않았습니다.");
return false;
}
if (!server.isValidPassword(email, password)) {
System.out.println("비밀번호가 틀렸습니다.");
return false;
}
System.out.println("인증 성공");
return checkNext(email, password);
}
}
- 로그인정보를 가지고 있는 서버를 통해서 해당 이메일과 패스워드가 적합한지 확인하는 과정이다.
다음으로 인가 체크를 담당하는 핸들러를 만들어 준다.
package com.example.designpattern.behavioral.cor.handler;
public class RoleCheckHandler extends Handler {
@Override
public boolean check(String email, String password) {
if (email.equals("admin@example.com")) {
System.out.println("반갑습니다, 어드민!");
return true;
}
System.out.println("반갑습니다, 일반유저!");
return checkNext(email, password);
}
}
- 이것도 유연하게 어드민계정을 등록하는 식으로 하면 좋겠지만 일단 하드코딩으로 넣어두었다. (이게 중점이 아니므로...)
이런 식으로 check 메서드를 목적에 알맞게 구현하고 마지막에 다음 핸들러를 호출하는 방식으로 핸들러를 구현하여 주면 된다.
마지막으로 이 핸들러들을 이용할 서버를 구현해 주자.
package com.example.designpattern.behavioral.cor.server;
import com.example.designpattern.behavioral.cor.handler.Handler;
import lombok.Setter;
import java.util.HashMap;
import java.util.Map;
public class Server {
private Map<String, String> users = new HashMap<>();
@Setter
private Handler handler;
public boolean logIn(String email, String password) {
if (handler.check(email, password)) {
System.out.println("인증 성공");
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);
}
}
클라이언트는 이제 간단하게 로그인 로직을 사용할 수 있다.
package com.example.designpattern.behavioral.cor;
import com.example.designpattern.behavioral.cor.handler.Handler;
import com.example.designpattern.behavioral.cor.handler.RoleCheckHandler;
import com.example.designpattern.behavioral.cor.handler.UserExistsHandler;
import com.example.designpattern.behavioral.cor.server.Server;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class CorClient {
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");
Handler handler = Handler.link(
new UserExistsHandler(server),
new RoleCheckHandler()
);
server.setHandler(handler);
}
public static void main(String[] args) throws IOException {
init();
boolean success;
do {
System.out.print("이메일을 입력하시오: ");
String email = reader.readLine();
System.out.print("패스워드를 입력하시오: ");
String password = reader.readLine();
success = server.logIn(email, password);
} while (!success);
}
}
실행 결과
이메일을 입력하시오: awdw
패스워드를 입력하시오: qwads
해당 이메일은 가입되지 않았습니다.
이메일을 입력하시오: admin@example.com
패스워드를 입력하시오: wefw
비밀번호가 틀렸습니다.
이메일을 입력하시오: admin@example.com
패스워드를 입력하시오: admin_pass
인증 성공
반갑습니다, 어드민!
인증 성공
반응형
'Design Pattern > 행동 패턴(Behavioral patterns)' 카테고리의 다른 글
Observer Pattern, 옵저버 패턴 (0) | 2024.04.01 |
---|---|
Memento Pattern, 메멘토 패턴 (1) | 2024.03.27 |
Mediator Pattern, 중재자 패턴 (2) | 2024.03.07 |
Iterator Pattern, 반복자 패턴 (0) | 2024.02.28 |
Command Pattern, 커맨드 패턴 (1) | 2024.02.27 |