[设计模式]行为型-责任链模式

简介

责任链模式(Chain of Responsibility Pattern)允许开发者将请求沿着链进行发送,直到其中一个处理者对象对其进行处理。

责任链模式的角色说明:

  • 处理者(Handler):声明所有具体处理类的通用接口。该接口通常仅包含一个方法,用于处理请求,但有时还会包含一个用于设置下一个具体处理者对象的方法。
  • 基础处理者(Base Handler):可选类,可将所有处理者对象共用的样本代码放置在其中
  • 具体处理者(ConcreteHandler)
  • 客户端(Client)

使用场景

  • 责任链模式适用于有多个候选选项处理相同请求的情形,适用于不希望客户端选择接受者(因为多个对象都可以处理请求)的情形,还适用于需要将客户端与接受者解耦的情形,客户端只需链中的首个元素。
  • 核心使用场景是当需要多个对象按顺序处理请求,且处理流程可能动态变化时。
  • 多级审批/处理流程,例如医院就诊流程,挂号->诊室->药房->收费
  • 需要运行时动态调整处理顺序或增减处理器。通过指定下一节点的方法来灵活调整责任链。
  • 需要多个过滤器按顺序处理数据流

优点

  • 请求处理顺序可控。
  • 符合单一职责原则。可以对发起操作和执行操作的类进行解耦。
  • 符合开闭原则。开发者可以在不更改现有代码的情况下在程序中添加处理者对象。
  • 提高对象分配职责的灵活性。通过更改链中的成员或更改顺序,允许动态添加或删除处理者对象。增加请求处理新类非常方便。
  • 责任链模式可以简化对象,对象不需要知道链结构。

缺点

  • 部分请求可能未被处理。如果前一个处理者没能正常处理,后一个处理者就有可能无法接收到请求。
  • 调试复杂度高,异常发生时可能需要逐级回溯链路。
  • 有循环引用风险。
  • 有性能损耗,可能每个处理者对象都要经手判断请求。

示例代码

Go

package chainofresponsibility

import "fmt"

// 基础处理者类 Patient 病人类
type Patient struct {
	Name              string
	RegistrationDone  bool
	ClinicCheckUpDone bool
	DrugstoreDone     bool
	PaymentDone       bool
}

// 处理者接口 department 科室接口
type department interface {
	Operate(*Patient)
	SetNext(department)
}

// 具体处理者, 前台类
type Reception struct {
	next department
}

func (r *Reception) Operate(p *Patient) {
	if p.RegistrationDone {
		fmt.Println("Patient already registered")
		r.next.Operate(p)
		return
	}
	fmt.Println("Reception registering patient")
	p.RegistrationDone = true
	r.next.Operate(p)
}

func (r *Reception) SetNext(next department) {
	r.next = next
}

// 具体处理者, 诊室类
type Clinic struct {
	next department
}

func (c *Clinic) Operate(p *Patient) {
	if p.ClinicCheckUpDone {
		fmt.Println("Patient already checked up")
		c.next.Operate(p)
		return
	}
	fmt.Println("Clinic checking up patient")
	p.ClinicCheckUpDone = true
	c.next.Operate(p)
}

func (c *Clinic) SetNext(next department) {
	c.next = next
}

// 具体处理者, 药房类
type Drugstore struct {
	next department
}

func (d *Drugstore) Operate(p *Patient) {
	if p.DrugstoreDone {
		fmt.Println("Patient already drugstored")
		d.next.Operate(p)
		return
	}
	fmt.Println("Drugstore giving medicine to patient")
	p.DrugstoreDone = true
	d.next.Operate(p)
}

func (d *Drugstore) SetNext(next department) {
	d.next = next
}

// 具体处理者, 收银台类
type Cashier struct {
	next department
}

func (c *Cashier) Operate(p *Patient) {
	if p.PaymentDone {
		fmt.Println("Patient already paid")
		return
	}
	fmt.Println("Cashier taking money from patient")
	p.PaymentDone = true
}

func (c *Cashier) SetNext(next department) {
	c.next = next
}

测试代码

package chainofresponsibility

import (
    "testing"
)

func TestChainOfResponsibility(t *testing.T) {
	cashier := &Cashier{}

	drugstore := &Drugstore{}
	clinic := &Clinic{}
	reception := &Reception{}

	reception.SetNext(clinic)
	clinic.SetNext(drugstore)
	drugstore.SetNext(cashier)

	reception.Operate(&Patient{Name: "Alan"})
}

Python

from abc import ABCMeta, abstractmethod


class Patient:
    """基础处理类"""

    def __init__(
        self,
        name: str,
        is_registration: bool = False,
        is_clinic_check: bool = False,
        is_drugstore: bool = False,
        is_payment: bool = False,
    ):
        self.name = name
        self.is_registration = is_registration
        self.is_clinic_check = is_clinic_check
        self.is_drugstore = is_drugstore
        self.is_payment = is_payment


class Department(metaclass=ABCMeta):
    """抽象基类, 科室接口"""

    def __init__(self):
        self.next: Department = None

    @abstractmethod
    def operate(self, patient: Patient):
        pass

    def set_next(self, depr):
        if not issubclass(type(depr), Department):
            raise Exception("Invalid Department")
        self.next = depr


class Registration(Department):
    """具体处理类, 科室"""

    def __init__(self):
        super().__init__()

    def operate(self, p: Patient):
        if p.is_registration:
            print(f"{p.name} is already registered")
            self.next.operate(p)
            return
        print(f"{p.name} is registering")
        p.is_registration = True
        self.next.operate(p)


class Clinic(Department):
    """具体处理类, 诊室"""

    def __init__(self):
        super().__init__()

    def operate(self, p: Patient):
        if p.is_clinic_check:
            print(f"{p.name} is already checked at the clinic")
            self.next.operate(p)
            return
        print(f"{p.name} is checking clinic")
        p.is_clinic_check = True
        self.next.operate(p)


class DrugStore(Department):
    """具体处理类, 药房"""

    def __init__(self):
        super().__init__()

    def operate(self, p: Patient):
        if p.is_drugstore:
            print(f"{p.name} is already given drugs")
            self.next.operate(p)
            return
        print(f"{p.name} is checking drugstore")
        p.is_drugstore = True
        self.next.operate(p)


class Cashier(Department):
    """具体处理类, 收费处"""

    def __init__(self):
        super().__init__()

    def operate(self, p: Patient):
        if p.is_payment:
            print(f"{p.name} is already paid")
            return
        print(f"{p.name} is paying")
        p.is_payment = True
        print(f"{p.name} is being released")


if __name__ == "__main__":
    registration = Registration()
    clinic = Clinic()
    drugstore = DrugStore()
    cashier = Cashier()

    registration.set_next(clinic)
    clinic.set_next(drugstore)
    drugstore.set_next(cashier)

    patient = Patient("John")
    registration.operate(patient)

参考

  • 廖显东《Go语言设计模式》
posted @ 2025-03-18 23:28  花酒锄作田  阅读(99)  评论(0)    收藏  举报