![Chain of Responsibility](/images/patterns/cards/chain-of-responsibility-mini.png?id=36d85eba8d14986f053123de17aac7a7)
Chain of Responsibility en Go
Chain of Responsibility es un patrón de diseño de comportamiento que permite pasar solicitudes a lo largo de la cadena de manejadores potenciales hasta que uno de ellos gestiona la solicitud.
El patrón permite que varios objetos gestionen la solicitud sin acoplar la clase emisora a las clases concretas de los receptores. La cadena puede componerse dinámicamente durante el tiempo de ejecución con cualquier manejador que siga una interfaz manejadora estándar.
Ejemplo conceptual
Veamos el patrón Chain of Responsibility con el caso de una aplicación de hospital. Un hospital puede tener varios departamentos, como:
- Recepción
- Consulta
- Sala de curas
- Caja
Cuando llega un paciente, primero pasa por recepción, después por la consulta del médico y después por la sala de curas, y por último por la caja (etcétera). El paciente pasa por una cadena de departamentos y cada uno de ellos envía al paciente un poco más allá en la cadena, una vez que se complete su función.
El patrón se aplica cuando hay varios candidatos para procesar la misma solicitud. Cuando no quieres que el cliente elija el receptor ya que varios objetos pueden gestionar la solicitud. Además, quieres desacoplar el cliente de los receptores. El cliente solo necesita conocer el primer elemento de la cadena.
Al igual que en el ejemplo del hospital, el paciente pasa primero por la recepción. Después, en base al estado del paciente, desde recepción lo envían al siguiente manejador de la cadena.
department.go: Interfaz manejador
package main
type Department interface {
execute(*Patient)
setNext(Department)
}
reception.go: Manipulador concreto
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: Manipulador concreto
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: Manipulador concreto
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: Manipulador concreto
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: Código cliente
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: Resultado de la ejecución
Reception registering patient
Doctor checking patient
Medical giving medicine to patient
Cashier getting money from patient patient