装饰者设计模式在业务中的实践

装饰者设计模式在业务中的实践

​ 装饰者设计模式在顾名思义就是在原来逻辑上进行一层装饰逻辑,从而实现不通过if-else实现对优雅的对基础逻辑的扩充。在JDK源码中的InputStream中就有使用了装饰者的设计模式。从而实现通过BufferedInputStream、DataInputStream等其他修饰InputStream,增加了缓存读取、类型读取等功能,相当于InputStream之上加了很多修饰功能,在所以它是一个装饰器模式。

image-20220113001347847

这里inputStream作为一个被装饰的类,然后BufferedInputStream,对功能加入了带buffer功能的装饰

从构造器也是可以看出来。

image-20220116181241019

构造器初始化的时候需要传入一个inputStream

简单构造一个可以用到装饰器模式的场景。

现在需要对一个订单实际支付金额的计算。

1:计算商品原价。

2:存在优惠券,需要对商品原价扣减。

3:存在红包也需要对空包金额扣减。

这种场景,可以通过if-else,写一大段很长的代码实现,但是给人的感觉不优雅,可拓展性也不好。处理if-else的场景,可以通过策略模式,责任链模式,装饰器模式。

但是这里可能多种优惠策略,不太适合策略模式。责任链模式也是可以的,这里重点说明的是装饰器模式。这里不展开讨论了。

梳理:这里我们可以定义一个计算价格的基类,订单种存在那些优惠,我们可以采用装饰器模式,对原来的类进行装饰。从而实现多种优惠的叠加计算。

伪代码

定义一个订单对象,一个订单包含多个子单,每个子单包含一个商品,商品绑定价格,以及多个优惠信息。

这里主要关注优惠信息,定义两种类型的优惠:1:打折 ,2:红包

每种优惠类型,都通过属性描述优惠的额度

public class Order {
  
  private int id; //订单ID
  private String orderNo; //订单号
  private BigDecimal totalPayMoney; //总支付金额
  private List<OrderDetail> list; //详细订单列表
}

public class OrderDetail {
  private int id; //详细订单ID
  private int orderId;//主订单ID
  private Merchandise merchandise; //商品详情
  private BigDecimal payMoney; //支付单价
}

public class Merchandise {
  
  private String sku;//商品SKU
  private String name; //商品名称
  private BigDecimal price; //商品单价
  private Map<String, SupportPromotions> supportPromotions; //支持促销类型
}

public class UserCoupon {
  
  private int id; //优惠券ID
  private int userId; //领取优惠券用户ID
  private String sku; //商品SKU
  private BigDecimal coupon; //优惠金额
}

public class UserRedPacket {

  private int id; //红包ID
  private int userId; //领取用户ID
  private String sku; //商品SKU
  private BigDecimal redPacket; //领取红包金额
}

然后定义一个计算订单金额的接口

public interface IBaseCount {
  
  BigDecimal countPayMoney(OrderDetail orderDetail);

}

分别构建要给计算订单金额的抽象类,无优惠计算类,红包优惠的计算类

public abstract class BaseCountDecorator implements IBaseCount{
  
  private IBaseCount count;
  
  public BaseCountDecorator(IBaseCount count) {
    this.count = count;
  }

  public BigDecimal countPayMoney(OrderDetail orderDetail) {
    BigDecimal payTotalMoney = new BigDecimal(0);
    if(count!=null) {
      payTotalMoney = count.countPayMoney(orderDetail);
    }
    return payTotalMoney;
  }
}
public class CouponDecorator extends BaseCountDecorator{

  public CouponDecorator(IBaseCount count) {
    super(count);
  }
  
  public BigDecimal countPayMoney(OrderDetail orderDetail) {
    BigDecimal payTotalMoney = new BigDecimal(0);
    payTotalMoney = super.countPayMoney(orderDetail);
    payTotalMoney = countCouponPayMoney(orderDetail);
    return payTotalMoney;
  }
  
  private BigDecimal countCouponPayMoney(OrderDetail orderDetail) {
    
    BigDecimal coupon =  orderDetail.getMerchandise().getSupportPromotions().get(PromotionType.COUPON).getUserCoupon().getCoupon();
    System.out.println("优惠券金额:" + coupon);
    
    orderDetail.setPayMoney(orderDetail.getPayMoney().subtract(coupon));
    return orderDetail.getPayMoney();
  }
}
public class RedPacketDecorator extends BaseCountDecorator{

  public RedPacketDecorator(IBaseCount count) {
    super(count);
  }
  
  public BigDecimal countPayMoney(OrderDetail orderDetail) {
    BigDecimal payTotalMoney = new BigDecimal(0);
    payTotalMoney = super.countPayMoney(orderDetail);
    payTotalMoney = countCouponPayMoney(orderDetail);
    return payTotalMoney;
  }
  
  private BigDecimal countCouponPayMoney(OrderDetail orderDetail) {
    
    BigDecimal redPacket = orderDetail.getMerchandise().getSupportPromotions().get(PromotionType.REDPACKED).getUserRedPacket().getRedPacket();
    System.out.println("红包优惠金额:" + redPacket);
    
    orderDetail.setPayMoney(orderDetail.getPayMoney().subtract(redPacket));
    return orderDetail.getPayMoney();
  }
}
public class BaseCount implements IBaseCount{

  public BigDecimal countPayMoney(OrderDetail orderDetail) {
orderDetail.setPayMoney(orderDetail.getMerchandise().getPrice());
    System.out.println("商品原单价金额为:" +  orderDetail.getPayMoney());
    
    return orderDetail.getPayMoney();
  }

}

整个计算金额的体系以及构建好了。

然后通过一个计算工厂类将这些计算逻辑连接起来

public class PromotionFactory {
  
  public static BigDecimal getPayMoney(OrderDetail orderDetail) {
    
    //获取给商品设定的促销类型
    Map<String, SupportPromotions> supportPromotionslist = orderDetail.getMerchandise().getSupportPromotions();
    
    //初始化计算类
    IBaseCount baseCount = new BaseCount();
    if(supportPromotionslist!=null && supportPromotionslist.size()>0) {
      for(String promotionType: supportPromotionslist.keySet()) {//遍历设置的促销类型,通过装饰器组合促销类型
        baseCount = protmotion(supportPromotionslist.get(promotionType), baseCount);
      }
    }
    return baseCount.countPayMoney(orderDetail);
  }
  
  /**
   * 组合促销类型
   * @param supportPromotions
   * @param baseCount
   * @return
   */
  private static IBaseCount protmotion(SupportPromotions supportPromotions, IBaseCount baseCount) {
    if(PromotionType.COUPON.equals(supportPromotions.getPromotionType())) {
      baseCount = new CouponDecorator(baseCount);
    }else if(PromotionType.REDPACKED.equals(supportPromotions.getPromotionType())) {
      baseCount = new RedPacketDecorator(baseCount);
    }
    return baseCount;
  }

}

通过工厂类的getPayMoney方法获取子单商品上所有的的促销类型,然后依次获取最终的装饰对象,执行计算订单金额,最终获取到最终的金额。

DEMO

 public static void main( String[] args ) throws InterruptedException, IOException
    {
        Order order = new Order();
        init(order);

        for(OrderDetail orderDetail: order.getList()) {
            BigDecimal payMoney = PromotionFactory.getPayMoney(orderDetail);
            orderDetail.setPayMoney(payMoney);
            System.out.println("最终支付金额:" + orderDetail.getPayMoney());
        }
    }

获得执行结果:

商品原单价金额为:100
红包优惠金额:10
优惠券金额:10
最终支付金额:80

其实这种方式和mybatis的插件的设计模式(责任链+动态代理)很像

posted @ 2022-01-16 23:30  一懒众衫小QAQ  阅读(354)  评论(1编辑  收藏  举报