본문 바로가기

Architecture, Pattern

[Pattern] 책임 연쇄 패턴 (Chain-of-responsibility pattern)

#What

실제 업무에 적용했었던 패턴을 소개하고자 합니다

chain of responsibility  책임 연쇄 패턴 : 문제를 두 개 이상의 객체(해결 담당자)에서 해결할 기회를 주고자 할 때 사용

*'responsibility : 책임자, 담당자'의 뜻도 있다.

 

[개요]

  • 어떤 문제나 요청사항이 발생했을 때 현재 담당자가 해결하지 못하면 다음 담당자에게 문제를 떠넘기는 패턴
  • 요청은 1번째 담당자에게 우전 전달, 미해결시 2번째 담당자에게 전해진다.. 쭉쭉...

[장점]

  • 문제 요청 객체와 문제 해결 객체 사이의 결합도를 낮출 수 있다
  • 담당자 Chain에서 담당자를 추가/삭제/순서변경 이 자유로우며 (객체지향) 각 담당자 클래스의 코드가 간결 해진다
  • 문제 해결 가능성이 높은 담당자 부터 해결 시도 하게끔 Chain을 구성 하는 것이 좋겠지요

[단점]

  • 하지만 모든 담당자가 해결하지 못하는 경우도 생길 수 있으므로 문제가 해결될것이라 보장할 수 없다
  • 문제 해결에 시간이 얼마나 소요될지 예측도 어렵다
  • 디버깅이 어려워 질 수 있다

 

 

 

구체적 상황에 대해 예제로 확인해 보겠습니다

 

***설정***

  1. 한 카페에 음료 주문이 들어옵니다
  2. 이 카페엔 총 3명의 음료 담당자가 있고 (1)Americano 담당자 (2)Latte 담당자 (3)Ade 담당자 
  3. 주믄 메뉴 문자열에 담당자의 'beverageType' 이 포함되어 있다면 만들 수 있다고 가정 합시다

ex) 주문 메뉴 = Ice Latte-> Latte 담당자가 만들 수 있음

 

 

주문 실행

    private fun processOrder(menu: String) {

		//첫번째 담당자는 AmericanoManager입니다
        val firstWorker = AmericanoManager("Americano")
		
        //그 다음 순서로는 'LatteManager -> setNextManager' 순으로 순서를 지정합니다
        firstWorker.setNextManager(LatteManager("Latte")).setNextManager(AdeManager("Lemonade"))
		
        //첫번째 담당자인 AmericanoManager에게 일을 시작하게 합니다
        val orderResult = firstWorker.processMenuOrder(menu)

        val result = "[메뉴 주문 결과] \n $orderResult" 
}

 

CaffeManager.kt

abstract class CaffeManager {
    //카페 주문 처리 프로세스
    fun processMenuOrder(menu: String): String {
        return if (makeBeverage(menu)) {
            "Success To Make [$beverageType]"
        } else {
            nextManager?.processMenuOrder(menu)
                ?: "Fail To Make Beverage : No Manager To Make $menu"
        }
    }

    //만들 수 있는 음료 종류
    abstract val beverageType: String

    //음료 만들기
    abstract fun makeBeverage(menu: String): Boolean 

    //내가 처리 못하는 주문일 경우 담당할 NextManager 지정
    private var nextManager: CaffeManager? = null
    fun setNextManager(nextManager: CaffeManager): CaffeManager {
        this.nextManager = nextManager
        return nextManager
    }

}

 

Manager.kt

class AmericanoManager(override val beverageType: String) : CaffeManager() {
    override fun makeBeverage(menu: String): Boolean {
        //'beverageType' 문자열을 포함한 'menu' 요청 이라면 만들 수 있는 음료라고 가정
        return menu.contains(beverageType, true)
    }
}

class LatteManager(override val beverageType: String) : CaffeManager() {
    override fun makeBeverage(menu: String): Boolean {
        //'beverageType' 문자열을 포함한 'menu' 요청 이라면 만들 수 있는 음료라고 가정
        return menu.contains(beverageType, true)
    }
}

class AdeManager(override val beverageType: String) : CaffeManager() {
    override fun makeBeverage(menu: String): Boolean {
        //'beverageType' 문자열을 포함한 'menu' 요청 이라면 만들 수 있는 음료라고 가정
        return menu.contains(beverageType, true)
    }
}

 

TEST

menu : Ice Latte

result :

         [메뉴 주문 결과] 
         Success To Make [Latte]

-> AmericanoManager는 latte 만들기 실패

-> LatteManager가 만들어 줌

 

 

동일한 예제 GitHub에서 확인 하실 수 있습니다.

GitHub 예제 : Chain Of Responsibility : CaffeManager


문제 있을시 알려 주세요.

 

좋은 하루 되세요!

 

 

 

 

 

'Architecture, Pattern' 카테고리의 다른 글

[Android] MVVM - ViewModels AntiPatterns  (0) 2022.08.02
[Pattern] MVC/MVP/MVVM  (0) 2021.10.10