-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Chain of Responsibility 패턴은 Handler chain에 따라 요청을 연쇄적으로 전달할 수 있는 디자인 패턴입니다. 요청을 수신한 각 Handler는 요청을 처리하거나 다음 Handler로 전달할지 결정하게 됩니다.
즉, 여러 개의 Handler가 연결되어있고, 이렇게 연결된 Handler들을 Chain이라고 합니다. 어떤 요청이 Chain에 들어왔을 때 조건이 맞다면 이를 처리 할 수도 있고, 아니라면 다음 Handler로 넘길 수도 있습니다.
- Client는 이벤트를 수락하고 Handler 프로토콜의 인스턴스로 전달합니다. 이벤트는 단순한 property-only 구조이거나 복잡한 사용자 작업과 같은 복잡한 객체일 수 있습니다.
- Handler 프로토콜은 Client의 요청을 처리하기 위해 Concrete Handler가 구현해야 하는 필수 프로퍼티 및 메서드를 정의합니다.
- 프로토콜은 추상 클래스로 대체하여 저장 프로퍼티를 사용할 수 있도록 구성하는 것도 가능합니다. 그럼에도 불구하고, 여전히 직접적으로 인스턴스화 되는 것을 의미하지 않습니다. 오히려 Concrete Handler가 충족해야 하는 인터페이스만 정의합니다.
- 현재 Handler에서 처리하지 않을것이라면 요청을 넘길 다음 Handler를 옵셔널로 정의합니다.
- 첫 번째 Concrete Handler는 Handler 프로토콜을 구현하며, Client에 의해 직접 저장됩니다. 이벤트를 수신하면 먼저 이벤트를 처리하려고 시도합니다. 그렇지 않으면 이벤트를 다음 Handler로 전달합니다
따라서, Client는 모든 Concrete Handler를 단일 인스턴스인 것처럼 처리할 수 있습니다. 후드 아래에서 각 Concrete Handler는 자신에게 전달된 이벤트를 처리할지 또는 다음 Handler에 전달할지를 결정합니다. 이는 Client가 프로세스에 대해 아무것도 알 필요 없이 발생합니다.
이벤트를 처리할 수 있는 Concrete Handler가 없는 경우, 마지막 Handler는 요구 사항에 따라 nil을 반환하거나, 아무 작업도 수행하지 않거나, 오류를 발생시켜 이를 마무리 합니다.
사용
유사한 이벤트를 처리하지만 이벤트 유형, 속성 또는 이벤트와 관련된 기타 사항에 따라 달라지는 관련 객체 그룹이 있을 때마다 Chain of Responsibility 패턴을 사용합니다.
실생활에서 발견할 수 있는 Chain of Responsibility 패턴의 예로는 전화 고객 서비스가 있습니다. 예를 들어 어떤 가전제품 회사의 컴퓨터를 사용 중인데, 컴퓨터가 고장이 난 거예요. 그래서 그 회사의 고객 서비스에 전화를 합니다. 그 회사는 세탁기만 만드는 회사가 아니므로 고객에게 어떤 제품을 사용 중이냐고 고르라고 합니다. 고객은 컴퓨터가 고장 났다고 선택합니다. 그러자 고객 서비스에서는 컴퓨터에서도 자주 발생하는 문제를 나열한 뒤 그중에 있다면 골라달라고 합니다. 거기서 하나를 고르자 고객은 드디어 해당 내용을 담당하는 직원과 통화를 할 수 있게 되었습니다.
이렇게 어떤 일을 처리하기 위해 해당 업무 담당자를 찾는 방금의 예처럼, Client가 요청을 주면 Handler 체인에 존재하는 객체들 중 해당 요청을 처리하는 객체가 나올 때까지 요청을 넘기는 방법이 Chain of Responsibility 패턴이라고 할 수 있습니다.
예제를 기반으로 보면 Concrete Handler는 완전히 다른 클래스이거나, 혹은 동일한 클래스 유형이지만 인스턴스 및 구성이 다를 수 있습니다. 예를 들어, 패턴을 사용하여 동전을 받는 자동판매기를 구현할 수 있습니다.
- 자동판매기 자체가 Client가 되고 코인 입력 이벤트를 받습니다.
- Handler 프로토콜에는 handleCoinValidation(_:) 메서드와 다음 속성이 필요합니다.
- Concrete Handler는 동전 검증기일 것입니다. 그들은 동전의 무게와 지름과 같은 특정 기준에 따라 알려지지 않은 동전이 유효한지 여부를 판단하고, 이를 사용하여 Penny와 같은 알려진 동전 유형으로 구분지을 것입니다.
주의 사항
Chain of Responsibility 패턴은 이벤트를 처리할 것인지 여부를 매우 빠르게 결정할 수 있는 Handler에 가장 적합합니다. 이벤트를 다음 Handler로 전달하는 속도가 느린 Handler Chain을 만들 때는 주의해야합니다. 처리되지 않는 요청이 있을 수 있지만, 요청이 진행되는 동안에는 이걸 알 수 없습니다. 체인의 끝까지 가야 알 수 있기 때문이죠.
또한 이벤트를 처리할 수 없는 경우 어떻게 되는지도 고려해야 합니다. nil을 반환하거나, 오류를 발생시키거나, 다른 조치를 취할지 생각해보아야합니다. 시스템을 적절하게 계획할 수 있도록 먼저 해당 정보를 명확하게 해야 합니다.
이벤트를 둘 이상의 Handler에서 처리해야 하는지 여부를 고려해야 합니다. 패턴의 변형으로 동일한 이벤트를 처리할 수 있는 첫 번째 이벤트에서 중지한 다음 응답 객체 배열을 반환하는 대신 모든 Handler로 전달하여 이를 검증할 수 있습니다.
체인을 잘 못 만들 경우 사이클이 발생할 수 있습니다.
정리
- Chain of Responsibility을 사용하면 여러 Handler 중 하나가 이벤트를 처리할 수 있습니다. Client, Handler 프로토콜 및 Concrete Handler 세 가지 유형을 포함합니다.
- Client는 이벤트를 수락하고 해당 이벤트를 Handler 프로토콜의 인스턴스로 전달합니다. Handler 프로토콜은 각 Concrete Handler에서 구현되는 필수 메서드와 프로퍼티를 정의합니다. 각 Concrete Handler는 이벤트를 수락하고 다시 이벤트를 처리하거나 다음 Handler로 전달하는 것 처럼 요청 처리 순서를 제어할 수 있습니다.
- 결합도를 감소시킵니다. 체인에 존재하는 객체들은 요청을 처리할 것인지 넘길 것인지만 판단하면 되고, 체인의 다른 객체들이 뭘 하는지 알 필요가 없습니다. 물론 이건 Client도 몰라도 됩니다.
- 따라서, 이 패턴은 관련 Handler 그룹을 정의하며, 각 Handler가 처리할 수 있는 이벤트 유형에 따라 달라집니다. 새로운 유형의 이벤트를 처리해야 하는 경우 새 Concrete Handler를 작성하기만 하면 됩니다.
- Single Responsibility Principle(단일 책임 원칙)을 지킬 수 있습니다. 작업을 수행하는 클래스, 작업을 호출하는 클래스를 분리할 수 있습니다.
- Open, Closed Principle(개방/폐쇄 원칙)을 지킬 수 있습니다. 기존 Client의 코드를 바꾸지 않고 새로운 Handler를 앱에 추가할 수 있습니다.