본문 바로가기

설계

[디자인 패턴] 책임 연쇄 패턴 (Chain of Responsibility Pattern) - python 예제 코드

책임 연쇄 패턴은 객체의 처리 요청을 받은 객체가 그 요청을 처리하지 못할 경우 다음 객체에게 책임을 전달하여 처리를 이어가는 디자인 패턴입니다. 이를 통해 객체 간의 결합도를 낮추고 유연성을 높일 수 있습니다.

책임 연쇄 패턴은 보통 객체들이 리스트 형태로 연결되어 있으며, 각 객체는 다음 객체에 대한 참조를 유지합니다. 클라이언트는 요청을 보내고, 첫 번째 객체는 요청을 처리할 수 있는지 검사한 후 요청을 처리할 수 없으면 다음 객체에게 요청을 전달합니다. 이러한 과정을 반복하여 적절한 객체가 요청을 처리할 때까지 진행합니다.

 

1. 예제 코드


Python으로 구현된 간단한 예제를 살펴보겠습니다. 우리는 로그 메시지를 처리하는 책임 연쇄 패턴을 구현할 것입니다. 로그 메시지에는 세 가지 유형이 있습니다. Debug, Info 및 Error입니다. Debug 메시지는 콘솔에만 출력되고, Info 메시지는 콘솔과 파일에 출력되며, Error 메시지는 콘솔, 파일 및 이메일로 보내집니다. 이 예제에서는 세 가지 로그 메시지를 처리하는 세 가지 객체를 만들 것입니다.

 

class AbstractLogger:
    def __init__(self, level):
        self._level = level
        self._next_logger = None

    def set_next_logger(self, next_logger):
        self._next_logger = next_logger

    def log_message(self, level, message):
        if self._level <= level:
            self.write(message)
        if self._next_logger is not None:
            self._next_logger.log_message(level, message)

    def write(self, message):
        raise NotImplementedError


class ConsoleLogger(AbstractLogger):
    def __init__(self, level):
        super().__init__(level)

    def write(self, message):
        print(f"ConsoleLogger: {message}")


class FileLogger(AbstractLogger):
    def __init__(self, level):
        super().__init__(level)

    def write(self, message):
        print(f"FileLogger: {message}")


class EmailLogger(AbstractLogger):
    def __init__(self, level):
        super().__init__(level)

    def write(self, message):
        print(f"EmailLogger: {message}")


def get_logger_chain():
    error_logger = EmailLogger(AbstractLogger.ERROR)
    file_logger = FileLogger(AbstractLogger.INFO)
    file_logger.set_next_logger(error_logger)
    console_logger = ConsoleLogger(AbstractLogger.DEBUG)
    console_logger.set_next_logger(file_logger)
    return console_logger


if __name__ == "__main__":
    logger_chain = get_logger_chain()

    logger_chain.log_message(AbstractLogger.ERROR, "An error occurred!")
    logger_chain.log_message(AbstractLogger.INFO, "This is an info message.")
    logger_chain.log_message(AbstractLogger.DEBUG, "This is a debug message.")



이 예제에서는 AbstractLogger라는 추상 클래스를 만들고, 이 클래스는 로그 메시지의 처리를 담당하는 메서드를 정의합니다. AbstractLogger 클래스는 set_next_logger 메서드를 통해 다음 로그 메시지 처리자를 지정할 수 있습니다.

ConsoleLogger, FileLogger, EmailLogger 클래스는 AbstractLogger 클래스를 상속합니다. 각 클래스는 처리할 수 있는 로그 메시지 유형을 지정합니다. 이 예제에서는 각 클래스가 처리할 수 있는 로그 메시지의 유형을 다음과 같이 지정합니다.

ConsoleLogger: Debug
FileLogger: Debug, Info
EmailLogger: Debug, Info, Error

 

 

이 예제에서는 get_logger_chain 함수를 통해 로그 메시지 처리자의 체인을 구성합니다. 이 함수는 다음과 같은 작업을 수행합니다.

  1. Error 로그 메시지를 처리하는 EmailLogger 객체를 생성합니다.
  2. Info 및 Error 로그 메시지를 처리하는 FileLogger 객체를 생성합니다. FileLogger 객체는 다음 로그 메시지 처리자로 Error 로그 메시지를 처리하는 EmailLogger 객체를 지정합니다.
  3. Debug, Info, Error 로그 메시지를 처리하는 ConsoleLogger 객체를 생성합니다. ConsoleLogger 객체는 다음 로그 메시지 처리자로 FileLogger 객체를 지정합니다.

이러한 로그 메시지 처리자 체인을 구성한 후, 클라이언트는 로그 메시지 처리자 체인의 첫 번째 요소를 호출하여 로그 메시지를 처리합니다. 이 예제에서는 다음 코드를 사용하여 로그 메시지를 처리합니다.

 

logger_chain = get_logger_chain()

logger_chain.log_message(AbstractLogger.ERROR, "An error occurred!")
logger_chain.log_message(AbstractLogger.INFO, "This is an info message.")
logger_chain.log_message(AbstractLogger.DEBUG, "This is a debug message.")

 

  1. 첫 번째 코드는 Error 로그 메시지를 처리하도록 지정합니다. Error 로그 메시지는 EmailLogger 객체에서 처리됩니다.
  2. 두 번째 코드는 Info 로그 메시지를 처리하도록 지정합니다. Info 로그 메시지는 FileLogger 객체에서 처리되며, 다음 로그 메시지 처리자인 EmailLogger 객체에서도 처리됩니다.
  3. 마지막 코드는 Debug 로그 메시지를 처리하도록 지정합니다. Debug 로그 메시지는 ConsoleLogger 객체에서 처리되며, 다음 로그 메시지 처리자인 FileLogger와 EmailLogger 객체에서도 처리됩니다.


이러한 방식으로 책임 연쇄 패턴을 사용하여 로그 메시지를 처리하는 예제를 구현할 수 있습니다.

 

3. 장점

  • 유연성: 새로운 로그 메시지 유형을 처리하기 위해 로그 메시지 처리자 체인에 새로운 객체를 추가하기만 하면 됩니다. 이를 통해 쉽게 확장성을 높일 수 있습니다.
  • 확장성: 새로운 로그 메시지 유형을 처리하는 로그 메시지 처리자를 추가하는 것 외에도, 로그 메시지 처리자의 일부분을 다른 객체로 분리하여 새로운 처리기능을 추가할 수 있습니다.
  • 분리: 책임 연쇄 패턴은 각 객체가 자신의 책임 범위 내에서 동작하도록 구성됩니다. 이를 통해 유지보수가 용이해지며, 코드의 이해도가 높아집니다.
  • 무결성: 책임 연쇄 패턴을 사용하면 처리되지 않은 로그 메시지가 없어지므로, 로그 메시지 처리의 무결성을 보장할 수 있습니다.


4. 단점

처리 시간: 로그 메시지 처리자 체인을 통해 로그 메시지를 처리하는 것은 일반적으로 직접 처리하는 것보다 느릴 수 있습니다.

  • 복잡성: 로그 메시지 처리자 체인이 복잡하게 연결되어 있을 경우, 이해하기 어려울 수 있습니다.
  • 디버깅: 로그 메시지 처리자 체인에서 처리되는 로그 메시지가 어떤 객체에서 처리되는지 파악하기 어려울 수 있습니다.
  • 불필요한 객체 생성: 처리할 수 없는 로그 메시지를 처리하는 객체가 존재할 경우, 해당 객체는 불필요하게 생성됩니다.

이러한 장단점을 고려하여, 적절한 상황에서 책임 연쇄 패턴을 사용하는 것이 좋습니다.