디자인 패턴: 책임 사슬 ⛓

carlos arandaUnsplash의 사진

책임 사슬 패턴은 복잡한 문제를 순차적으로 발생하는 단계로 나누는 행동 디자인 패턴입니다. 각 단계에서 다음 단계로 이동하거나 체인에서 나갈 가능성이 있습니다.

이것을 더 잘 이해하기 위해 기본 콜 센터가 어떻게 작동하는지 이해합시다. 당신이 구입한 새 제품에 관한 도움을 받기 위해 전자 제품 매장의 고객 관리 센터에 전화를 걸 때 거쳐야 할 모든 단계를 생각해 보십시오.

고객 지원



일반 고객 관리는 다음 단계를 따릅니다.
  • 사용자는 자동화된 음성 도우미와 상호 작용하여 시작합니다. 사용자는 계속하거나 중단하도록 선택할 수 있습니다.
  • 다음으로 사용자는 콜 센터 직원에게 리디렉션됩니다. 쿼리를 기반으로 직원은 문제를 해결하거나 사용자를 다음 수준으로 진행하도록 선택할 수 있습니다.
  • 최종 단계; 사용자는 거의 모든 것을 처리할 수 있는 관리자에게 리디렉션됩니다.

  • 고객이 우선 순위가 높은 고객인 경우 시스템이 직원과의 대화를 건너뛰고 고객을 관리자와 직접 연결하는 특별한 경우도 있습니다.

    이것을 어떻게 코딩할지 상상해 봅시다. 🤔 순진한 접근 방식은 사용자가 입력하는 단일 기능을 갖고 사용자의 상태와 상호 작용에 따라 다른 하위 기능(음성 비서, 동료 또는 관리자)이 호출되는 것입니다.

    그러나 시스템이 성장하고 더 많은 기능을 추가하고자 함에 따라 이 접근 방식은 기능을 관리하기 어렵게 만들고 하위 기능을 재사용하기 어렵게 만듭니다. 더 중요한 것은 이 접근 방식은 관심사를 분리하는 SOLID 프로그래밍 원칙을 위반한다는 것입니다. 🤕

    대안으로, 우리는 실행이 끝나자 마자 체인의 다음 단계를 즉시 호출하는 방식으로 하위 기능을 설계할 수 있습니다. 그런 식으로 모든 단계의 기능은 별도의 기능으로 유지되지만 체인에서 조각을 교체하거나 체인에 더 많은 단계를 삽입하는 것이 더 쉽습니다 🔁

    이에 대한 구현을 go에서 확인해 보겠습니다.

    type step interface {
        run(*customer)
        setNextStep(step)
    }
    



    type customer struct {
        name string
        isHighPriority bool
    }
    



    type voiceAssistant struct {
        next step
    }
    
    func (v *voiceAssistant) run(cust *customer) {
        fmt.Println("[Voice Assistant] Serving the customer: ", cust.name)
        v.next.run(cust)
    }
    
    func (v *voiceAssistant) setNextStep(next step) {
        v.next = next
    }
    



    type associate struct {
        next step
    }
    
    func (a *associate) run(cust *customer) {
        if cust.isHighPriority {
            fmt.Println("Redirecting customer directly to manager")
            a.next.run(cust)
            return
        }
        fmt.Println("[Associate] Serving the customer: ", cust.name)
        a.next.run(cust)
    }
    
    func (a *associate) setNextStep(next step) {
        a.next = next
    }
    



    type manager struct {
        next step
    }
    
    func (a *manager) run(cust *customer) {
        fmt.Println("[Manager] Serving the customer: ", cust.name)
    }
    
    func (a *manager) setNextStep(next step) {
        a.next = next
    }
    


    이 시점에서 체인의 각 링크가 생성됩니다. 원하는 워크플로를 따르도록 적절하게 정렬해야 합니다. Voice Assistant -> Associate(optional) -> Manager . 두 명의 서로 다른 고객에 대해 주요 기능에서 이 작업을 수행하고 결과를 살펴보겠습니다.

    func main() {
        m := &manager{}
    
        assoc := &associate{};
        assoc.setNextStep(m)
    
        va := &voiceAssistant{};
        va.setNextStep(assoc);
    
        // Chain formation complete
    
        // Start chain execution for normal customer
        normalCust := &customer{
            name: "Bob"
        }
    
        va.run(normalCust);
    
        fmt.Println("===================")
    
        // Start chain execution for high priority customer
        highPriorityCust := &customer{
            name: "John",
            isHighPriority: true,
        }
    
        va.run(highPriorityCust)
    }
    


    그러면 다음과 같은 출력이 표시됩니다.

    [Voice Assistant] Serving the customer:  Bob
    [Associate] Serving the customer:  Bob
    [Manager] Serving the customer:  Bob
    ===================
    [Voice Assistant] Serving the customer:  John
    Redirecting customer directly to manager
    [Manager] Serving the customer:  John
    


    이것이 바로 Chain of Responsibility 패턴입니다! 😁

    이 튜토리얼의 모든 코드는 이것this github repo에서 찾을 수 있습니다.

    건배 ☕️

    좋은 웹페이지 즐겨찾기