![Chaîne de responsabilité](/images/patterns/cards/chain-of-responsibility-mini.png?id=36d85eba8d14986f053123de17aac7a7)
Chaîne de responsabilité en Go
La Chaîne de responsabilité est un patron de conception comportemental qui permet de faire circuler une demande tout au long d’une chaîne de handlers, jusqu’à ce que l’un d’entre eux la traite.
Ce patron permet à plusieurs objets de traiter une demande sans coupler la classe du demandeur aux classes concrètes des récepteurs. La chaîne peut être assemblée dynamiquement à l’exécution à l’aide de tout handler implémentant l’interface standard des handlers.
Exemple conceptuel
Regardons un peu la chaîne de responsabilité utilisée dans l’application d’un hôpital. Un hôpital peut être composé de plusieurs départements comme :
- La réception
- Les docteurs
- La salle de soins
- La caisse
Lorsqu’un patient arrive, il se rend d’abord à la réception, consulte ensuite un docteur, passe du temps dans la salle de soins et finit par aller à la caisse pour payer, etc. Le patient est envoyé à travers une chaîne de départements, et chacun d’entre eux expédie le patient plus loin dans la chaîne une fois qu’il a rempli son rôle.
Ce patron est utilisable lorsque plusieurs candidats vont effectuer la même demande. Il se montre très utile lorsque vous voulez empêcher le client de choisir son récepteur, car plusieurs objets peuvent gérer la demande. Il est également pratique si vous voulez découpler le client des récepteurs : le client n’aura besoin que de connaître le premier élément de la chaîne.
Si l’on reprend l’exemple de l’hôpital, le patient arrive d’abord à la réception. Ensuite, en fonction de son état, la réception l’envoie au prochain handler de la chaîne.
department.go: Interface handler
package main
type Department interface {
execute(*Patient)
setNext(Department)
}
reception.go: Handler concret
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: Handler concret
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: Handler concret
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: Handler concret
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: Code client
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: Résultat de l’exécution
Reception registering patient
Doctor checking patient
Medical giving medicine to patient
Cashier getting money from patient patient