Весняний РОЗПРОДАЖ
Ланцюжок обов'язків

Ланцюжок обов'язків на Go

Ланцюжок обов’язків — це поведінковий патерн, що дозволяє передавати запит ланцюжком потенційних обробників до тих пір, поки один з них не обробить його.

Позбавляє від жорсткої прив’язки відправника запиту до одержувача, дозволяючи динамічно вибудовувати ланцюг з різних обробників.

Концептуальний приклад

Давайте розглянемо патерн Ланцюжок обов’язків на прикладі додатка лікарні. Шпиталь може мати різні приміщення, наприклад:

  • Приймальне відділення
  • Доктор
  • Кімната медикаментів
  • Касир

Коли пацієнт приходить до лікарні, перш за все він потрапляє у Приймальне відділення, звідти — до Доктора, потім у Кімнату медикаментів, після цього — до Касира, і так далі. Пацієнт проходить ланцюжком приміщень, в якому кожна ланка відправляє його далі відразу після виконання своєї функції.

Цей патерн можна застосовувати у випадках, коли для виконання одного запиту є кілька кандидатів, і коли ви не хочете, щоб клієнт сам вибирав виконавця. Важливо знати, що клієнта необхідно відгородити від виконавців, йому необхідно знати лише про існування першої ланки ланцюга.

Використовуючи приклад лікарні, пацієнт спершу потрапляє в Приймальне відділення. Потім, залежно від його стану, Приймальне відділення відправляє його до наступного виконавця у ланцюзі.

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
На основі: Golang By Example

Ланцюжок обов'язків іншими мовами програмування

Ланцюжок обов'язків на C# Ланцюжок обов'язків на C++ Ланцюжок обов'язків на Java Ланцюжок обов'язків на PHP Ланцюжок обов'язків на Python Ланцюжок обов'язків на Ruby Ланцюжок обов'язків на Rust Ланцюжок обов'язків на Swift Ланцюжок обов'язків на TypeScript