背景

随着业务不断发展,订单资源模块会对接越来越多的产品,代码量也随之不断增长。本篇文章主要介绍如何结合模板模式和策略模式来重构订单模块,从而避免业务增长带来的代码重复和维护困难的难题

用户在页面上下单,订单创建模块会进行订单创建、创建成功之后会发送一条rocketMq消息,由订单资源模块接收rocketMq消息并创建底层资源。当订单资源模块接收到不同产品的创建消息,会按排创建不同的底层资源,会执行以下操作:

1. 订单校验

2. 创建不同产品的底层资源

3. 将产品资源记录记录写入数据库

4. 回调订单创建模块反馈底层资源创建成功

一、设计方案

由于订单资源创建中存在不变的部分,且流程是唯一的,并需要将可变的行为留给子类来实现,则需要将各子类中公共的行为被提取出来并集中收集到一个公共的父类中,从而避免代码重复,因此我们在订单资源模块中使用模板模式

1.1 使用模板模式

模板方法模式,又叫模板模式, 指在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。简单说,模板方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤

这种类型的设计模式属于行为型模式。模板模式类图如下:

订单模块中有一些逻辑是公共的,这个时候,其实就可以使用模板模式来解决。模板模式的适用场景:

1. 有多个子类共有的方法,且逻辑相同

2. 重要的、复杂的方法,可以考虑作为模板方法

1.2 使用策略模式

订单模块在接收到资源创建消息时,会创建不同模块的底层资源,这个时候,则适合使用策略模式来解决。策略模式的适用场景:

1. 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为

2. 当一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现(就是解决 if...else 所带来的复杂和难以维护)

策略模式一般配合工厂模式一起使用

二、订购模块模板模式案例

订购模块资源创建分为4个步骤:

  • 校验check方法

  • 创建资源 createInstance方法

  • 写入数据库方法 saveToDb

  • 回调方法 callBack

其中写入数据库以及回调方法每个产品都是相同的

2.1 Order抽象类

public abstract class Order {

    private String productType;

    /**
     * 模板方法
     */
    public final void createOrder() {
        check();
        createInstance();
        saveToDb();
        callBack();
    }

    /**
     * 校验产品
     */
    protected abstract void check();


    /**
     * 创建资源
     */
    protected abstract void createInstance();

    /**
     * 保存数据库
     */
    private void saveToDb() {
        System.out.println("保存到数据库" + this.getProductType());
    }

    /**
     * 回调
     */
    private void callBack() {
        System.out.println("回调产品类型" + this.getProductType());
    }

    public String getProductType() {
        return productType;
    }

    public void setProductType(String productType) {
        this.productType = productType;
    }
}

2.2 ToyOrder具体实现子类

public class ToyOrder extends Order{

    @Override
    protected void check() {
        System.out.println("创建玩具 toy 校验");
    }

    @Override
    protected void createInstance() {
        System.out.println("创建玩具 toy 创建资源 ");
    }

}

2.3 AppleOrder具体实现子类

public class AppleOrder extends Order{

    @Override
    protected void check() {
        System.out.println("创建玩具 toy 校验");
    }

    @Override
    protected void createInstance() {
        System.out.println("创建玩具 toy 创建资源 ");
    }

}

2.4 Client测试类

public class Client {
    public static void main(String[] args) {
       System.out.println("-------创建玩具-------");
       ToyOrder toyOrder = new ToyOrder();
       toyOrder.setProductType("toy");
       toyOrder.createOrder();
       System.out.println("-------创建苹果-------");
       AppleOrder appleOrder = new AppleOrder();
       appleOrder.setProductType("apple");
       appleOrder.createOrder();

    }
}

实现结果:

2.5 优化

在订购创建资源过程中,某些实例不需要实际创建资源,在模板方法模式的父类中,我们可以定义一个方法,它默认不做任何事,子类可以视情况要不要覆盖它,该方法称为“钩子”

public abstract class Order {

    private String productType;

    /**
     * 模板方法
     */
    public final void createOrder() {
        check();
        if(isCreateInstance()){
            createInstance();
        }
        saveToDb();
        callBack();
    }

    /**
     * 校验产品
     */
    protected abstract void check();


    /**
     * 创建资源
     */
    protected abstract void createInstance();

    /**
     * 保存数据库
     */
    private void saveToDb() {
        System.out.println("保存到数据库" + this.getProductType());
    }

    /**
     * 回调
     */
    private void callBack() {
        System.out.println("回调产品类型" + this.getProductType());
    }

    /**
     * 钩子方法
     */
    protected boolean  isCreateInstance(){
        return true;
    }

    public String getProductType() {
        return productType;
    }

    public void setProductType(String productType) {
        this.productType = productType;
    }
}

2.6 BananaOrder具体实现子类

public class BananaOrder extends Order{

    @Override
    protected void check() {
        System.out.println("创建香蕉 banana 校验 ");
    }

    @Override
    protected void createInstance() {
        System.out.println("创建香蕉 banana 创建资源 ");
    }

    @Override
    protected boolean isCreateInstance() {
        return false;
    }

}

2.7 Client测试类

public class Client {
    public static void main(String[] args) {
      System.out.println("-------创建玩具-------");
      ToyOrder toyOrder = new ToyOrder();
      toyOrder.setProductType("toy");
      toyOrder.createOrder();
      System.out.println("-------创建苹果-------");
      AppleOrder appleOrder = new AppleOrder();
      appleOrder.setProductType("apple");
      appleOrder.createOrder();
      System.out.println("-------创建香蕉-------");
      BananaOrder bananaOrder = new BananaOrder();
      bananaOrder.setProductType("banana");
      bananaOrder.createOrder();

    }
}

实验结果:

三、订购模块策略+工厂模式案例

若订单侧不使用策略+工厂模式的代码,随着产品侧接入的产品越来越多,代码会显得越来越臃肿,变得十分不可维护

@Transactional(rollbackFor = Exception.class)
public void test(OrderVo req) {
    //不仅有大量if判断,还有各种new对象
    String productType = req.getProductType();
    if(productType.equals("apple")){  
          AppleOrder appleOrder = new AppleOrder();
          appleOrder.setProductType("apple");
          appleOrder.createOrder();
    }else if(productType.equals("toy")){
          ToyOrder toyOrder = new ToyOrder();
          toyOrder.setProductType("toy");
          toyOrder.createOrder();
    }else if(productType.equals("banana"){
        BananaOrder bananaOrder = new BananaOrder();
       bananaOrder.setProductType("banana");
         bananaOrder.createOrder();
    }
}

3.1 策略模式优化

策略工厂OrderFactory

public class OrderFactory {
    
    //map存放不同order
    private static final HashMap<String, Order> orderStrategyFactory = new HashMap<>();

    //static代码块在类加载时执行
    static {
        ToyOrder toyOrder = new ToyOrder();
        toyOrder.setProductType("toy");
        OrderFactory.putOrderStrategy(toyOrder.getProductType(), toyOrder);

        AppleOrder appleOrder = new AppleOrder();
        appleOrder.setProductType("apple");
        OrderFactory.putOrderStrategy(appleOrder.getProductType(), appleOrder);
    }

    public static Order getOrderStrategy(String productType) {
        return orderStrategyFactory.get(productType);
    }

    public  static void putOrderStrategy(String productType, Order order) {
        orderStrategyFactory.put(productType, order);
    }

}

3.2 代码优化

原来的代码

@Transactional(rollbackFor = Exception.class)
public void test(OrderVo req) {

    //不仅有大量if判断,还有各种new对象
    String productType = req.getProductType();
    if(productType.equals("apple")){  
          AppleOrder appleOrder = new AppleOrder();
          appleOrder.setProductType("apple");
          appleOrder.createOrder();
    }else if(productType.equals("toy")){
          ToyOrder toyOrder = new ToyOrder();
          toyOrder.setProductType("toy");
          toyOrder.createOrder();
    }else if(productType.equals("banana"){
          BananaOrder bananaOrder = new BananaOrder();
          bananaOrder.setProductType("banana");
          bananaOrder.createOrder();
    }

}

优化后的

@Transactional(rollbackFor = Exception.class)
public void test(OrderVo req) {
    String productType = req.getProductType();
    Order order = OrderFactory.getOrderStrategy(productType);
    order.createOrder();
}

相关文章

SpringBoot实现责任链模式