![Łańcuch zobowiązań](/images/patterns/cards/chain-of-responsibility-mini.png?id=36d85eba8d14986f053123de17aac7a7)
Łańcuch zobowiązań w języku Go
Łańcuch zobowiązań to behawioralny wzorzec projektowy pozwalający przekazywać żądanie wzdłuż łańcucha potencjalnych obiektów obsługujących aż zostanie obsłużone.
W łańcuchu zobowiązań wiele obiektów może obsłużyć żądanie bez konieczności sprzęgania klas wysyłających je z konkretnymi klasami odbierającymi. Łańcuch można układać dynamicznie w trakcie działania programu z dowolnych obiektów obsługujących, wyposażonych w standardowy interfejs obsługi żądań.
Przykład koncepcyjny
Popatrzmy na wzorzec Łańcuch Zobowiązań przez pryzmat przykładu opartego na aplikacji do obsługi szpitala. Szpital może mieć wiele oddziałów:
- Recepcja
- Lekarz
- Gabinet zabiegowy
- Kasa
Każdy nowy pacjent najpierw jest przyjmowany w Recepcji, następnie przez Lekarza, potem udaje się do Gabinetu Zabiegowego, a na końcu do Kasy (i tak dalej). Pacjent pokonuje więc pewien łańcuch oddziałów, gdzie każdy z nich kieruje go dalej po wypełnieniu swojej funkcji.
Wzorzec można zastosować gdy mamy do czynienia z wieloma kandydatami do przetworzenia tego samego żądania oraz gdy nie chcemy, aby klient wybierał odbiorcę samodzielnie, gdyż wiele obiektów może obsłużyć jego żądanie. Dzięki temu unikamy też sprzężenia klienta z odbiorcami. Klient musi wiedzieć tylko o pierwszym ogniwie łańcucha.
Jak w przykładzie ze szpitalem, pacjent najpierw kieruje się do recepcji. Następnie, na podstawie określonego stanu pacjenta jest wysyłany do stosownego obsługującego w łańcuchu.
department.go: Interfejs obsługującego
package main
type Department interface {
execute(*Patient)
setNext(Department)
}
reception.go: Konkretny obsługujący
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: Konkretny obsługujący
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: Konkretny obsługujący
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: Konkretny obsługujący
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: Kod klienta
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: Wynik działania
Reception registering patient
Doctor checking patient
Medical giving medicine to patient
Cashier getting money from patient patient