ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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)

    댓글