apple/DesignPattern, Architecture

Swift 디자인패턴 Chain-of-Responsibility Pattern (책임 연쇄 패턴)

lgvv 2022. 7. 5. 19:13

Swift 디자인패턴 Chain-of-Responsibility Pattern (책임 연쇄 패턴)

 

Chain of Responsibility 패턴은 요청을 처리할 수 있는 여러 객체를 연결하여, 요청이 처리될 수 있는 객체를 찾는 행동 패턴.

요청을 보낸 객체와 요청을 처리하는 객체를 분리하여, 요청을 처리할 수 있는 객체가 여러 개일 때 유용

 

히스토리

  • 2022-07-05: 디자인 패턴 스터디 정리
  • 2024-11-26: 포스팅 글 재정리 및 스스로 만든 예제로 변경

 

Chain-of-Responsibility Pattern

 

 

Chain-of-Responsibility Pattern

Chain-of-Responsibility Pattern은 일반적으로 3개지 개념으로 구성됨

  • Handler Protocol: 요청을 처리할 수 있는 인터페이스
  • Concrete Handler: 요청을 처리하는 구현 클래스. 각 핸들러는 다음에 위치한 핸들러 참조할 수 있음
  • Client: 요청을 생성하고, 핸들러 체인에 요청 전달

책임 연쇄 패턴 적용을 고려할 수 있는 상황

  • 텍스트의 형식을 변경하는 여러 처리기를 연결해, 각 처리기가 요청을 처리할 수 있도록 함.
  • GUI 이벤트 처리, 로그 처리, 다양한 요청을 처리하는 시스템에서 유용
  • 요청을 보낸 객체와 요청을 처리하는 객체를 분리해, 요청 처리의 유연성을 높이고자 할 경우

책임 연쇄 패턴 장점

  • 유연성: 요청 핸들러의 경로를 동적으로 변경할 수 있을때 유용
  • OCP: 기존 핸들러의 수정 없이 새로운 핸들러를 쉽게 추가할 수 있음
  • SRP: 각 핸들러는 단일 책임 원칙을 준수

책임 연쇄 패턴 단점

  • 디버깅 어려움: 요청을 처리하는 곳이 런타임에 결정되기 때문에 추적하기 어려움
  • 성능 문제: 요청이 여러 핸들러를 거쳐야함으로 성능 저하가 발생할 수 있음

 

코드 예제

에러 로그 예제를 만들어보고자 함. 최근 WWDC에서 발표한 Logger(os_log)와 비슷

import SwiftUI

// 핸들러 프로토콜
protocol Handler {
    var successor: Handler? { get set }
    func handle(level: Level, message: String)
}

enum Level {
    case info
    case error
    case warning
    case debug
    
    var stringValue: String {
        switch self {
        case .info: return "INFO"
        case .error: return "ERROR"
        case .warning: return "WARNING"
        case .debug: return "DEBUG"
        }
    }
}

// 정보 로그 핸들러
class InfoHandler: Handler {
    var successor: Handler?

    func handle(level: Level, message: String) {
        if level == .info {
            print("INFO: \(message)")
        } else {
            successor?.handle(level: level, message: message)
        }
    }
}

// 경고 로그 핸들러
class WarningHandler: Handler {
    var successor: Handler?

    func handle(level: Level, message: String) {
        if level == .warning {
            print("WARNING: \(message)")
        } else {
            successor?.handle(level: level, message: message)
        }
    }
}

// 오류 로그 핸들러
class ErrorHandler: Handler {
    var successor: Handler?

    func handle(level: Level, message: String) {
        if level == .warning {
            print("ERROR: \(message)")
        } else {
            successor?.handle(level: level, message: message)
        }
    }
}

private struct ContentView: View {
    var body: some View {
        Button("Execute") {
            // 핸들러 체인 구성
            let errorHandler = ErrorHandler()
            let warningHandler = WarningHandler()
            warningHandler.successor = errorHandler
            let infoHandler = InfoHandler()
            infoHandler.successor = warningHandler

            // 로그 메시지 처리
            infoHandler.handle(level: .info, message: "This is an info message.")
            infoHandler.handle(level: .warning, message: "This is a warning message.")
            infoHandler.handle(level: .error, message: "This is an error message.")
            infoHandler.handle(level: .debug, message: "This is a debug message.")  // 처리되지 않음
        }
    }
}

#Preview {
    ContentView()
}

 

결과



(참고)

https://www.raywenderlich.com/books/design-patterns-by-tutorials/v3.0/chapters/22-chain-of-responsibility-pattern

 

Design Patterns by Tutorials, Chapter 22: Chain-of-Responsibility Pattern

This is a behavioral design pattern that allows an event to be processed by one of many handlers. See how to use this pattern whenever you have a group of related objects that handle similar events but vary based on event type, attributes or something else

www.kodeco.com:443

https://refactoring.guru/ko/design-patterns/chain-of-responsibility

 

책임 연쇄 패턴

/ 디자인 패턴들 / 행동 패턴 책임 연쇄 패턴 다음 이름으로도 불립니다: CoR, 커맨드 사슬, Chain of Responsibility 의도 책임 연쇄 패턴은 핸들러들의 체인​(사슬)​을 따라 요청을 전달할 수 있게

refactoring.guru