开门见山
某天,我在公园散步打开手机 JD 发现 GTX 3080 TI 显卡上架,正准备付款的时候我看看我的订单详情,就联想到了我以前做的一个订单详情处理的业务。具体的伪代码如下:
public Map<String, List<Object>> doHandler(List<Object> dataList) {
Map<String, List<Object>> resultMap = new HashMap<>();
// 实物商品
if (实物商品) {
// 数据处理 ...
resultMap.put("inKindList", mainList);
}
// 虚拟商品
if (虚拟商品) {
// 数据处理 ...
resultMap.put("virtualList", tradeList);
}
// 其他商品
if (其他商品) {
// 数据处理 ...
resultMap.put("otherList", otherList);
}
return resultMap;
}
复制代码
业务描述
上面我只是简单的对我的数据处理方法做了一个伪代码的表述。对于这种场景就非常类似京东购物下单的情况。
比如:最近618 搞活动我在需要在京东购买一张 GTX 3038 TI 显卡,包含 2 年的质保服务。送一个机械键盘,再加 500 数码品类的优惠券(两张 200 的,一张 100 ),自动关注该店铺送 VIP 不定期推送该店铺活动。我直接购买我的订单假设只有这一个商品。
再把这个场景套回到我的伪代码中。
那么订单详情中主要的信息就可以分为三部分
- 商品的主要信息包含: GTX 3038 TI 显卡, 2 年的质保服务
- 赠送实物和优惠券:赠送机械键盘,再加 500 数码品类。
- 其他辅助信息:比如订单成为某店铺的 VIP , 订阅该店铺活动等。
数据结构
假设我上面提及到的所有订单详情记录都是存储在 order_detail 表中,并且每一订单实物、虚拟商品都存为一条数据。并且我在回显的时候需要做一个分类处理,需要分为三类:商品信息(显卡、质保信息),实物赠送(机械键盘)、优惠券、其他。他们都存在实际价格、优惠价格、原价等。
业务需求
相当于我的需求就是需要将这些商品的价值分别展示在订单详情中,只要是用户在没有支付之前这些这些商品的信息可能都会发生变化,都需要进行实时的计算(个人觉得如果是热点商品肯定这些都是提前预热好的,不能去实时计算的,为了贴合场景咱们假设一下???)。
代码设计
在代码设计之前我们先来看看我的设计, 如下图所示:
为什么是命令模式?
命令模式:它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。
适用场景:在某些场合,比如要对行为进行”记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将”行为请求者”与”行为实现者”解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
对于“订单详情” 的这个场景,咱们就可以确定三个分类:主要信息(实物商品)、虚拟商品(优惠券等)、其他商品。这个可以分别对应三个数据处理命令类。
我的代码
我的代码如下,主要是解决订单详情中的信息进行分类显示的问题。
命令接口和参数定义
命令接口如下:
/**
* 订单详情处理
*/
public interface OrderDetailService {
Map<String, List<OrderDetailDto>> orderDetailProcess(List<OrderDetailDto> orderDetailList);
}
复制代码
OrderDetailDto
的处理如下:
/**
* 订单详情
*/
@Data
public class OrderDetailDto {
private Long id;
/**
* 订单编码
*/
private String orderCode;
/**
* 商品编码
*/
private String commodityCode;
/**
* 商品名称
*/
private Integer commodityType;
/**
* 商品名称
*/
private String commodityName;
/**
* 单价
*/
private BigDecimal commodityUnitPrice;
/**
* 商品数量
*/
private Integer commodityQuantity;
/**
* 原价
*/
private BigDecimal originalPrice;
/**
* 折扣价格
*/
private BigDecimal discountPrice;
/**
* 实际价格
*/
private BigDecimal actualPrice;
}
复制代码
命令实现
实物商品实现类:
/**
* 实物订单处理
*/
public class InKindOrderDetailCommand implements OrderDetailService {
@Override
public Map<String, List<OrderDetailDto>> orderDetailProcess(List<OrderDetailDto> orderDetailList) {
// 订单中的实物
List<OrderDetailDto> resultList = orderDetailList.stream()
// 筛选出实物
.filter(item -> Objects.equals(1, item.getCommodityType()))
.peek(item -> {
// 计算原价
item.setOriginalPrice(item.getCommodityUnitPrice().multiply(new BigDecimal(item.getCommodityQuantity())));
// 计算其他数据项目 ....
}).collect(toList());
return new HashMap<String, List<OrderDetailDto>>() {
{
put("inKind", resultList);
}
};
}
}
复制代码
虚拟商品实现类:
public class VirtualItemOrderDetailCommand implements OrderDetailService {
@Override
public Map<String, List<OrderDetailDto>> orderDetailProcess(List<OrderDetailDto> orderDetailList) {
return null;
}
}
复制代码
其他商品实现类:
/**
* 其他物品
*/
public class OtherOrderDetailCommand implements OrderDetailService {
@Override
public Map<String, List<OrderDetailDto>> orderDetailProcess(List<OrderDetailDto> orderDetailList) {
return null;
}
}
复制代码
命令调用
调用工具类(Terminial 类)
public class OrderDetailServices {
public List<OrderDetailService> orderDetailServiceList = new ArrayList<>();
/**
* 初始化方法
*/
public static OrderDetailServices create() {
OrderDetailServices orderDetailServices = new OrderDetailServices();
// 实物
OrderDetailService inKindOrderDetail = new InKindOrderDetailCommand();
// 虚拟物品
OrderDetailService virtualItemOrderDetail = new VirtualItemOrderDetailCommand();
// 赠送物品(优惠券、积分)
OrderDetailService giveAwayOrderDetail = new GiveAwayOrderDetailCommand();
// 其他
OrderDetailService otherOrderDetail = new OtherOrderDetailCommand();
orderDetailServices.orderDetailServiceList =
Arrays.asList(inKindOrderDetail, virtualItemOrderDetail, giveAwayOrderDetail, otherOrderDetail);
return orderDetailServices;
}
public Map<String, List<OrderDetailDto>> orderDetailProcess(List<OrderDetailDto> orderDetailList) {
Map<String, List<OrderDetailDto>> resultMap = new HashMap<>();
for (OrderDetailService item : orderDetailServiceList) {
Map<String, List<OrderDetailDto>> tmpMap = item.orderDetailProcess(orderDetailList);
if (tmpMap != null && !tmpMap.isEmpty()) {
resultMap.putAll(tmpMap);
}
}
return resultMap;
}
}
复制代码
测试类:
public class OrderDetailServiceTest {
public static void main(String[] args) {
OrderDetailServices detailServices = OrderDetailServices.create();
OrderDetailDto orderDetailDto = new OrderDetailDto();
orderDetailDto.setOrderCode("CD00001");
orderDetailDto.setCommodityType(1);
orderDetailDto.setCommodityName("GTX 3080 TI");
orderDetailDto.setCommodityUnitPrice(new BigDecimal("8999"));
orderDetailDto.setCommodityQuantity(2);
OrderDetailDto orderDetailDto2 = new OrderDetailDto();
orderDetailDto2.setCommodityType(1);
orderDetailDto2.setOrderCode("CD00002");
orderDetailDto2.setCommodityName("LEI SHEN 茶轴 1088");
orderDetailDto2.setCommodityUnitPrice(new BigDecimal("488"));
orderDetailDto2.setCommodityQuantity(1);
OrderDetailDto orderDetailDto3 = new OrderDetailDto();
orderDetailDto3.setCommodityType(2);
orderDetailDto3.setOrderCode("CJ00002");
orderDetailDto3.setCommodityName("满 5000 减 200");
orderDetailDto3.setCommodityUnitPrice(new BigDecimal("200"));
orderDetailDto3.setCommodityQuantity(2);
OrderDetailDto orderDetailDto4 = new OrderDetailDto();
orderDetailDto4.setCommodityType(2);
orderDetailDto4.setOrderCode("CJ00002");
orderDetailDto4.setCommodityName("满 5000 减 100");
orderDetailDto4.setCommodityUnitPrice(new BigDecimal("100"));
orderDetailDto4.setCommodityQuantity(1);
List<OrderDetailDto> detailList = Arrays.asList(
orderDetailDto,
orderDetailDto2,
orderDetailDto3,
orderDetailDto4);
Map<String, List<OrderDetailDto>> resultMap = detailServices
.orderDetailProcess(detailList);
System.out.println(resultMap);
}
}
复制代码
总结
命令模式非常适合于数据的处理,支持撤销、重做。将命令处理对象以参数的形式进行传递,最后对数据进行一个统一的处理。它的优缺点如下:
优点:降低耦合度、非常容易拓展、比如上面我在增加一个详情分类非常容易。
缺点:如果当命令非常多的情况下,可能会导致项目的 “类爆炸”。