-
[Design Pattern] 책임 연쇄 패턴 (Chain of Responsibility Pattern)공부/디자인 패턴 2021. 5. 15. 19:13
클라이언트에게 어떠한 요청이 들어왔을 때, 요청을 받은 객체가 해당 요청을 해결할 수 없을 경우 연결된 다음 객체들에 전달하고 해당 요청을 해결할 수 있는 객체가 처리하는 방식입니다. 요청 객체와 처리 객체를 분리하거나 요청을 처리할 수 있는 객체가 여러 개인데 하나의 객체에 요청을 보낼 때 책임 연쇄 패턴을 적용할 수 있습니다. 즉, 요청을 처리할 수 있는 객체가 여러개이고 이러한 처리를 하는 객체가 명시적이지 않을 때 사용할 수 있는 패턴입니다.
클래스 다이어그램
Handler: 요청을 처리하기 위한 객체들이 가질 인터페이스
ConcreteHandler1, 2: 요청 종류에 따라 자신이 처리 할 수 있는 부분을 구현한 객체
Client : 수신자에게 처리 요청
장점
요청의 발신자와 수신자를 분리시킬 수 있고 클라이언트는 처리 객체들의 내부 구조를 알 필요가 없습니다. 또한 처리 객체들(chain)의 처리 순서를 변경하거나 추가, 삭제를 유연하게 할 수 있고 새로운 요청에 대해 객체 생성도 가능합니다.
단점
처리 객체들 내부에서 사이클이 발생할 수 있고 디버깅 및 테스트가 어렵습니다. 또한 요청을 처리할 객체가 chain 내부에서 어느 위치에 있을 지는 동적으로 변경되기 때문에 시간 예측이 어렵습니다.
예시
public abstract class Handler { public String name; private Handler next; public Handler(String name) { this.name = name; } public void setNext(Handler next) { this.next = next; } public void handleRequest(int number) { if (canHandle(number)) { print(number); // chain이 연결되어 있으면 다음 처리 객체에 문제 전달 (oddHandler -> evenHandler 순으로 처리) } else if (next != null) { next.handleRequest(number); } else { System.out.println("처리할 수 있는 객체 없음"); } } public void print(int number) { System.out.println(number + ":" + name + "로 처리"); } public abstract boolean canHandle(int number); }
public class OddHandler extends Handler { public OddHandler(String name) { super(name); } public boolean canHandle(int number) { return number % 2 != 0; } }
public class EvenHandler extends Handler { public EvenHandler(String name) { super(name); } public boolean canHandle(int number) { return number % 2 == 0; } }
Handler oddHandler = new OddHandler("홀수"); Handler evenHandler = new EvenHandler("짝수"); // chain 연결 oddHandler -> evenHandler oddHandler.setNext(evenHandler); for (int i = 1; i <= 10; i++) { oddHandler.handleRequest(i); }
위에서는 oddHandler 다음으로 evenHandler를 연결하여 chain을 형성하고 oddHandler가 처리할 수 없는 문제면 evenHandler 가 처리하도록 하는 예시입니다.
파이썬
파이썬에서는 abcMeta 모듈을 활용해 자바와 동일하게 구현할 수 있습니다.
from abc import ABCMeta, abstractmethod class Handler(metaclass=ABCMeta): def __init__(self, name: str): self.name = name self.__next = None def set_next(self, next_obj: object): self.__next = next_obj def handle_request(self, number: int): if self.can_handle(number): self.response(number) elif self.__next: self.__next.handle_request(number) else: print('처리할 수 있는 객체 없음') def response(self, number: int): print(f'{number}: {self.name} 처리') @abstractmethod def can_handle(self, number: int): pass class OddHandler(Handler): def __init__(self, name: str): super().__init__(name) def can_handle(self, number: int): return number % 2 != 0 class EvenHandler(Handler): def __init__(self, name: str): super().__init__(name) def can_handle(self, number: int): return number % 2 == 0 odd_handler = OddHandler('홀수') even_handler = EvenHandler('짝수') odd_handler.set_next(even_handler) for i in range(10): odd_handler.handle_request(i)