Hura! Mamy wreszcie przyjemność udostępnić wam polską wersję! Zapraszamy do przesyłania wiadomości z waszymi uwagami i informacjami o zauważonych błędach.
Łańcuch zobowiązań

Ł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: Konkretny obsługujący

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
Na podstawie: Golang By Example

Łańcuch zobowiązań w innych językach

Wzorce projektowe: Łańcuch zobowiązań w języku Java Wzorce projektowe: Łańcuch zobowiązań w języku C# Wzorce projektowe: Łańcuch zobowiązań w języku C++ Wzorce projektowe: Łańcuch zobowiązań w języku PHP Wzorce projektowe: Łańcuch zobowiązań w języku Python Wzorce projektowe: Łańcuch zobowiązań w języku Ruby Wzorce projektowe: Łańcuch zobowiązań w języku Swift Wzorce projektowe: Łańcuch zobowiązań w języku TypeScript