【设计模式运用】责任链模式

介绍

责任链设计模式(Chain of Responsibility Pattern)是一种行为型设计模式,用于构建处理请求的对象链。在这种模式中,请求从链的一端开始,然后依次经过链中的各个处理器(或处理节点),直到找到能够处理请求的处理器为止。每个处理器都决定是否能够处理请求,如果可以处理,则处理请求,否则将请求传递给下一个处理器。这种方式可以实现请求的分发和处理,同时使代码更加灵活和可扩展。

责任链模式包含以下主要角色:

  1. 抽象处理器(Handler):定义一个处理请求的接口,通常包括一个处理方法(或处理请求的抽象方法),以及一个可选的后继处理器引用。具体的处理器类将实现这个接口。

  2. 具体处理器(ConcreteHandler):实现抽象处理器接口,在处理请求时,如果自己可以处理,则处理请求;否则将请求传递给下一个处理器。每个具体处理器知道自己的责任范围,并决定是否继续传递请求。

  3. 客户端(Client):创建请求对象并将其发送到责任链的第一个处理器。客户端通常不知道具体的处理器是如何处理请求的,它只需要将请求发送给责任链即可。

主要优点包括:

  1. 松耦合(Loose Coupling):责任链模式将请求的发送者和接收者解耦,使得发送者不需要知道具体的接收者,从而提高了系统的灵活性。

  2. 可扩展性:可以轻松地向责任链中添加新的处理者或修改现有的处理者,而不需要修改已有的代码,这有助于系统的扩展和维护。

  3. 职责分离:责任链模式将不同的职责分散到不同的处理者中,每个处理者只关注自己的职责,使系统更易理解和维护。

  4. 动态链:责任链可以在运行时动态构建,可以根据请求的类型和条件来决定责任链的组成,从而实现灵活的链路构建。

  5. 递归处理:责任链模式支持递归处理,当一个处理者无法处理请求时,可以将请求传递给下一个处理者,直到找到合适的处理者为止。

  6. 可过滤性:可以在责任链中的某个处理者中决定是否终止整个请求处理链,这样可以根据需要决定是否继续传递请求。

  7. 可定制性:每个具体的处理者可以根据需求自定义处理逻辑,从而实现不同的处理策略。

  8. 透明性:客户端无需关心请求的具体处理过程,只需要将请求发送到责任链的起始点即可,责任链内部的细节对客户端透明。

使用场景

  1. 审批流程: 一个典型的场景是请假审批流程。不同级别的管理者(如组长、经理、总经理)可以处理请假请求,每个级别的管理者负责处理自己级别以下的请求。如果一个管理者无法处理请求,请求会继续传递给下一个级别的管理者。

  2. 日志记录系统: 在日志记录系统中,可以创建多个日志处理器,每个处理器负责处理不同级别的日志信息。例如,一个处理器可以记录所有日志,另一个可以只记录错误日志。日志消息从高到低级别经过责任链中的处理器,每个处理器决定是否处理该消息。

  3. 权限验证: 在Web应用中,可以使用责任链模式来验证用户的权限。每个处理器可以检查用户的权限,并决定是否允许用户访问某个资源。如果一个处理器无法验证权限,请求会传递给下一个处理器。

  4. 异常处理: 在异常处理系统中,可以定义多个异常处理器,每个处理器负责处理不同类型的异常。当系统抛出异常时,异常会经过责任链中的处理器,每个处理器可以选择捕获并处理异常,或将异常传递给下一个处理器。

  5. 购物车订单处理: 在电子商务网站中,可以使用责任链模式来处理购物车订单。每个处理器可以检查订单中的商品是否有库存、是否符合促销规则等。如果一个处理器无法处理订单中的某个商品,它可以将该商品从订单中移除,并将订单传递给下一个处理器。

  6. 工作流程管理: 在工作流程管理系统中,可以使用责任链模式来定义不同的工作流程步骤。每个步骤可以有一个处理器,负责执行特定的任务。工作流程中的任务可以按照预定义的顺序依次执行。

  7. 请求过滤器: 在Web应用中,可以使用责任链模式来实现请求过滤器。每个过滤器可以处理特定类型的请求,例如身份验证、日志记录、跨域处理等。多个过滤器可以组成一个责任链,以依次处理请求。

  8. 链式调用: 在编程中,责任链模式也可以用于构建链式调用的API。每个方法可以返回一个包含下一个方法的对象,从而实现链式调用。

这些场景和案例展示了责任链设计模式的多样性和实用性。责任链模式允许您构建灵活、可扩展且可维护的系统,使不同的处理逻辑可以根据需要组合和配置。

示例

以下是一个使用Java代码示例来演示责任链设计模式的简单请假审批系统:

// 抽象处理器
abstract class Approver {
    protected Approver successor; // 后继处理者

    public void setSuccessor(Approver successor) {
        this.successor = successor;
    }

    public abstract void processLeaveApplication(LeaveApplication application);
}

// 具体处理器1:组长
class TeamLead extends Approver {
    public void processLeaveApplication(LeaveApplication application) {
        if (application.getDays() <= 1) {
            System.out.println("Team Lead approved leave for " + application.getDays() + " days");
        } else if (successor != null) {
            successor.processLeaveApplication(application);
        }
    }
}

// 具体处理器2:经理
class Manager extends Approver {
    public void processLeaveApplication(LeaveApplication application) {
        if (application.getDays() <= 5) {
            System.out.println("Manager approved leave for " + application.getDays() + " days");
        } else if (successor != null) {
            successor.processLeaveApplication(application);
        }
    }
}

// 具体处理器3:总经理
class GeneralManager extends Approver {
    public void processLeaveApplication(LeaveApplication application) {
        if (application.getDays() <= 10) {
            System.out.println("General Manager approved leave for " + application.getDays() + " days");
        } else {
            System.out.println("Leave application rejected for " + application.getDays() + " days");
        }
    }
}

// 请假申请
class LeaveApplication {
    private int days;

    public LeaveApplication(int days) {
        this.days = days;
    }

    public int getDays() {
        return days;
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建责任链
        Approver teamLead = new TeamLead();
        Approver manager = new Manager();
        Approver generalManager = new GeneralManager();

        teamLead.setSuccessor(manager);
        manager.setSuccessor(generalManager);

        // 提交请假申请
        LeaveApplication application1 = new LeaveApplication(1);
        teamLead.processLeaveApplication(application1);

        LeaveApplication application5 = new LeaveApplication(5);
        teamLead.processLeaveApplication(application5);

        LeaveApplication application10 = new LeaveApplication(10);
        teamLead.processLeaveApplication(application10);

        LeaveApplication application15 = new LeaveApplication(15);
        teamLead.processLeaveApplication(application15);
    }
}

在上面的示例中,我们创建了三个具体的处理器(TeamLead、Manager、GeneralManager),它们分别处理不同级别的请假申请。每个处理器都可以批准一定天数内的请假,否则将请求传递给下一个处理器。

Main类中,我们创建了责任链并提交了不同天数的请假申请,最终得到相应的批准结果。

这个示例展示了责任链设计模式的用法,它使得请求可以依次经过不同的处理者,每个处理者根据自己的规则来处理请求,从而实现了请求的分发和处理。

项目应用

背景:运营商号卡选号下单需要根据优先级和不同的规则匹配号池,匹配顺序是身份证生日->下单手机后四位->号池权重->随机选号,只要匹配到号码就返回,否则会继续匹配直到最后一个规则,基于面向对象的开闭原则和类的单一职责,也为了后续基于需求变动可以灵活拓展,故采用责任链设计模式。

  • 抽象处理器(Handler)
/**
 * @description 号卡下单匹配手机号处理器抽象类
 * @author youmu
 * @date 2023/8/10 10:23
 */
@Data
@Component
@Slf4j
public abstract class AbstractMatchPhoneHandler {

    public AbstractMatchPhoneHandler nextHandler;

    /**
     * @description 按优先级匹配手机号
     * @author youmu
     * @date 2023/8/10 10:25
     * @param pair 下单参数
     * @return 下一个处理器
     */
    public AbstractMatchPhoneHandler doFilter(Pair<PlaceSIMCardOrderRO, Product> pair) {
        String result = null;
        try{
            result = getMatchPhone(pair);
        }catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        pair.getLeft().setHandleNo(result);
        return doNextHandler(pair);
    }

    /**
     * @description 获取匹配手机号
     * @author youmu
     * @date 2023/8/10 14:07
     */
    public abstract String getMatchPhone(Pair<PlaceSIMCardOrderRO, Product> pair);

    /**
     * @description 执行下一个处理器
     * @author youmu
     * @date 2023/8/10 10:29
     * @param pair 下单参数
     * @return void
     */
    private AbstractMatchPhoneHandler doNextHandler(Pair<PlaceSIMCardOrderRO, Product> pair) {
        if (nextHandler != null && StringUtils.isBlank(pair.getLeft().getHandleNo())) {
            return nextHandler.doFilter(pair);
        }
        // 执行到最后一个处理器或匹配到手机号直接返回
        return nextHandler;
    }

}
  • 具体处理器(ConcreteHandler)

在责任链设计模式中,@Order 注解可以用来控制责任链中不同责任对象的执行顺序。责任链模式通常由多个处理器(责任对象)组成,每个处理器负责处理特定类型的请求,并且它们按照一定的顺序执行。

/**
 * @description 手机号匹配处理-身份证号生日
 * @author youmu
 * @date 2023/8/10 10:43
 */
@RequiredArgsConstructor
@Service
@Order(100)
@Slf4j
public class BirthdayMatchPhoneHandler extends AbstractMatchPhoneHandler {
    private final RedissonClient redissonClient;

    @Override
    public String getMatchPhone(Pair<PlaceSIMCardOrderRO, Product> pair) {
        String pid = pair.getRight().getSupplierConfig().split(",")[0];
        PlaceSIMCardOrderRO ro = pair.getLeft();
        List<PhoneInfoDTO> list = redissonClient.getList(CacheNamesConstant.JUNBO_PHONE_SORTED_NUM_LIST + "_" + pid + "_" + ro.getProvince() + "_" + ro.getCity());
        // 身份证出生月日
        String birthday = ro.getIdCardNo().substring(10,14);
        Optional<PhoneInfoDTO> optional = list.stream().filter(item->item.getPhoneNumber().contains(birthday)).findFirst();
        String result = optional.map(PhoneInfoDTO::getPhoneNumber).orElse(null);;
        log.info("【自动下单手机匹配】匹配身份证生日,pid={},province={},city={},result={}",pid,ro.getProvince(),ro.getCity(),result);
        return result;
    }
}
/**
 * @description 手机号匹配处理-权重
 * @author youmu
 * @date 2023/8/10 10:43
 */
@RequiredArgsConstructor
@Service
@Order(200)
@Slf4j
public class WeightMatchPhoneHandler extends AbstractMatchPhoneHandler {
    private final RedissonClient redissonClient;
    @Override
    public String getMatchPhone(Pair<PlaceSIMCardOrderRO, Product> pair) {
        String pid = pair.getRight().getSupplierConfig().split(",")[0];
        PlaceSIMCardOrderRO ro = pair.getLeft();
        List<PhoneInfoDTO> phoneList = redissonClient.getList(CacheNamesConstant.JUNBO_PHONE_SORTED_NUM_LIST + "_" + pid + "_" + ro.getProvince() + "_" + ro.getCity());
        List<PhoneInfoDTO> list = phoneList.stream().filter(item->!item.isJumpMatch()).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(list)) {
            return null;
        }
        Collections.shuffle(list);
        String result = list.get(0).getPhoneNumber();
        log.info("【自动下单手机匹配】匹配权重,pid={},province={},city={},result={}",pid,ro.getProvince(),ro.getCity(),result);
        return result;
    }
}
/**
 * @description 手机号匹配处理-下单号码后4位
 * @author youmu
 * @date 2023/8/10 10:43
 */
@RequiredArgsConstructor
@Service
@Order(300)
@Slf4j
public class OrderMatchPhoneHandler extends AbstractMatchPhoneHandler {
    private final RedissonClient redissonClient;
    @Override
    public String getMatchPhone(Pair<PlaceSIMCardOrderRO, Product> pair) {
        String pid = pair.getRight().getSupplierConfig().split(",")[0];
        PlaceSIMCardOrderRO ro = pair.getLeft();
        List<PhoneInfoDTO> list = redissonClient.getList(CacheNamesConstant.JUNBO_PHONE_SORTED_NUM_LIST + "_" + pid + "_" + ro.getProvince() + "_" + ro.getCity());
        // 下单手机后四位
        String contactNumber = ro.getContactNumber();
        String str = contactNumber.substring(ro.getContactNumber().length() -4);
        Optional<PhoneInfoDTO> optional = list.stream().filter(item->item.getPhoneNumber().contains(str)).findFirst();
        String result = optional.map(PhoneInfoDTO::getPhoneNumber).orElse(null);
        log.info("【自动下单手机匹配】匹配下单号码后四位,pid={},province={},city={},result={}",pid,ro.getProvince(),ro.getCity(),result);
        return result;
    }
}
/**
 * @description 手机号匹配处理-默认
 * @author youmu
 * @date 2023/8/10 10:43
 */
@Service
@Order(400)
@Slf4j
public class DefaultMatchPhoneHandler extends AbstractMatchPhoneHandler {
    @Override
    public String getMatchPhone(Pair<PlaceSIMCardOrderRO, Product> pair) {
        return "";
    }
}

/**
 * @description 号卡下单匹配手机号责任链
 * @author youmu
 * @date 2023/8/10 10:52
 */
@Service
@Getter
public class MatchPhoneChainHandler {
    @Autowired
    private List<AbstractMatchPhoneHandler> chains;

    public AbstractMatchPhoneHandler firstHandler;


    @PostConstruct
    private void constructChain() {
        if (CollectionUtils.isEmpty(chains)) {
            throw new RuntimeException("not found match phone chain handler");
        }
        firstHandler = chains.get(0);
        for (int i = 0; i < chains.size(); i++) {
            if (i == chains.size() - 1) {
                chains.get(i).setNextHandler(null);
            } else {
                chains.get(i).setNextHandler(chains.get(i + 1));
            }
        }
    }

}
  • 客户端(Client)
/**
 * 号卡下单
 *
 * @param ro                参数
 * @param placeOrderType    下单类型
 * @param matchPhoneHandler 重推下单Handler
 * @param isReport          是否上报
 * @return 结果
 */
private PlaceCardOrderResultVO placeCardOrder(PlaceSIMCardOrderRO ro, PlaceOrderTypeEnum placeOrderType,
                                              AbstractMatchPhoneHandler matchPhoneHandler, boolean isReport) {
    // 获取广告参数
    OceanMacroParam oceanMacroParam = OceanUtil.getOceanMacroParam(ro);
    // 设置下单方式
    ro.setPlaceOrderType(placeOrderType.getCode());
    Triple<Product, SupplierInfoVO, SupplierApiStrategy> params = buildParams(ro.getProductId());
    // 判断是否为号卡
    if (!params.getLeft().getType().equals(ProductBusinessTypeEnum.HK.getValue())) {
        throw new BizException(CodeEnum.NOT_FOUND);
    }
    // 获取产品信息
    Product product = params.getLeft();
    // 处理选号责任链
    if (Objects.nonNull(matchPhoneHandler)) {
        matchPhoneHandler = matchPhoneHandler.doFilter(Pair.of(ro, product));
    }
    // 具体业务逻辑。。。
    
    // 执行重推下单责任链
    if (matchPhoneHandler != null && !result.getSuccess()) {
        return placeCardOrder(ro, AUTO_RETRY, matchPhoneHandler, isReport);
    }
    return result;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

linsm1231

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值