디자인 패턴(구)/행위 패턴

책임 연쇄(chain of responsibility) 패턴이란?

공대키메라 2022. 4. 16. 22:23

1. 책임 연쇄(chain of responsibility) 패턴이란?

의도

메시지를 보내는 객체와 이를 받아 처리하는 객체들 간의 결합도를 없애기 위한 패턴.

하나의 요청에 대한 처리가 반드시 한 객체에서만 되지 않고, 여러 객체에게 기회를 주려 한다. 

 

즉, 요청을 보내는 쪽(sender)과 요청을 처리하는 쪽(receiver)의 분리하는 패턴이다.

사용 시기

  • 하나 이상의 객체가 요청을 처리해야 하고, 그 요청 처리자 중 어떤 것이 선행자인지 모를 때, 처리자가 자동으로 확정되어야 한다.
  • 메시지를 받을 객체를 명시하지 않을 채 여러 객체 중 하나에게 처리를 요청하고 싶을 때
  • 요청을 처리할 수 있는 객체 집합이 동적으로 정의되어야 할 때

구조

  • Handler : 요청을 처리하는 인터페이스를 정의하고, 후속 처리자와 연결을 구현. 즉, 연결고리에 연결된 다음 객체에서 다시 메시지를 보낸다. 
  • ConcreteHandler : 책임져야 할 행동이 있다면 스스로 요청일 처리하여 후속 처리자에 접근할 수 있다. 즉, 자신이 처리할 행동이 있으면 처리하고, 그렇지 않으면 후속 처리자에 다시 처리를 요청한다. 
  • Client : ConcreteHandler 객체에게 필요한 요청을 보낸다.

2. 구현

저번에 ProxyPattern 처럼 class 다이어그램을 그려볼까 했는데

 

구조 자체가 쉽다(건방지지만...) 하는 관계로 그냥 넘어갈것이다. 데헷.

 

패키지 구성은 다음과 같다.

RequestHandler.java

//field선언이 필요해 interface 말고 abstract를 사용
public abstract class RequestHandler {

    private RequestHandler nextHandler;

    public RequestHandler(RequestHandler nextHandler) {
        this.nextHandler = nextHandler;
    }

    public void handle(Request request) {
        if (nextHandler != null) {
            nextHandler.handle(request);
        }
    }
}

Request.java

public class Request {

    private String body;

    public Request(String body) {
        this.body = body;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }

}

AuthRequestHandler.java

public class AuthRequestHandler extends RequestHandler{

    public AuthRequestHandler(RequestHandler nextHandler) {
        super(nextHandler);
    }

    @Override
    public void handle(Request request) {
        System.out.println("인증 여부");
        super.handle(request);
    }
}

LogRequestHandler.java

public class LogRequestHandler extends RequestHandler{

    public LogRequestHandler(RequestHandler nextHandler) {
        super(nextHandler);
    }

    @Override
    public void handle(Request request) {
        System.out.println(request.getBody());
        super.handle(request);
    }
}

PringRequestHandler.java

public class PrintRequestHandler extends RequestHandler{

    public PrintRequestHandler(RequestHandler nextHandler) {
        super(nextHandler);
    }

    @Override
    public void handle(Request request) {
        System.out.println("print");
        super.handle(request);
    }
}

Client.java

public class Client {

    private RequestHandler requestHandler;

    public Client(RequestHandler requestHandler) {
        this.requestHandler = requestHandler;
    }

    public void doJob() {
        Request request = new Request("한번 테스트를 해보자앙");
        requestHandler.handle(request);
    }

    public static void main(String[] args) {
        RequestHandler chain = new AuthRequestHandler(new LogRequestHandler(new PrintRequestHandler(null)));
        Client client = new Client(chain);
        client.doJob();
    }

}

지금처럼 모든 handler를 거쳐가게 할 수도 있고, 중간에 무언가 조건을 줄 수도 있다.

 

설계자의 마음대로 코드를 조작하면 된다. 

 

근데 무언가 많이 본거같은데... 

 

아하! (혼자 뭐함...?)

 

Servlet Filter가 이런 구 조이다.

 

Filter chain에 대해 많이 들어봤을텐데 Filter 와 Interceptor의 차이점이 궁금하면 다음 링크를 참조하면 된다. 

 

(내 블로그)

 

그리고 Spring Security도 Filter구조로 많이 작성이 되어 있어서 무언가... chaing을 한다는 개념이 이런 책임 연쇄 패턴이랑 연관이 있을줄 꿈에도 몰랐다.(세상에 마상에)

 

장점

  • 클라이언트 코드를 변경하지 않고 새로운 핸들러를 체인에 추가할 수 있다.
  • 각각의 체인은 자신이 해야하는 일만 한다.
  • 체인을 다양한 방법으로 구성할 수 있다. 

단점

  • 디버깅이 조금 어렵다.

이번에는 filter에 대해 책임 연쇄 패턴에 대해 알아보았다.

 

모든 패턴들이 사실 이미 구현이 되어있는 것이 많고 대부분 잘 사용하면 크게 문제가 없지만

 

그래도... 근간 원리를 잘 알아야 나은 개발자가 될거라 생각한다.

 

부족하거나 무언가 더 더했으면 하는 내용이 있으면 피드백 부탁 헤엉