Gof의 디자인 패턴 - 책임 연쇄 패턴

Groot·2023년 12월 10일
0

TIL

목록 보기
144/153
post-thumbnail

책임 연쇄 패턴

  • 메시지를 보내는 객체와 이를 받아 처리하는 객체들 간의 결합도를 없애기 위한 패턴.
  • 하나의 요청에 대한 처리가 반드리 한 객체에서만 되지 않고, 여러 객체에게 그 처리 기회를 주려는 것.
  • 핸들러들의 체인(사슬)을 따라 요청을 전달할 수 있게 해주는 행동 디자인 패턴

활용성

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

구조

요소

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

협력 방법

  • 사용자는 처리를 요청하고, 이 처리 요청은 실제로 그 요청을 받을 책임이 있는 ConcreteHandler 객체를 만날 때까지 정의된 연결 고리를 따라서 계속 전달

장점

  • 객체간의 결합도가 낮아진다.
    • 객체가 관련된 후보 객체들을 알 필요 없이 단순하게 자신과 연결된 후보 객체만 알면 된다.
  • 객체에 책임을 할당하는 데 유연성을 높일 수 있다.
    • 객체의 책임을 여러 객체에 분산시킬 수 있으므로 런타임에 객체 연결 고리를 변경하거나 추가하여 책임을 변경하거나 확장할 수 있다.
  • 단일 책임 원칙
  • 개방/폐쇄 원칙

단점

  • 메시지 수신이 보장되지 않음. ← 아주 큰 단점?
    • 객체들 간의 연결 고리가 잘 정의되지 않으면, 요청은 처리되지 않는다.

예시 코드

import Foundation

// 로그 처리기 프로토콜
protocol LogHandler: AnyObject {
    var next: LogHandler? { get set }
    
    func handleLogMessage(_ message: String, priority: Int)
    func setNext(handler: LogHandler) -> LogHandler
}

extension LogHandler {
    func setNext(handler: LogHandler) -> LogHandler {
        self.next = handler
        
        return handler
    }
}

// 기본 로그 처리기
class BaseLogHandler: LogHandler {
    weak var next: LogHandler?
    
    func handleLogMessage(_ message: String, priority: Int) {
        // 기본 처리 로직
        print("Base Log Handler: \(message)")
        
        // 다음 처리기로 메시지 전달
        next?.handleLogMessage(message, priority: priority)
    }
}

// 특정 우선순위에 따른 로그 처리기
class PriorityLogHandler: LogHandler {
    weak var next: LogHandler?
    let priorityThreshold: Int

    init(priority: Int) {
        self.priorityThreshold = priority
    }

    func handleLogMessage(_ message: String, priority: Int) {
        if priority <= priorityThreshold {
            // 우선순위가 일정 수준 이하인 경우 처리
            print("Priority Log Handler (\(priorityThreshold)): \(message)")
        } else {
            // 우선순위가 더 높은 경우 다음 처리기에게 전달
            next?.handleLogMessage(message, priority: priority)
        }
    }
}

// 처리기 인스턴스 생성
let baseHandler = BaseLogHandler()
let lowPriorityHandler = PriorityLogHandler(priority: 3)
let mediumPriorityHandler = PriorityLogHandler(priority: 6)
let highPriorityHandler = PriorityLogHandler(priority: 9)

// 처리기 연결
baseHandler.setNext(handler: lowPriorityHandler)
    .setNext(handler: mediumPriorityHandler)
    .setNext(handler: highPriorityHandler)
// 로그 메시지 처리
baseHandler.handleLogMessage("Low Priority Message", priority: 2)
baseHandler.handleLogMessage("Medium Priority Message", priority: 5)
baseHandler.handleLogMessage("High Priority Message", priority: 9)
  • next를 설정해줘야만 제대로 책임을 전달하는데, 실제 구현 시 이 부분을 놓치기 쉬울것 같다. (모든 객체에 대한 내용을 개발자가 알아야 하기 때문에)

참고

profile
I Am Groot

0개의 댓글