【设计模式运用】模版方法模式

介绍

模版方法设计模式是一种行为型设计模式,它允许你定义一个算法的框架,但将一些步骤的实现延迟到子类中。这样,子类可以重新定义算法的某些特定步骤,而不改变算法的结构。

模版方法包含以下主要角色:

抽象类(Abstract Class)

  • 定义了一个提供了算法骨架的模版方法,包含了算法中的基本步骤和一些具体实现的方法。

  • 可能包含一些抽象方法,这些方法由具体的子类来实现,以完成算法的特定步骤。

具体子类(Concrete Subclasses):

  • 实现了抽象类中定义的抽象方法,以完成算法的特定步骤。

  • 可以根据需要重写模版方法中的某些步骤,以实现特定的功能。

在模版方法设计模式中,抽象类和具体子类之间的关系是一种典型的模板(Template)与具体内容的关系。抽象类定义了算法的框架和骨架,而具体子类提供了具体的实现内容,从而形成了一个完整的算法。这种设计使得算法的框架和具体内容分离,提高了代码的可复用性和可扩展性。

主要优点包括:

  1. 代码复用性:

    通过定义算法的框架和基本步骤,模版方法将相同的代码抽取到抽象类中,使得这部分代码可以在多个具体子类中被复用,从而减少了重复编码。
  2. 扩展性:

    允许子类在不改变算法整体结构的情况下,重写或者扩展算法的特定步骤,以满足不同的需求,从而提高了代码的灵活性和可扩展性。
  3. 代码结构清晰:

    模版方法将算法的框架和具体实现分离开来,使得代码结构更加清晰,易于维护和理解。
  4. 促进统一性:

    通过定义一个统一的模版方法,可以确保所有的子类都按照相同的算法来执行操作,从而保持了系统中相关行为的统一性。

使用场景

  1. JDK中的IO类(如InputStream和OutputStream):JDK中的IO类库中的InputStream和OutputStream等类就是使用了模版方法设计模式。它们定义了读取和写入数据的基本算法框架,而具体的读取和写入操作则留给子类来实现,例如FileInputStream和FileOutputStream等。 
  2. Servlet生命周期管理:Servlet生命周期管理是一个很好的模版方法设计模式的例子。Servlet规范定义了Servlet的生命周期,包括初始化、处理请求和销毁等阶段。Servlet容器(如Tomcat)提供了Servlet接口,开发者只需继承Servlet接口并重写特定方法来实现自己的Servlet类,而Servlet容器会根据规范来管理Servlet的生命周期。
  3. Spring框架中的JdbcTemplate:在Spring框架中,JdbcTemplate是一个用于执行SQL查询和更新的模版类。它提供了执行SQL操作的基本算法框架,如创建连接、执行SQL语句和处理结果等,而具体的SQL语句和参数设置则由用户提供。

  4. JUnit测试框架:JUnit测试框架也使用了模版方法设计模式。JUnit定义了一系列用于编写单元测试的基本方法,如setUp()、tearDown()和testXxx()等,用户只需继承TestCase类并重写这些方法来编写自己的测试用例。

  5. 电商网站订单处理流程:在电商网站中,订单处理流程通常具有一定的规范,如创建订单、支付订单、发货等。这些订单处理流程可以使用模版方法设计模式来实现,定义一个订单处理的抽象类,其中包含订单处理的基本步骤,而具体的订单处理流程则由不同的订单类型子类来实现,如普通订单、预定订单等。

  6. 银行业务流程:银行业务流程中,比如开户流程、贷款审批流程等,也可以使用模版方法设计模式来实现。银行业务流程中的各个环节有固定的操作步骤,但每种业务又有不同的具体实现方式,因此可以使用模版方法来定义通用的流程,而具体的业务流程则由不同的业务子类来实现。

示例

下面是一个简单的Java示例,展示了如何使用模版方法设计模式来实现一个通用的文档处理器,该处理器可以根据不同类型的文档(如Word文档和PDF文档)执行不同的操作。

// 抽象类:文档处理器
abstract class DocumentProcessor {
    // 模版方法,定义了处理文档的算法框架
    public final void processDocument() {
        openDocument(); // 打开文档
        if (isValidDocument()) { // 如果文档有效
            parseDocument(); // 解析文档
            extractText(); // 提取文本
            processText(); // 处理文本
        } else {
            System.out.println("Invalid document type."); // 不支持的文档类型
        }
        closeDocument(); // 关闭文档
    }

    // 具体步骤的实现
    private void openDocument() {
        System.out.println("Opening document"); // 打开文档的具体实现
    }

    private void closeDocument() {
        System.out.println("Closing document"); // 关闭文档的具体实现
    }

    // 抽象方法,由子类实现
    abstract void parseDocument(); // 解析文档的抽象方法
    abstract void extractText(); // 提取文本的抽象方法
    abstract void processText(); // 处理文本的抽象方法

    // 钩子方法,子类可以选择性地覆盖
    boolean isValidDocument() {
        return true; // 默认实现,假设文档都是有效的
    }
}

// 具体子类:处理Word文档的文档处理器
class WordDocumentProcessor extends DocumentProcessor {
    @Override
    void parseDocument() {
        System.out.println("Parsing Word document"); // 解析Word文档的具体实现
    }

    @Override
    void extractText() {
        System.out.println("Extracting text from Word document"); // 提取Word文档文本的具体实现
    }

    @Override
    void processText() {
        System.out.println("Processing text of Word document"); // 处理Word文档文本的具体实现
    }
}

// 具体子类:处理PDF文档的文档处理器
class PdfDocumentProcessor extends DocumentProcessor {
    @Override
    void parseDocument() {
        System.out.println("Parsing PDF document"); // 解析PDF文档的具体实现
    }

    @Override
    void extractText() {
        System.out.println("Extracting text from PDF document"); // 提取PDF文档文本的具体实现
    }

    @Override
    void processText() {
        System.out.println("Processing text of PDF document"); // 处理PDF文档文本的具体实现
    }

    @Override
    boolean isValidDocument() {
        // 额外的PDF格式验证逻辑
        return checkPdfFormat(); // 验证PDF格式
    }

    private boolean checkPdfFormat() {
        // 模拟验证PDF格式的逻辑
        System.out.println("Checking PDF format"); // 验证PDF格式的具体实现
        return true; // 假设验证通过
    }
}

public class TemplateMethodDemo {
    public static void main(String[] args) {
        // 创建Word文档处理器
        DocumentProcessor wordProcessor = new WordDocumentProcessor();
        // 处理Word文档
        wordProcessor.processDocument();
        System.out.println();

        // 创建PDF文档处理器
        DocumentProcessor pdfProcessor = new PdfDocumentProcessor();
        // 处理PDF文档
        pdfProcessor.processDocument();
    }
}

项目应用

背景:通信行业需要接入多家供应商,开放人员需要对接号卡、流量包等产品接口回调,虽然每个供应商都走自己的策略类,但是涉及订单状态回调的代码存在以下问题:每个回调策略类互相拷贝,存在大量重复代码和公共方法,可读性差,未对业务流程进行标准化,不利于后续维护和进一步拓展,为了解决以上问题和提高开发接入效率,采用模版方法进行回调策略的重构。

 

设计思路:采取策略模式+模板方法重构,抽取了公共流程和通用方法,允许子类可以重写钩子方法,灵活性高。原则上是一个产品类型一套模版,如无特殊则继承公共模板,核心关注doCallback。

抽象类(Abstract Class) 

public interface SupplierCallback<R> {
    /**
     * 获取供应商策略
     *
     * @return 供应商策略
     */
    SupplierStrategyNameEnum getSupplierStrategy();

    /**
     * 获取产品类型集合
     *
     */
    List<ProductBusinessTypeEnum> getProductTypeList();

    /**
     * 下单后主动回调,防止回调比下单快到情况
     * @param orderInfo
     */
    void callback(OrderInfo orderInfo);

    /**
     * 供应商回调订单状态变更
     * @param orderNo
     * @param callbackParam
     */
    void callback(String orderNo, R callbackParam);
}

/**
 * 通用回调抽象策略
 */
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Slf4j
@Component
public abstract class AbstractCommonCallback<R extends CallbackParam> implements SupplierCallback<R> {

    protected final OrderInfoService orderInfoService;
    protected final JunboCallbackService junboCallbackService;
    protected final FsNoticeService fsNoticeService;
    protected final OrderFailTypeParseUtil orderFailTypeParseUtil;
    protected final PromotionFacade promotionFacade;
    protected final ProductService productService;

    /**
     * 解析回调参数
     *
     * @param responseMsg
     * @return
     */
    protected abstract R parseCallbackParam(String responseMsg);

    /**
     * 根据时间获取最新回调参数
     *
     * @param orderNo
     * @return
     */
    protected R getNewCallBack(String orderNo) {
        // 获取最新回调时间
        List<JunboCallback> list = junboCallbackService.lambdaQuery()
                .eq(JunboCallback::getSysOrderId, orderNo)
                .list();
        if (CollUtil.isEmpty(list)) {
            return null;
        }
        R callback = null;
        DateTime createTime = null;
        for (JunboCallback junboCallback : list) {
            R r = parseCallbackParam(junboCallback.getResponseMsg());
            if (callback == null) {
                callback = r;
                createTime = DateTime.of(junboCallback.getCreateTime());
            } else {
                // 回调时间字段为null,取创建时间
                Date oldTime = Objects.isNull(callback.getCallbackTime()) ? createTime : callback.getCallbackTime();
                Date newTime = Objects.isNull(r.getCallbackTime()) ? junboCallback.getCreateTime() : r.getCallbackTime();
                if (newTime.after(oldTime)) {
                    callback = r;
                    createTime = DateTime.of(junboCallback.getCreateTime());
                }
            }
        }
        return callback;
    }

    @Override
    public final void callback(OrderInfo orderInfo) {
        Long id = orderInfo == null ? null : orderInfo.getId();
        log.info("开始执行订单主动回调供应商策略:orderId={},strategy={}",id,getSupplierStrategy());
        try {
            if(orderInfo == null || StringUtils.isBlank(orderInfo.getOrderNo())) {
                log.warn("异常订单数据,订单不存在或订单号为空,orderId={}",id);
                return;
            }
            // 获取最新的回调参数
            R callbackParam = getNewCallBack(orderInfo.getOrderNo());
            if (callbackParam == null) {
                return;
            }
            CallbackResult callbackResult = doCallback(orderInfo, callbackParam);
            if(callbackResult == null || callbackResult.getOrderInfo() == null) {
                log.warn("回调更新结果为空");
                return;
            }
            afterCallBack(orderInfo, callbackResult);
        } catch (Exception e) {
            log.error("供应商回调执行失败,orderId={}",id,e);
        }
    }

    @Override
    public final void callback(String orderNo, R callbackParam) {
        log.info("开始执行供应商策略回调:orderNo={},strategy={}",orderNo,getSupplierStrategy());
        try {
            if (StringUtils.isBlank(orderNo)) {
                log.warn("异常订单数据,订单号为空:{}", callbackParam);
                return;
            }
            OrderInfo orderInfo = orderInfoService.getByOrderNo(orderNo);
            if (orderInfo == null) {
                return;
            }
            if (callbackParam.getCallbackTime() != null) {
                Date time = callbackParam.getCallbackTime();
                R callback = getNewCallBack(orderNo);
                Date newTime = callback.getCallbackTime();
                if (newTime.after(time)) {
                    log.warn("回调时间小于最新回调时间,不处理,{}", callbackParam);
                    return;
                }
            }
            CallbackResult callbackResult = doCallback(orderInfo, callbackParam);
            if(callbackResult == null || callbackResult.getOrderInfo() == null) {
                log.warn("回调更新结果为空");
                return;
            }
            afterCallBack(orderInfo, callbackResult);

        } catch (Exception e) {
            log.error("供应商回调执行失败,orderNo:{},callbackParam:{}", orderNo, callbackParam, e);
        }
    }

    /**
     * 回调业务逻辑实现
     *
     * @param orderInfo
     * @param callbackParam
     * @return
     */
    protected abstract CallbackResult doCallback(OrderInfo orderInfo, R callbackParam);

    protected void afterCallBack(OrderInfo orderInfo, CallbackResult callbackResult) {
        // 订单状态变更记录;
        orderInfoService.saveOrderStatusChangeRecordByOrderInfo(callbackResult.getOrderInfo(), orderInfo.getProductType());
        if(!Objects.equals(orderInfo.getOrderStatus(),OrderStatusEnum.SUCCEED.getValue()) && Objects.equals(callbackResult.getOrderInfo().getOrderStatus(),OrderStatusEnum.SUCCEED.getValue())) {
            // 更新成功
            orderInfoService.updateProductSuccessOrders(orderInfo.getProductCode(), 1);
        } else if(Objects.equals(orderInfo.getOrderStatus(),OrderStatusEnum.SUCCEED.getValue()) && Objects.equals(callbackResult.getOrderInfo().getOrderStatus(),OrderStatusEnum.FAILED.getValue())) {
            // 更新失败
            orderInfoService.updateProductSuccessOrders(orderInfo.getProductCode(), -1);
        }
    }

    /**
     * @param param     回调参数
     * @param orderInfo 订单信息
     * @param update    更新订单
     * @description 处理成功->失败订单
     * @author youmu
     * @date 2023/8/29 18:36
     */
    protected void handleSuccessToFail(R param, OrderInfo orderInfo, OrderInfo update) {
        if (OrderStatusEnum.SUCCEED.getValue().equals(orderInfo.getOrderStatus())) {
            promotionFacade.updateOrderSuccessCount(orderInfo, -1);
            update.setOrderStatus(OrderStatusEnum.FAILED.getValue());
            if (StringUtils.isNotBlank(param.getErrMsg())) {
                update.setFailCode(orderFailTypeParseUtil.matchFailType(orderInfo.getSupplierId(), orderInfo.getProductType(),param.getErrMsg()).getCode());
                update.setExecuteResultMsg(param.getErrMsg());
            }
            // 订单信息日志
            reportLog(orderInfo, update, false);
            // 状态变更通知
            fsNoticeService.orderStatusNotice(orderInfo, param.getErrMsg(), OrderStatusEnum.SUCCEED, OrderStatusEnum.FAILED);
        }
    }

    /**
     * @param param     回调参数
     * @param orderInfo 订单信息
     * @param update    更新订单
     * @description 处理失败->成功订单
     * @author youmu
     * @date 2023/8/29 18:36
     */
    protected void handleFailToSuccess(R param, OrderInfo orderInfo, OrderInfo update) {
        if (OrderStatusEnum.FAILED.getValue().equals(orderInfo.getOrderStatus())) {
            promotionFacade.updateOrderSuccessCount(orderInfo, 1);
            update.setOrderStatus(OrderStatusEnum.SUCCEED.getValue());
            // 订单信息日志
            reportLog(orderInfo, update, true);
            fsNoticeService.orderStatusNotice(orderInfo, "", OrderStatusEnum.FAILED, OrderStatusEnum.SUCCEED);
            // 清空失败原因的code
            update.setFailCode(0);
            update.setExecuteResultMsg("");
        }
    }

}
/**
 * 号卡回调抽象策略
 */
@Slf4j
@Component
public abstract class AbstractCardCallback<R extends CallbackParam> extends AbstractCommonCallback<R> {

    private final SalvageOrderLogService salvageOrderLogService;

    public AbstractCardCallback(OrderInfoService orderInfoService, JunboCallbackService junboCallbackService, SalvageOrderLogService salvageOrderLogService, FsNoticeService fsNoticeService, OrderFailTypeParseUtil orderFailTypeParseUtil, PromotionFacade promotionFacade, ProductService productService) {
        super(orderInfoService, junboCallbackService, fsNoticeService, orderFailTypeParseUtil, promotionFacade, productService);
        this.salvageOrderLogService = salvageOrderLogService;
    }

    @Override
    public List<ProductBusinessTypeEnum> getProductTypeList() {
        return Collections.singletonList(ProductBusinessTypeEnum.HK);
    }

    protected void afterCallBack(OrderInfo orderInfo, CallbackResult callbackResult) {
        // 回调后置处理逻辑
    }
}

 具体子类(Concrete Subclasses)



/**
 * 众联回调策略接口
 */
@Slf4j
@Service
public class ZoneLianCardCallback extends AbstractCardCallback<ZoneLianCardOrderCallBackParam> {

    public ZoneLianCardCallback(OrderInfoService orderInfoService, JunboCallbackService junboCallbackService, SalvageOrderLogService salvageOrderLogService, FsNoticeService fsNoticeService, OrderFailTypeParseUtil orderFailTypeParseUtil, PromotionFacade promotionFacade, ProductService productService) {
        super(orderInfoService, junboCallbackService, salvageOrderLogService, fsNoticeService, orderFailTypeParseUtil, promotionFacade, productService);
    }

    @Override
    public SupplierStrategyNameEnum getSupplierStrategy() {
        return SupplierStrategyNameEnum.ZL;
    }

    @Override
    protected ZoneLianCardOrderCallBackParam parseCallbackParam(String responseMsg) {
        return JacksonUtils.parseObject(responseMsg, ZoneLianCardOrderCallBackParam.class);
    }

    @Override
    protected CallbackResult doCallback(OrderInfo orderInfo, ZoneLianCardOrderCallBackParam param) {
        // 具体业务逻辑
    }

}

/**
 * 风茂存量回调策略接口
 */
@Slf4j
@Service
public class FengmaoStockCallback extends AbstractStockCallback<FengmaoOrderCallbackParam> {

    public FengmaoStockCallback(OrderInfoService orderInfoService, JunboCallbackService junboCallbackService, FsNoticeService fsNoticeService, OrderFailTypeParseUtil orderFailTypeParseUtil, PromotionFacade promotionFacade, ProductService productService) {
        super(orderInfoService, junboCallbackService, fsNoticeService, orderFailTypeParseUtil, promotionFacade, productService);
    }

    @Override
    public SupplierStrategyNameEnum getSupplierStrategy() {
        return SupplierStrategyNameEnum.FENGMAO;
    }

    @Override
    protected FengmaoOrderCallbackParam parseCallbackParam(String responseMsg) {
        return JacksonUtils.parseObject(responseMsg, FengmaoOrderCallbackParam.class);
    }

    @Override
    protected CallbackResult doCallback(OrderInfo orderInfo, FengmaoOrderCallbackParam param) {
        // 具体业务逻辑
    }

}

 工厂调用

package com.onecode.tc.platform.service.supplier.callback;

import com.google.common.collect.Maps;
import com.onecode.tc.platform.enums.BusinessTypeEnum;
import com.onecode.tc.platform.enums.ProductBusinessTypeEnum;
import com.onecode.tc.platform.enums.SupplierStrategyNameEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * 供应商回调策略工厂类
 */
@Slf4j
@Component
public class SupplierCallbackFactory<R> {
    private final Map<String, SupplierCallback<R>> strategyMap = Maps.newHashMap();
    private static final Map<BusinessTypeEnum,List<ProductBusinessTypeEnum>> businessTypeMap;

    // 静态初始化块
    static {
        businessTypeMap = Maps.newHashMap();
        // 添加初始化的键值对
        businessTypeMap.put(BusinessTypeEnum.CARD, Collections.singletonList(ProductBusinessTypeEnum.HK));
        businessTypeMap.put(BusinessTypeEnum.STOCK, Arrays.asList(ProductBusinessTypeEnum.LLB,ProductBusinessTypeEnum.CL));
    }

    @Autowired
    public SupplierCallbackFactory(List<SupplierCallback<R>> strategies) {
        for (SupplierCallback<R> strategy : strategies) {
            for(ProductBusinessTypeEnum productBusinessTypeEnum : strategy.getProductTypeList()) {
                strategyMap.put(strategy.getSupplierStrategy().getDesc() + "_" + productBusinessTypeEnum.getValue(), strategy);
            }
        }
    }

    public SupplierCallback<R> getStrategy(String supplierStrategy, Integer productType) {
        SupplierCallback<R> supplierCallbackStrategy =  getSupplierCallback(supplierStrategy, productType);
        if(supplierCallbackStrategy == null) {
            log.warn("找不到对应供应商回调策略类,supplierStrategy={},productType={}", supplierStrategy,productType);
        }
        return supplierCallbackStrategy;
    }

    private SupplierCallback<R> getSupplierCallback(String supplierStrategy, Integer productType) {
        return strategyMap.get(supplierStrategy + "_" + productType);
    }

    public SupplierCallback<R> getStrategy(SupplierStrategyNameEnum supplierStrategy, ProductBusinessTypeEnum productType) {
        SupplierCallback<R> supplierCallbackStrategy = getSupplierCallback(supplierStrategy.getDesc(), productType.getValue());
        if(supplierCallbackStrategy == null) {
            log.warn("找不到对应供应商回调策略类,supplierStrategy={},productType={}", supplierStrategy,productType);
        }
        return supplierCallbackStrategy;
    }

    public SupplierCallback<R> getStrategy(SupplierStrategyNameEnum supplierStrategy, BusinessTypeEnum businessTypeEnum) {
        for(ProductBusinessTypeEnum productBusinessTypeEnum : businessTypeMap.get(businessTypeEnum)) {
            SupplierCallback<R> supplierCallback = getSupplierCallback(supplierStrategy.getDesc(), productBusinessTypeEnum.getValue());
            if(supplierCallback != null) {
                return supplierCallback;
            }
        }
        log.warn("找不到对应供应商回调策略类,supplierStrategy={},businessType={}", supplierStrategy,businessTypeEnum);
        return null;
    }

}


@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Tag(name = "杭州风茂回调")
@RestController
@RequestMapping("/open/api/callback/fengmao")
@Slf4j
public class FengmaoCallbackController {

    private final SupplierCallbackFactory<CallbackParam> supplierCallbackFactory;
    private final JunboCallbackService junboCallbackService;

    @GetMapping("/order")
    @Operation(summary = "订单回调接口")
    public String cardOrderCallback(FengmaoOrderCallbackParam param) {
        log.info("【杭州风茂回调】回调参数:{}", JacksonUtils.toJsonString(param));
        //记录信息
        JunboCallback junboCallback = new JunboCallback();
        junboCallback.setSysOrderId(param.getLinkid());
        junboCallback.setOrderStatus(param.getStatus());
        junboCallback.setResponseMsg(JacksonUtils.toJsonString(param));
        junboCallback.setOrderCode(param.getLinkid());
        junboCallback.setContactNumber(param.getMobile());
        junboCallback.setRemark(param.getMsg());
        junboCallback.setSupplier(SupplierStrategyNameEnum.FENGMAO.getValue());
        junboCallbackService.saveCallbackInfo(junboCallback);
        supplierCallbackFactory.getStrategy(SupplierStrategyNameEnum.FENGMAO, BusinessTypeEnum.STOCK).callback(param.getLinkid(), param);
        return "OK";
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

linsm1231

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

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

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

打赏作者

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

抵扣说明:

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

余额充值