HeadFirst设计模式学习之责任链模式

发布时间 2023-09-21 18:45:23作者: Chimengmeng

【一】介绍引入

  • 当你想要让一个以上的对象有机会能够处理某个请求的时候,就使用责任链模式(Chain of Responsibility Pattern)。

【二】场景引入

  • 自从推出Java版本的糖果机之后,万能糖果公司收到的电子邮件数量已超出他们所能处理的范围。
  • 据他们自己分析,所收到的电子邮件有四类:
    • 其一、Fans寄来的信,他们喜欢新推出的l in 10游戏;
    • 其二,父母寄来的信,抱怨他们的孩子沉溺于这个游戏;
    • 其三,店家寄来的信,他们希望能够在某些地方也摆设糖果机;
    • 其四,垃圾邮件。
  • 所有Fans的邮件都需要直接送到CEO手上,所有的抱怨邮件则是送给法律部门,而所有的新机器请求邮件则交给业务部门,至于垃圾邮件当然是删除了事。

【三】任务需求

  • 万能糖果公司已经写了一些人工智能过滤程序,这些程序很厉害,它们会分辨邮件是属于上述哪一类
  • 但是他们需要你构造一个设计——使用这个过滤程序处理收到的邮件。

【四】如何使用责任链模式

  • 责任链模式是一种行为设计模式,用于构建一条对象链,每个对象都包含了对请求的处理方式。
    • 当一个请求被发送到这个链上,每个对象都会依次尝试处理请求,直到其中一个对象处理成功或者所有对象都不能处理为止。
  • 责任链模式的核心思想是将请求的发送者和接收者解耦,使多个对象都有机会处理请求,避免了将请求的发送者与接收者紧密耦合在一起。
  • 责任链模式通常包括以下角色
    • Handler(处理者): 定义了处理请求的接口,通常包括一个处理方法。具体的处理者对象实现这个接口,并决定是否能够处理请求,如果能够处理,则进行处理;如果不能处理,则将请求传递给下一个处理者。
    • ConcreteHandler(具体处理者): 具体的处理者对象,实现了Handler接口,负责处理请求。如果自己能够处理请求,则进行处理;否则,将请求传递给下一个处理者。
    • Client(客户端): 创建处理者链并向链中的第一个处理者发送请求。

image-20230921101200413

【五】责任链模式的优点

  • 将请求的发送者和接受者解耦。
  • 可以简化你的对象,因为它不需要知道链的结构。
  • 通过改变链内的成员或调动它们的次序,允许你动态地新增或者删除责任。
  • 可以避免请求发送者需要知道接收者的详细信息,降低了耦合度。

【六】责任链模式的用途和缺点

  • 经常被使用在窗口系统中,处理鼠标和键盘之类的事件。
  • 并不保证请求一定会被执行:如果没有任何象处理它的话,它可能会落到链尾端之外(这可以是优点也可以是缺点)。
  • 可能不容易观察运行时的特征,有碍于除错。

【七】代码演示

【1】理论

  • Handler

    • successor
    • handleRequest()
  • SpanHandler

    • handleRequest()
  • FanHandler

    • handleRequest()
  • ComplaintHandler

    • handleRequest()
  • NewLocalHandler

    • handleRequest()
  • 链中的每个对象扮演处理器,并且都有一个后继对象

  • 如果他可以处理请求,就进行处理;否则把请求传给后继者

  • 当收到电子邮件的时候,它会被送进第一个处理器,也就是SpamHandler。如果SpamHandler无法处理,就将它传给FanHandler。依次类推……

【2】Java代码

// 定义邮件处理器接口
public interface EmailHandler {
    void setNextHandler(EmailHandler handler);
    void handleEmail(Email email);
}

// 实现垃圾邮件处理器
public class SpamHandler implements EmailHandler {
    private EmailHandler nextHandler;

    @Override
    public void setNextHandler(EmailHandler handler) {
        this.nextHandler = handler;
    }

    @Override
    public void handleEmail(Email email) {
        if (isSpam(email)) {
            System.out.println("Deleted spam email: " + email.getContent());
        } else if (nextHandler != null) {
            nextHandler.handleEmail(email);
        }
    }

    private boolean isSpam(Email email) {
        // 判断邮件是否是垃圾邮件的逻辑
        // 这里只是一个示例,实际逻辑需要根据具体情况编写
        return email.getContent().contains("spam");
    }
}

// 实现Fans邮件处理器
public class FansHandler implements EmailHandler {
    private EmailHandler nextHandler;

    @Override
    public void setNextHandler(EmailHandler handler) {
        this.nextHandler = handler;
    }

    @Override
    public void handleEmail(Email email) {
        if (isFansEmail(email)) {
            System.out.println("Forwarded fans email to CEO: " + email.getContent());
        } else if (nextHandler != null) {
            nextHandler.handleEmail(email);
        }
    }

    private boolean isFansEmail(Email email) {
        // 判断邮件是否是Fans邮件的逻辑
        // 这里只是一个示例,实际逻辑需要根据具体情况编写
        return email.getContent().contains("fans");
    }
}

// 实现抱怨邮件处理器
public class ComplaintHandler implements EmailHandler {
    private EmailHandler nextHandler;

    @Override
    public void setNextHandler(EmailHandler handler) {
        this.nextHandler = handler;
    }

    @Override
    public void handleEmail(Email email) {
        if (isComplaintEmail(email)) {
            System.out.println("Forwarded complaint email to legal department: " + email.getContent());
        } else if (nextHandler != null) {
            nextHandler.handleEmail(email);
        }
    }

    private boolean isComplaintEmail(Email email) {
        // 判断邮件是否是抱怨邮件的逻辑
        // 这里只是一个示例,实际逻辑需要根据具体情况编写
        return email.getContent().contains("complaint");
    }
}

// 实现新机器请求邮件处理器
public class NewMachineRequestHandler implements EmailHandler {
    private EmailHandler nextHandler;

    @Override
    public void setNextHandler(EmailHandler handler) {
        this.nextHandler = handler;
    }

    @Override
    public void handleEmail(Email email) {
        if (isNewMachineRequestEmail(email)) {
            System.out.println("Forwarded new machine request email to business department: " + email.getContent());
        } else if (nextHandler != null) {
            nextHandler.handleEmail(email);
        }
    }

    private boolean isNewMachineRequestEmail(Email email) {
        // 判断邮件是否是新机器请求邮件的逻辑
        // 这里只是一个示例,实际逻辑需要根据具体情况编写
        return email.getContent().contains("new machine request");
    }
}

// 定义Email类
public class Email {
    private String content;

    public Email(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 创建邮件处理器
        EmailHandler spamHandler = new SpamHandler();
        EmailHandler fansHandler = new FansHandler();
        EmailHandler complaintHandler = new ComplaintHandler();
        EmailHandler newMachineRequestHandler = new NewMachineRequestHandler();

        // 构建责任链
        spamHandler.setNextHandler(fansHandler);
        fansHandler.setNextHandler(complaintHandler);
        complaintHandler.setNextHandler(newMachineRequestHandler);

        // 模拟收到不同类型的邮件并处理
        Email email1 = new Email("This is a fans email.");
        Email email2 = new Email("This is a complaint email.");
        Email email3 = new Email("This is a spam email.");
        Email email4 = new Email("This is a new machine request email.");

        spamHandler.handleEmail(email1);
        spamHandler.handleEmail(email2);
        spamHandler.handleEmail(email3);
        spamHandler.handleEmail(email4);
    }
}

image-20230921103720605

【2】Python代码

# 定义邮件处理器基类
class EmailHandler:
    def __init__(self):
        self.next_handler = None

    def set_next_handler(self, handler):
        self.next_handler = handler

    def handle_email(self, email):
        pass

# 实现垃圾邮件处理器
class SpamHandler(EmailHandler):
    def handle_email(self, email):
        if "spam" in email.content:
            print("Deleted spam email: " + email.content)
        elif self.next_handler:
            self.next_handler.handle_email(email)

# 实现Fans邮件处理器
class FansHandler(EmailHandler):
    def handle_email(self, email):
        if "fans" in email.content:
            print("Forwarded fans email to CEO: " + email.content)
        elif self.next_handler:
            self.next_handler.handle_email(email)

# 实现抱怨邮件处理器
class ComplaintHandler(EmailHandler):
    def handle_email(self, email):
        if "complaint" in email.content:
            print("Forwarded complaint email to legal department: " + email.content)
        elif self.next_handler:
            self.next_handler.handle_email(email)

# 实现新机器请求邮件处理器
class NewMachineRequestHandler(EmailHandler):
    def handle_email(self, email):
        if "new machine request" in email.content:
            print("Forwarded new machine request email to business department: " + email.content)
        elif self.next_handler:
            self.next_handler.handle_email(email)

# 定义Email类
class Email:
    def __init__(self, content):
        self.content = content

# 客户端代码
if __name__ == "__main__":
    # 创建邮件处理器
    spam_handler = SpamHandler()
    fans_handler = FansHandler()
    complaint_handler = ComplaintHandler()
    new_machine_request_handler = NewMachineRequestHandler()

    # 构建责任链
    spam_handler.set_next_handler(fans_handler)
    fans_handler.set_next_handler(complaint_handler)
    complaint_handler.set_next_handler(new_machine_request_handler)

    # 模拟收到不同类型的邮件并处理
    email1 = Email("This is a fans email.")
    email2 = Email("This is a complaint email.")
    email3 = Email("This is a spam email.")
    email4 = Email("This is a new machine request email.")

    spam_handler.handle_email(email1)
    spam_handler.handle_email(email2)
    spam_handler.handle_email(email3)
    spam_handler.handle_email(email4)

image-20230921104557605

【八】实战项目中的应用--实现用户购票责任链验证

【1】购票请求验证引入

  • 在实际购票业务场景中,用户发起一次购票请求后,购票接口在真正完成创建订单和扣减余票行为前,需要验证当前请求中的参数是否正常请求,或者说是否满足购票情况。
  • 购票请求用户传递的参数是否为空
    • 比如:车次 ID、乘车人、出发站点、到达站点等。
  • 购票请求用户传递的参数是否正确
    • 比如:车次 ID 是否存在、出发和到达站点是否存在等。
  • 需要购票的车次是否满足乘车人的数量
    • 也就是列车对应座位的余量是否充足。
  • 乘客是否已购买当前车次,或者乘客是否已购买当天时间冲突的车次。
  • ...

【2】思路引入

  • java
public TicketPurchaseRespDTO purchaseTickets(PurchaseTicketReqDTO requestParam) {
	// 购票请求用户传递的参数是否为空
	// 购票请求用户传递的参数是否正确
	// 需要购票的车次是否满足乘车人的数量
	// 乘客是否已购买当前车次,或者乘客是否已购买当天时间冲突的车次
	// ......
}
  • python
from typing import Dict, List


class TicketPurchaseRespDTO:
    def __init__(self, success: bool, message: str, data: Dict):
        self.success = success
        self.message = message
        self.data = data
        ...


class TicketPurchaseReqDTO:
    def __init__(self, user_id: int, train_id: int, passengers: List[int]):
        self.user_id = user_id
        self.train_id = train_id
        self.passengers = passengers
        ...


class TicketPurchaseService:
    def purchase_tickets(self, request_param: TicketPurchaseReqDTO) -> TicketPurchaseRespDTO:
        # 购票请求用户传递的参数是否为空
        # 购票请求用户传递的参数是否正确
        # 需要购票的车次是否满足乘车人的数量
        # 乘客是否已购买当前车次,或者乘客是否已购买当天时间冲突的车次
        # 其他购票逻辑处理
        # 检查车次容量是否足够
        # 检查购票冲突
        # ...
        ...


def main():
    purchase_service = TicketPurchaseService()
    request_param = TicketPurchaseReqDTO(user_id=1, train_id=123, passengers=[101, 102])
    response = purchase_service.purchase_tickets(request_param)
    print(f"Purchase Result: {response.success}, Message: {response.message}, Data: {response.data}")

【3】流程及问题

  • 解决前置校验需求需要实现一堆逻辑,常常需要写上几百上千行代码。
  • 并且,上面的代码不具备开闭原则,以及代码扩展性,整体来说复杂且臃肿。
  • 为了避免这种坏代码味道,我们可以运用责任链设计模式,对购票验证逻辑进行抽象。

【4】责任链模式引入

(1)什么是责任链模式

  • 在责任链模式中,多个处理器依次处理同一个请求。
  • 一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B 处理器处理完后再传递给 C 处理器,以此类推,形成一个链条
  • 链条上的每个处理器各自承担各自的处理职责。

image.png

(2)责任链模式优点

  • 责任链模式的优点在于,它可以动态地添加、删除和调整处理者对象,从而灵活地构建处理链。
  • 同时,它也避免了请求发送者和接收者之间的紧耦合,增强了系统的灵活性和可扩展性。

【5】责任链优化项目

(1)定义购票过滤接口

package org.opengoofy.index12306.biz.ticketservice.service.handler.ticket.filter.purchase;

import org.opengoofy.index12306.biz.ticketservice.common.enums.TicketChainMarkEnum;
import org.opengoofy.index12306.biz.ticketservice.dto.req.PurchaseTicketReqDTO;
import org.opengoofy.index12306.framework.starter.designpattern.chain.AbstractChainHandler;

/**
 * 列车购买车票过滤器
 */
public interface TrainPurchaseTicketChainFilter<T extends PurchaseTicketReqDTO> extends AbstractChainHandler<PurchaseTicketReqDTO> {

    @Override
    default String mark() {
        return TicketChainMarkEnum.TRAIN_PURCHASE_TICKET_FILTER.name();
    }
}
  • 这段代码定义了一个购票过滤接口TrainPurchaseTicketChainFilter,它继承了AbstractChainHandler接口,并提供了默认实现方法mark(),用于返回责任链的标识。
  • 这个接口将在不同的购票过滤器中实现。

(2)实现列车购买过滤器实现

/**
 * 购票流程过滤器之验证参数必填
 */
@Component
public class TrainPurchaseTicketParamNotNullChainHandler implements TrainPurchaseTicketChainFilter<PurchaseTicketReqDTO> {

    @Override
    public void handler(PurchaseTicketReqDTO requestParam) {
    	// ......
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

/**
 * 购票流程过滤器之验证参数是否有效
 * 验证参数有效这个流程会大量交互缓存,为了优化性能需要使用 Lua。为了方便大家理解流程,这里使用多次调用缓存
 */
@Component
@RequiredArgsConstructor
public class TrainPurchaseTicketParamVerifyChainHandler implements TrainPurchaseTicketChainFilter<PurchaseTicketReqDTO> {

    private final TrainMapper trainMapper;
    private final TrainStationMapper trainStationMapper;
    private final DistributedCache distributedCache;

    @Override
    public void handler(PurchaseTicketReqDTO requestParam) {
        // ......
    }

    @Override
    public int getOrder() {
        return 10;
    }
}

/**
 * 购票流程过滤器之验证列车站点库存是否充足
 */
@Component
@RequiredArgsConstructor
public class TrainPurchaseTicketParamStockChainHandler implements TrainPurchaseTicketChainFilter<PurchaseTicketReqDTO> {

    private final SeatMarginCacheLoader seatMarginCacheLoader;
    private final DistributedCache distributedCache;

    @Override
    public void handler(PurchaseTicketReqDTO requestParam) {
        // ......
    }

    @Override
    public int getOrder() {
        return 20;
    }
}

/**
 * 购票流程过滤器之验证乘客是否重复购买
 */
@Component
@RequiredArgsConstructor
public class TrainPurchaseTicketRepeatChainHandler implements TrainPurchaseTicketChainFilter<PurchaseTicketReqDTO> {

    @Override
    public void handler(PurchaseTicketReqDTO requestParam) {
        // ......
    }

    @Override
    public int getOrder() {
        return 30;
    }
}
  • 购票过滤器的实现包括了验证参数是否为空、验证参数是否有效、验证列车站点库存是否充足、验证乘客是否重复购买等。
  • 每个过滤器实现了TrainPurchaseTicketChainFilter接口,并根据自身的业务逻辑实现了handler方法和getOrder方法。
  • 这些过滤器会在责任链中按照getOrder方法返回的值排序执行。

(3)购票流程使用过滤器

private final AbstractChainContext<PurchaseTicketReqDTO> purchaseTicketAbstractChainContext;

@Override
@Transactional(rollbackFor = Throwable.class)
public TicketPurchaseRespDTO purchaseTickets(PurchaseTicketReqDTO requestParam) {
    // 责任链模式,验证 0:参数必填 1:参数正确性 2:列车车次余量是否充足 3:乘客是否已买当前车次等
    purchaseTicketAbstractChainContext.handler(TicketChainMarkEnum.TRAIN_PURCHASE_TICKET_FILTER.name(), requestParam);
	// ......
}
  • 购票服务在实际调用时,使用了责任链模式来处理购票请求。
  • 首先创建了一个TicketPurchaseService的实例,然后通过purchaseTicketAbstractChainContext调用责任链中的过滤器,传递购票请求参数,并按照责任链的执行顺序依次处理请求。

【6】购票责任链实现原理

(1)运行时获取责任链具体实现类

  • 方法位置:org.opengoofy.index12306.biz.ticketservice.service.impl.TicketServiceImpl#purchaseTickets
private final Map<String, List<AbstractChainHandler>> abstractChainHandlerContainer = new HashMap<>();

/**
 * 责任链组件执行
 *
 * @param mark         责任链组件标识
 * @param requestParam 请求参数
 */
public void handler(String mark, T requestParam) {
    // 通过 mark 获取到本次需要执行的责任链组件
    List<AbstractChainHandler> abstractChainHandlers = abstractChainHandlerContainer.get(mark);
    // 获取为空抛出异常
    if (CollectionUtils.isEmpty(abstractChainHandlers)) {
        throw new RuntimeException(String.format("[%s] Chain of Responsibility ID is undefined.", mark));
    }
    // 获取到的责任链组件依次执行
    abstractChainHandlers.forEach(each -> each.handler(requestParam));
}
  • 责任链模式的实现原理在AbstractChainContext类中,它实现了CommandLineRunner接口,表示在Spring Boot启动完成后执行的回调函数。

(2)初始化责任链容器

// CommandLineRunner:SpringBoot 启动完成后执行的回调函数
public final class AbstractChainContext<T> implements CommandLineRunner {

    // 存储责任链组件实现和责任链业务标识的容器
    // 比如:Key:购票验证过滤器 Val:HanlderA、HanlderB、HanlderC、......
    private final Map<String, List<AbstractChainHandler>> abstractChainHandlerContainer = new HashMap<>();

    @Override
    public void run(String... args) throws Exception {
        // 调用 SpirngIOC 工厂获取 AbstractChainHandler 接口类型的 Bean
        Map<String, AbstractChainHandler> chainFilterMap = ApplicationContextHolder
                .getBeansOfType(AbstractChainHandler.class);
        chainFilterMap.forEach((beanName, bean) -> {
            // 获取 mark(责任链业务标识)的处理器集合
            List<AbstractChainHandler> abstractChainHandlers = abstractChainHandlerContainer.get(bean.mark());
            // 如果不存在则创建一个集合
            if (CollectionUtils.isEmpty(abstractChainHandlers)) {
                abstractChainHandlers = new ArrayList();
            }
            // 添加到处理器集合中
            abstractChainHandlers.add(bean);
            // 对处理器集合执行顺序进行排序
            List<AbstractChainHandler> actualAbstractChainHandlers = abstractChainHandlers.stream()
                    .sorted(Comparator.comparing(Ordered::getOrder))
                    .collect(Collectors.toList());
            // 存入容器等待被运行时调用
            abstractChainHandlerContainer.put(bean.mark(), actualAbstractChainHandlers);
        });
    }
}
  • run方法中,它通过Spring的IoC容器获取所有实现了AbstractChainHandler接口的Bean,然后根据它们的mark方法返回的责任链业务标识将它们分组存储在abstractChainHandlerContainer容器中。
  • 这样,在运行时,可以根据具体的业务标识获取到对应的责任链处理器,并按照它们的getOrder方法返回的值排序执行。

【7】Python代码复写项目(伪代码)

from typing import List, Dict


# 定义购票响应DTO
class TicketPurchaseRespDTO:
    def __init__(self, success: bool, message: str, data: Dict):
        self.success = success
        self.message = message
        self.data = data


# 定义购票请求DTO
class TicketPurchaseReqDTO:
    def __init__(self, user_id: int, train_id: int, passengers: List[int]):
        self.user_id = user_id
        self.train_id = train_id
        self.passengers = passengers


# 定义购票过滤器接口
class TrainPurchaseTicketChainFilter:
    def mark(self):
        pass

    def handler(self, request_param: TicketPurchaseReqDTO):
        pass


# 实现列车购买过滤器 - 验证参数必填
class TrainPurchaseTicketParamNotNullChainHandler(TrainPurchaseTicketChainFilter):
    def mark(self):
        return "TRAIN_PURCHASE_TICKET_FILTER"

    def handler(self, request_param: TicketPurchaseReqDTO):
        # 验证参数是否为空
        if not request_param:
            return TicketPurchaseRespDTO(False, "参数为空", None)
        # 其他参数验证逻辑
        # ......

    def getOrder(self):
        return 0


# 实现列车购买过滤器 - 验证参数有效性
class TrainPurchaseTicketParamVerifyChainHandler(TrainPurchaseTicketChainFilter):
    def __init__(self, train_mapper, train_station_mapper, distributed_cache):
        self.train_mapper = train_mapper
        self.train_station_mapper = train_station_mapper
        self.distributed_cache = distributed_cache

    def mark(self):
        return "TRAIN_PURCHASE_TICKET_FILTER"

    def handler(self, request_param: TicketPurchaseReqDTO):
        # 验证参数有效性
        # 具体逻辑根据业务需求实现
        # ......
        ...

    def getOrder(self):
        return 10


# 实现列车购买过滤器 - 验证列车站点库存是否充足
class TrainPurchaseTicketParamStockChainHandler(TrainPurchaseTicketChainFilter):
    def __init__(self, seat_margin_cache_loader, distributed_cache):
        self.seat_margin_cache_loader = seat_margin_cache_loader
        self.distributed_cache = distributed_cache

    def mark(self):
        return "TRAIN_PURCHASE_TICKET_FILTER"

    def handler(self, request_param: TicketPurchaseReqDTO):
        # 验证列车站点库存是否充足
        # 具体逻辑根据业务需求实现
        # ......
        ...

    def getOrder(self):
        return 20


# 实现列车购买过滤器 - 验证乘客是否重复购买
class TrainPurchaseTicketRepeatChainHandler(TrainPurchaseTicketChainFilter):
    def mark(self):
        return "TRAIN_PURCHASE_TICKET_FILTER"

    def handler(self, request_param: TicketPurchaseReqDTO):
        # 验证乘客是否重复购买
        # 具体逻辑根据业务需求实现
        # ......
        ...

    def getOrder(self):
        return 30


# 责任链模式上下文
class AbstractChainContext:
    def __init__(self):
        self.handlers = []

    def add_handler(self, handler):
        self.handlers.append(handler)

    def handler(self, request_param: TicketPurchaseReqDTO):
        for handler in self.handlers:
            result = handler.handler(request_param)
            if not result.success:
                return result  # 停止处理链

def main():
    # 创建购票请求参数
    request_param = TicketPurchaseReqDTO(user_id=1, train_id=123, passengers=[101, 102])

    # 创建责任链上下文
    chain_context = AbstractChainContext()

    # 添加购票过滤器到责任链上下文
    chain_context.add_handler(TrainPurchaseTicketParamNotNullChainHandler())
    chain_context.add_handler(TrainPurchaseTicketParamVerifyChainHandler(train_mapper, train_station_mapper, distributed_cache))
    chain_context.add_handler(TrainPurchaseTicketParamStockChainHandler(seat_margin_cache_loader, distributed_cache))
    chain_context.add_handler(TrainPurchaseTicketRepeatChainHandler())

    # 开始责任链处理
    response = chain_context.handler(request_param)

    # 打印购票响应结果
    print(f"Purchase Result: {response.success}, Message: {response.message}, Data: {response.data}")


# 客户端代码示例
if __name__ == "__main__":
    main()