책임 연쇄 패턴
- 메시지를 보내는 객체와 이를 받아 처리하는 객체들 간의 결합도를 없애기 위한 패턴.
- 하나의 요청에 대한 처리가 반드리 한 객체에서만 되지 않고, 여러 객체에게 그 처리 기회를 주려는 것.
- 핸들러들의 체인(사슬)을 따라 요청을 전달할 수 있게 해주는 행동 디자인 패턴
활용성
- 하나 이상의 객체가 요청을 처리해야 하고, 그 요청 처리자 중 어떤 것이 선행자(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를 설정해줘야만 제대로 책임을 전달하는데, 실제 구현 시 이 부분을 놓치기 쉬울것 같다. (모든 객체에 대한 내용을 개발자가 알아야 하기 때문에)
참고