基于接口编程:使用收集器模式使数据获取流程更加清晰可配置
背景
订单导出中,常常需要从多个数据源获取数据,并组装数据详情。常规写法是这样的:
看上去是不是有些混乱?
SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。本文将应用“基于接口编程”的思想,改造这个流程,使之更清晰,可配置化。
基于接口编程
基于接口编程,提倡不直接编写具体实现,而是先定义接口及交互关系, 然后编写接口的多个组件实现,最后,通过组件编排将实现串联起来。
确立数据模型
首要的是确立数据模型。 数据详情获取的整个流程,都会围绕这个数据模型而展开。
在这个例子中, 可以看到, 主要的数据对象是 List<OrderInfo>, List<OrderItemInfo> , 分别对应订单级别和商品级别的信息。在获取数据的过程中,将源源不断的新数据详情充填这两个对象。
@Data
public class OrderInfo {
private String orderNo;
public OrderInfo(String orderNo) {
this.orderNo = orderNo;
}
}
@Data
public class OrderItemInfo {
private String orderNo;
private String itemId;
private String expressId;
public OrderItemInfo(String orderNo, String itemId) {
this.orderNo = orderNo;
this.itemId = itemId;
}
}
定义接口
基于数据模型,定义数据收集的接口。
public interface OrderDetailCollector {
void collect(List<OrderInfo> orderInfoList, List<OrderItemInfo> orderItemInfoList);
}
组件实现
编写组件类,使用适配器,将现有获取数据的实现迁入。 组件化是配置化的前提。
@Component("baseOrderDetailCollector")
public class BaseOrderDetailCollector implements OrderDetailCollector {
@Override
public void collect(List<OrderInfo> orderInfoList, List<OrderItemInfo> orderItemInfoList) {
// 这里可以使用适配器
orderInfoList.addAll(Arrays.asList(new OrderInfo("E001"), new OrderInfo("E002")));
orderItemInfoList.addAll(Arrays.asList(new OrderItemInfo("E001", "I00001"), new OrderItemInfo("E002", "I000002")));
}
}
@Component("expressOrderDetailCollector")
public class ExpressOrderDetailCollector implements OrderDetailCollector {
@Override
public void collect(List<OrderInfo> orderInfoList, List<OrderItemInfo> orderItemInfoList) {
orderItemInfoList.forEach(
orderItemInfo -> orderItemInfo.setExpressId("EXP")
);
}
}
组件工厂
在实现一系列组件后,需要创建一个组件工厂,让客户端方便地获取组件实现类。 通常会用到 ApplicationContextAware 这个接口。
@Component("orderDetailCollectorFactory")
public class OrderDetailCollectorFactory implements ApplicationContextAware {
private static Logger logger = LoggerFactory.getLogger(OrderDetailCollectorFactory.class);
private ApplicationContext applicationContext;
private Map<String, OrderDetailCollector> orderDetailCollectorMap;
private static boolean hasInitialized = false;
@PostConstruct
public void init() {
try {
if(!hasInitialized){
synchronized (OrderDetailCollectorFactory.class){
if(!hasInitialized) {
orderDetailCollectorMap = applicationContext.getBeansOfType(OrderDetailCollector.class);
logger.info("detailCollectorMap: {}", orderDetailCollectorMap);
}
}
}
} catch (Exception ex) {
logger.error("failed to load order detail collector !");
throw new RuntimeException(ServerError.getMessage());
}
}
public OrderDetailCollector get(String name) {
return orderDetailCollectorMap.get(name);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
客户端使用
通常,在具有一系列组件实现后,客户端就可以通过配置的方式来灵活选取和编排组件,实现灵活多变的功能。
public class CollectorClient {
@Resource
OrderDetailCollectorFactory orderDetailCollectorFactory;
public void usage() {
// 可以配置在 DB 或 Apollo 里
List<String> collectors = Arrays.asList("baseOrderDetailCollector", "expressOrderDetailCollector");
List<OrderInfo> orderInfos = new ArrayList<>();
List<OrderItemInfo> orderItemInfos = new ArrayList<>();
collectors.forEach(
collector -> orderDetailCollectorFactory.get(collector).collect(orderInfos, orderItemInfos)
);
}
}
小结
从上面例子可见,确立数据模型,定义接口,实现组件,进行组件编排,是使得代码设计与实现更加清晰灵活的常用模式。
在实现功能服务或业务流程时,不是想到哪写到哪,而是首先进行一番设计和构思,想清楚数据、接口、组件、交互,然后再进行编程,往往实现出来会更加清晰、灵活、可配置化。
业务就是配置。

更多精彩