책임 연쇄 패턴은 핸들러 중 하나가 요청을 처리할 때까지 핸들러들의 체인(사슬)을 따라 요청을 전달할 수 있게 해주는 행동 디자인 패턴입니다.
이 패턴은 발신자 클래스를 수신자들의 구상 클래스들에 연결하지 않고도 여러 객체가 요청을 처리할 수 있도록 합니다. 체인은 표준 핸들러 인터페이스를 따르는 모든 핸들러와 런타임 때 동적으로 구성될 수 있습니다.
개념적인 예시
책임 연쇄 패턴을 병원 앱의 예시를 통해 살펴봅시다. 병원에는 다음과 같은 여러 부서가 있을 수 있습니다:
환자가 도착할 때마다 먼저 접수처에, 그다음에는 의사에게, 그다음에는 진료실에, 그다음에는 계산원(등)에게 이동합니다. 환자는 일련의 부서를 통해 보내지고 있으며, 각 부서의 기능이 완료될 때마다 환자를 사슬의 더 아래로 보냅니다.
이 패턴은 같은 요청을 처리할 후보가 여러 개일 때도 적용되며 또 요청을 처리할 수 있는 여러 객체가 있으므로 클라이언트가 수신자를 선택하지 않도록 할 때도 유용합니다. 또 다른 유용한 경우는 클라이언트와 수신기를 분리하려는 경우이며, 그러며 클라이언트는 체인의 첫 번째 요소만 알면 됩니다.
이 예시에서의 환자는 먼저 병원의 접수처에 갑니다. 그 후 접수처는 환자의 현재 상태에 따라 환자를 체인의 다음 핸들러로 보냅니다.
department.go: 핸들러 인터페이스
package main
type Department interface {
execute(*Patient)
setNext(Department)
}
reception.go: 구상 핸들러
package main
import "fmt"
type Reception struct {
next Department
}
func (r *Reception) execute(p *Patient) {
if p.registrationDone {
fmt.Println("Patient registration already done")
r.next.execute(p)
return
}
fmt.Println("Reception registering patient")
p.registrationDone = true
r.next.execute(p)
}
func (r *Reception) setNext(next Department) {
r.next = next
}
doctor.go: 구상 핸들러
package main
import "fmt"
type Doctor struct {
next Department
}
func (d *Doctor) execute(p *Patient) {
if p.doctorCheckUpDone {
fmt.Println("Doctor checkup already done")
d.next.execute(p)
return
}
fmt.Println("Doctor checking patient")
p.doctorCheckUpDone = true
d.next.execute(p)
}
func (d *Doctor) setNext(next Department) {
d.next = next
}
medical.go: 구상 핸들러
package main
import "fmt"
type Medical struct {
next Department
}
func (m *Medical) execute(p *Patient) {
if p.medicineDone {
fmt.Println("Medicine already given to patient")
m.next.execute(p)
return
}
fmt.Println("Medical giving medicine to patient")
p.medicineDone = true
m.next.execute(p)
}
func (m *Medical) setNext(next Department) {
m.next = next
}
cashier.go: 구상 핸들러
package main
import "fmt"
type Cashier struct {
next Department
}
func (c *Cashier) execute(p *Patient) {
if p.paymentDone {
fmt.Println("Payment Done")
}
fmt.Println("Cashier getting money from patient patient")
}
func (c *Cashier) setNext(next Department) {
c.next = next
}
patient.go
package main
type Patient struct {
name string
registrationDone bool
doctorCheckUpDone bool
medicineDone bool
paymentDone bool
}
main.go: 클라이언트 코드
package main
func main() {
cashier := &Cashier{}
//Set next for medical department
medical := &Medical{}
medical.setNext(cashier)
//Set next for doctor department
doctor := &Doctor{}
doctor.setNext(medical)
//Set next for reception department
reception := &Reception{}
reception.setNext(doctor)
patient := &Patient{name: "abc"}
//Patient visiting
reception.execute(patient)
}
output.txt: 실행 결과
Reception registering patient
Doctor checking patient
Medical giving medicine to patient
Cashier getting money from patient patient