文章目录
- 1.背景概述
- 2.设计思路
- 2.1 UML图
- 2.1.1 QueryListContext
- 2.1.2 QueryListHandle
- 2.1.3 QueryFlowListHandle
- 2.1.4 QueryListHandlerFactory
- 2.1.5 AbstractQueryListHandler
- 2.1.6 AbstractQueryFlowListHandler
- 2.1.7 AbstractQueryExportListHandler
- 2.1.8 流程类 RejectedQueryListHandler、InstalledQueryListHandler、MineLeadQueryListHandler
- 2.1.9 数据到类MineLeadExportQueryListHandler、GeneralQueryListExportHandler
- 2.1.10 通用类 GeneralQueryListHandler
- 2.1.11 代理类 QueryListProxy
- 3. 综述总结
我们在项目重构过程中,为了降低重构对线上的风险,会采用各种灰度机制,以提高系统稳定性;但是对于大量接口改造,我们在采用开关控制新接口与接口的切换,为了统一管理而采用集中管理机制。本文即例证一个重构案例,以说明我在重构方案设计中的设计思路,并以借鉴给读者用户。另外,本文采用了相关设计模式(代理、适配器、工厂、模板方法),以提高代码复用性和扩展性。
1.背景概述
本次系统重构,由于涉及到7个查询列表接口改造,意味着需要对接外部应用提供的接口。而原先对接前端的facade层存在大量冗余的代码,给系统迭代和后期维护增加了成本。同时,为了避免对facade层因为重构而带来代码修改的影响(因为未来这个facade将会被新的重构代码替换,以为着这个facade类需要废弃)。但是对于前端相关的查询接口,同时为了避免接口不兼容(譬如查询参数Form对象跟新接口不一致,查询结果跟新接口不一致)情况,需要采用一种适配模式,以便新接口对应查询接口的兼容。鉴于此,通过分析现状以及解决思路并通过相关设计模式的引入解决问题。
1.1 controller层
下图是目前controller层的相关接口清单,这是本次重构的接口清单一部分。
1.2 facade层
facade层包装了相关对接service层数据查询、转换等业务逻辑处理细节。
一个查询接口代码示例
public Result<PageVO<GpsQueryListVo>> queryListForInstalledOnApply(PageForm<GpsQueryListForm> form){
List<GpsQueryListVo> carGpsReList = new ArrayList<>();
int totalCount = 0;
String LOG_TITLE = "车贷申请管理-GPS安装单列表";
Boolean emptyFlag = StringUtils.isEmpty(form.getForm().getCustomerName()) ? Boolean.TRUE : Boolean.FALSE;
try {
if(StringTools.isNotEmpty(form.getForm().getBeginTime())){
form.getForm().setBeginTime(form.getForm().getBeginTime() + " 00:00:00");
}
if(StringTools.isNotEmpty(form.getForm().getEndTime())){
form.getForm().setEndTime(form.getForm().getEndTime() + " 23:59:59");
}
form.getForm().setBuzMark(QueryBuzMarkEnum.InstalledOnApply.getName());
log.info("{},form={}",LOG_TITLE, JSON.toJSON(form));
List<GpsQueryListVo> proppserInfo;
Map<String,String> customerNameMap = null;
if (StringTools.isNotEmpty(form.getForm().getCustomerName())){
//根据查询条件中的客户姓名进行查找
proppserInfo = proppserInfoService.listByForm(form.getForm());
if (CollectionUtils.isEmpty(proppserInfo)){
log.info("{},根据appCode={} customerName={}未查询到信息",LOG_TITLE,form.getForm().getAppCode(),form.getForm().getCustomerName());
return Result.suc(PageVO.newInstance(totalCount,carGpsReList));
}
List<String> appCodes = proppserInfo.stream().map(GpsQueryListVo::getAppCode).collect(toList());
form.getForm().setAppCodeList(appCodes);
customerNameMap = proppserInfo.stream().collect(Collectors.toMap(GpsQueryListVo::getAppCode,GpsQueryListVo::getCustomerName));
}
carGpsReList = gpsQueryListService.queryListForInstalledOnApply(form);
totalCount = gpsQueryListService.queryCountForInstalledOnApply(form);
if(!carGpsReList.isEmpty()){
List<String> appCodeScope = carGpsReList.stream().map(record -> record.getAppCode()).collect(toList());
// 1、组装数据结构模型
Map<String,List<CarGps>> gpsDeviceMap = getGpsDeviceListByAppCodes(appCodeScope);
//为true时说明未查询过客户姓名
if (emptyFlag){
// 查询客户姓名
List<Integer> proppserIds = carGpsReList.stream().map(record -> record.getProppserId()).collect(toList());
customerNameMap = getCustomerNameMap(proppserIds);
}
//2、设置相关属性
for(GpsQueryListVo record : carGpsReList ) {
String appCode = record.getAppCode();
List<CarGps> carGpsList = gpsDeviceMap.getOrDefault(appCode,new ArrayList<>());
List<String> autoAuditResult = carGpsList.stream().map(CarGps::getAutoAuditResult).collect(toList()).stream().filter(list->!StringUtils.isEmpty(list)).collect(toList());
List<String> auditSupplement = carGpsList.stream().map(CarGps::getAuditSupplement).collect(toList()).stream().filter(list->!StringUtils.isEmpty(list)).collect(toList());
List<String> manualAuditResult = carGpsList.stream().map(CarGps::getManualAuditResult).collect(toList()).stream().filter(list->!StringUtils.isEmpty(list)).collect(toList());
record.setAutoAuditResult(StringUtils.collectionToDelimitedString(autoAuditResult,","));
record.setAuditSupplement(StringUtils.collectionToDelimitedString(auditSupplement,","));
record.setManualAuditResult(StringUtils.collectionToDelimitedString(manualAuditResult,","));
record.setFlowStepDesc(getFlowStep(record));
record.setCarType(RuleConditionConstant.IsLcvEnum.getNameByIndex(Integer.valueOf(record.getCarType())));
record.setAppCodeSuffix(getAppCodeSuffix(record));
record.setGpsCount(com.mljr.gps.common.util.StringUtils.killNull(record.getGpsCount()));
record.setFlowRemark(com.mljr.gps.common.util.StringUtils.killNull(record.getFlowRemark()));
record.setGpsDesc(AppInfoIsGpsEnum.getNameByIndex(record.getIsGps()));
if (customerNameMap != null) {
record.setCustomerName(customerNameMap.get(record.getAppCode()));
}
}
}
} catch (Exception e) {
log.error("{}异常,form={}",LOG_TITLE,JSON.toJSON(form),e);
return Result.failInServer(PageVO.newInstance(totalCount,carGpsReList));
}
return Result.suc(PageVO.newInstance(totalCount,carGpsReList));
}
上述代码只是几个接口业务逻辑处理的其中一个代码片段,其他接口跟这个接口大同小异。只是相关查询依赖的service接口不一样,数据转换逻辑有些差异,对于页面查询参数等处理有些差异以外,其他都是类似代码。
2.设计思路
引入设计模式,提高代码复用和扩展性。
- 代理模式:包装对新旧接口的灰度控制切换(QueryListProxy)。
- 适配器模式:解决新接口参数和返回结果对于老接口不兼容问题(RejectedQueryListHandler等以“Handler”结尾的类)。
- 工厂模式:对于适配器类(以“Handler”结尾的类)的对象实例产生包装,客户端调用无需关注Handler具体如何产生(QueryListHandlerFactory)。
- 模板方法:封装共同抽象逻辑骨架以及相关公用代码,交由子类实现差异细节(AbstractQueryListHandler)
2.1 UML图
2.1.1 QueryListContext
该类是一个泛型类,规约定义分页查询参数F以及分页查询结果V。其中成员变量DiamondConfig是个配置类(维护相关全局配置参数,包括新旧接口灰度开关),而PoseidonClient即新接口查询client类。
/**
* @description: 查询列表上下文对象
* <F> 封装查询参数Form
* <V> 封装查询分页VO
* @Date : 2019/5/24 上午11:08
* @Author : 石冬冬-Seig Heil
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class QueryListContext<F extends Serializable,V extends Serializable> {
/**
* GPS审核流程Service
*/
protected GpsFlowService gpsFlowService;
/**
* 查询门面
*/
protected GpsQueryListFacade gpsQueryListFacade;
/**
* 服务调用client
*/
protected PoseidonClient poseidonClient;
/**
* 分页查询Form对象
*/
protected PageForm<F> pageForm;
/**
* 分页查询结果
*/
protected PageVO<V> pageVo;
/**
* 配置
*/
protected DiamondConfig config;
}
2.1.2 QueryListHandle
该类定义了模板方法相关抽象骨架方法,其中PageVo即负责最终各个适配器类内部新旧接口结果集的转换工作。
/**
* @description: 查询列表 Handle
* @Date : 2019/5/24 上午10:59
* @Author : 石冬冬-Seig Heil
*/
public interface QueryListHandle<C extends QueryListContext,V extends Serializable>{
/**
* 处理查询参数
* @param ctx
*/
void prepare(C ctx);
/**
* 处理查询数据
*/
void handle();
/**
* 数据加工
*/
void mapping();
/**
* 后置处理
*/
void after();
/**
* 查询结果集转换
* @return
*/
PageVO<V> view();
}
2.1.3 QueryFlowListHandle
该类的作用,对于几个查询列表,有几个需要特殊处理,但是子类又有相关共同的代码,因此提取的一个接口。其中,skipAppStatus()方法交由子类实现,以实现相关查询结果集需要进一步过滤筛选的状态过滤集合。
/**
* @description: 对于GPS审批流程的数据处理接口
* 适用于:GPS安装单已驳回、GPS安装单已上传、我的GPS领单
* @Date : 2019/6/12 下午7:27
* @Author : 石冬冬-Seig Heil
*/
public interface QueryFlowListHandle {
/**
* 构建查询Form参数
* @return
*/
GpsFlowForm buildQueryParam();
/**
* 设置VO域值
* @param record
* @param vo
*/
void setFieldValues(GpsFlow record, GpsQueryListVo vo);
/**
* 要过滤的订单状态集合
* {@link AppInfoStatusEnum#getIndex()}
* @return
*/
List<Integer> skipAppStatus();
}
2.1.4 QueryListHandlerFactory
Handler类(即实现各个查询接口的处理类)的对象实例产生,我们可以看到工厂方法规约了Class类一定要继承最上层抽象类AbstractQueryListHandler,防止客户端乱传参数。
/**
* @description: GPS查询列表Handler Factory
* @Date : 2019/5/29 下午3:40
* @Author : 石冬冬-Seig Heil
*/
@Slf4j
public final class QueryListHandlerFactory {
/**
* 工厂方法
* @param clazz
* @param <T>
* @return
*/
public static <T extends AbstractQueryListHandler> AbstractQueryListHandler create(Class<T> clazz){
try {
return (AbstractQueryListHandler)Class.forName(clazz.getName()).newInstance();
} catch (Exception e) {
log.error("create exception,name={}",clazz.getName(),e);
}
return null;
}
private QueryListHandlerFactory(){}
}
2.1.5 AbstractQueryListHandler
该类是个上层抽象类,并实现了QueryListHandle接口。同时该抽象类依然是个泛型类,并规约了查询参数F需要继承BaseForm类、查询结果V需要实现Serializable序列化接口。
/**
* @description: 抽象GPS查询列表处理器
* @Date : 2019/5/24 上午10:57
* @Author : 石冬冬-Seig Heil
*/
@Slf4j
public abstract class AbstractQueryListHandler<F extends BaseForm,V extends Serializable> implements QueryListHandle<QueryListContext,V>{
/**
* 字符串分隔符
*/
final String DELIM = ",";
/**
* 默认字符串数值
*/
final String ZERO = "0";
public AbstractQueryListHandler() {
}
/**
* 构造函数
* @param module
*/
public AbstractQueryListHandler(String module) {
this.module = module;
}
/**
* 标示是否过滤当前分页集合中元素
*/
protected boolean skipCurrentRecord;
/**
* 是否查询申请单销售、运营信息
*/
protected boolean includeOperateInfo;
/**
* 流程状态
*/
protected GpsFlowStatusEnum[] flowStatusEnums;
/**********************************Mapping**********************************
/**
* GPS设备 Mapping
* <单号,安装信息>
*/
protected Map<String,List<CarGps>> gpsDeviceMapping = Collections.emptyMap();
/**
* 关联信息 Mapping
* <单号,关联信息>
*/
protected Map<String,SimpleAppRelationInfoRe> relationMapping = Collections.emptyMap();
/**********************************Service**********************************
/**
* GPS审核流程Service
*/
protected GpsFlowService gpsFlowService;
/**
* 上下文对象
*/
protected QueryListContext<F,V> context;
/**
* 查询门面
*/
protected GpsQueryListFacade gpsQueryListFacade;
/**
* 服务调用client
*/
protected PoseidonClient poseidonClient;
/**********************************查询相关**********************************
/**
* 模块名称
*/
protected String module;
/**
* 查询条数
*/
protected int totalCount;
/**
* 过滤申请单状态集合
*/
protected List<Integer> skipStatusScope = Collections.emptyList();
/**
* 查询列表集合
*/
protected List<GpsFlow> flowQueryList = Collections.emptyList();
/**
* 当前页面申请单号集合
*/
protected List<String> appCodeScope = Collections.emptyList();
/**
* 分页查询Form对象
*/
protected PageForm<F> pageForm;
/**
* 条件参数
*/
protected F params;
@Override
public void prepare(QueryListContext ctx) {
context = ctx;
poseidonClient = ctx.poseidonClient;
gpsFlowService = ctx.gpsFlowService;
gpsQueryListFacade = ctx.gpsQueryListFacade;
pageForm = ctx.pageForm;
params = pageForm.getForm();
if(StringTools.isNotEmpty(params.getBeginTime())){
pageForm.getForm().setBeginTime(TimeTools.appendBeginOfDay(params.getBeginTime()));
}
if(StringTools.isNotEmpty(params.getEndTime())){
pageForm.getForm().setEndTime(TimeTools.appendEndOfDay(params.getEndTime()));
}
log.info("{},form={}",module, JSONObject.toJSONString(params));
}
@Override
public void mapping() {
if(!appCodeScope.isEmpty()){
AppRelationInfoDTO relationInfoDTO = AppRelationInfoDTO.builder().appCodeScope(appCodeScope)
.includingOperateInfo(includeOperateInfo)
.systemSource(poseidonClient.systemSource().name()).build();
List<SimpleAppRelationInfoRe> relationInfoReList = poseidonClient.querySimpleAppRelationInfoList(relationInfoDTO);
if(CollectionsTools.isNotEmpty(relationInfoReList)){
relationMapping = relationInfoReList.stream().collect(Collectors.toMap(SimpleAppRelationInfoRe::getAppCode, Function.identity()));
}
gpsDeviceMapping = Optional.ofNullable(gpsQueryListFacade.getGpsDeviceListByAppCodes(appCodeScope)).orElse(Collections.emptyMap());
}
}
/**
* 执行
*/
public final void execute(QueryListContext ctx){
prepare(ctx);
handle();
mapping();
after();
}
@Override
public void after() {
context.setPageVo(view());
}
/**
* 查询退回的单号集合
* @param appCodeScope
* @return
*/
List<String> getGpsIsBackListByAppCodes(List<String> appCodeScope){
return gpsQueryListFacade.getGpsIsBackListByAppCodes(appCodeScope);
}
/**
* 获取配置测试门店集合
* @return
*/
List<Integer> getTestDealers(){
return gpsQueryListFacade.getTestDealers();
}
/**
* 集合元素处理
* @param origin
* @param mapper
* @param <E>
* @param <R>
* @return
*/
<E,R> List<R> collects(Collection<E> origin, Function<? super E, ? extends R> mapper){
return origin.stream().map(mapper).collect(toList()).stream().filter(each -> !StringUtils.isEmpty(each)).collect(toList());
}
/**
* 从关联信息获取并设置VO对象中
* @param appCode
* @param vo
*/
void setValueFromRelationInfo(String appCode,GpsQueryListVo vo,SimpleAppRelationInfoRe simpleAppRelationInfoRe){
skipCurrentRecord = skipStatusScope.contains(Integer.valueOf(simpleAppRelationInfoRe.getStatus()));
if(null != simpleAppRelationInfoRe && skipCurrentRecord){
log.info("skip record appCode={},status={}",appCode,simpleAppRelationInfoRe.getStatus());
totalCount --;
}else{
log.info("keep record appCode={},status={}",appCode,simpleAppRelationInfoRe.getStatus());
}
if(null != simpleAppRelationInfoRe){
vo.setCustomerName(simpleAppRelationInfoRe.getCustomerName());
vo.setCompanyName(simpleAppRelationInfoRe.getCompanyName());
vo.setDealerName(simpleAppRelationInfoRe.getDealerName());
vo.setAppStatus(Integer.valueOf(simpleAppRelationInfoRe.getStatus()));
vo.setFlowStepDesc(getFlowStep(vo));
}else{
vo.setCustomerName("");
vo.setCompanyName("");
vo.setDealerName("");
vo.setAppStatus(-1);
vo.setFlowStepDesc("");
}
}
/**
* 获取流程阶段
* @param record
* @return
*/
String getFlowStep(GpsQueryListVo record){
int status = record.getAppStatus();
boolean status_29 = AppInfoStatusEnum._29 == AppInfoStatusEnum.getByIndex(status);
boolean status_19 = AppInfoStatusEnum._19 == AppInfoStatusEnum.getByIndex(status);
boolean loanCondition = status_29 || status_19;
CarGpsConstant.AppInfoIsGpsEnum appInfoIsGpsEnum = CarGpsConstant.AppInfoIsGpsEnum.getByIndex(Optional.ofNullable(record.getIsGps()).orElse(CarGpsConstant.AppInfoIsGpsEnum.UNINSTALL.getIndex()));
int gpsCount = Integer.valueOf(Optional.ofNullable(record.getGpsCount()).orElse(ZERO));
boolean gpsCondition = CarGpsConstant.AppInfoIsGpsEnum.INSTALLED != appInfoIsGpsEnum && gpsCount > 0;
boolean flowSeq = (null != record.getFlowSeq() && 3 != record.getFlowSeq());
String statusDesc = AppInfoStatusEnum.getNameByIndex(status);
if(null != statusDesc){
StringBuilder flowStep = new StringBuilder(AppInfoStatusEnum.getNameByIndex(status));
if(flowSeq && loanCondition && gpsCondition){
flowStep.append("(GPS待安装)");
}
return flowStep.toString();
}
return MessageFormat.format("UNKNOWN-[{0}]",status);
}
}
2.1.6 AbstractQueryFlowListHandler
该类的作用,封装了几个查询接口相关共同代码,其中view方法实现即需要转换对应VO类。
/**
* @description: 对于查询ca_gps_flow表的抽象封装
* 适用于:GPS安装单已驳回、GPS安装单已上传、我的GPS领单
* @Date : 2019/6/11 下午7:26
* @Author : 石冬冬-Seig Heil
*/
@Slf4j
public abstract class AbstractQueryFlowListHandler extends AbstractQueryListHandler<GpsQueryListForm,GpsQueryListVo> implements QueryFlowListHandle{
/**
* 类型转换定义
*/
final TypeReference<DiamondConfig.QueryListSkipStatusScopeConfig> TYPES = new TypeReference<DiamondConfig.QueryListSkipStatusScopeConfig>(){};
/**
* 前缀
*/
protected final List<String> SUFFIX = Arrays.asList("退");
/**
* 测试门店集合
*/
protected List<Integer> testDealerCodeScope = Collections.emptyList();
/**
* 状态过滤集合额
*/
protected DiamondConfig.QueryListSkipStatusScopeConfig scopeConfig;
/**
* 构造函数
* @param module 模块名称
* @param flowStatusEnums 流程状态枚举
*/
public AbstractQueryFlowListHandler(String module, GpsFlowStatusEnum[] flowStatusEnums) {
super(module);
this.flowStatusEnums = flowStatusEnums;
}
@Override
public void prepare(QueryListContext ctx) {
super.prepare(ctx);
scopeConfig = ctx.config.of(ctx.config.queryListSkipStatusScope,TYPES);
skipStatusScope = skipAppStatus();
testDealerCodeScope = getTestDealers();
}
@Override
public GpsFlowForm buildQueryParam() {
GpsFlowForm flowForm = GpsFlowForm.builder().appCode(params.getAppCode()).customerName(params.getCustomerName()).build();
if(null != flowStatusEnums && flowStatusEnums.length > 0){
flowForm.setFlowStatusScope(Stream.of(flowStatusEnums).map(EnumValue::getIndex).collect(Collectors.toList()));
}
return flowForm;
}
@Override
public void handle() {
PageForm<GpsFlowForm> pageQueryForm = PageForm.newInstance(pageForm.getStart(),pageForm.getLimit(),buildQueryParam());
totalCount = gpsFlowService.queryCount(pageQueryForm);
if(totalCount != 0){
flowQueryList = gpsFlowService.pageQuery(pageQueryForm);
appCodeScope = collects(flowQueryList,GpsFlow::getAppCode);
}
}
@Override
public PageVO<GpsQueryListVo> view() {
if(flowQueryList.size() == 0){
return PageVO.newInstance(totalCount,Collections.emptyList());
}
List<GpsQueryListVo> voList = Lists.newArrayListWithExpectedSize(flowQueryList.size());
//曾经被驳回的GPS审批单
List<String> rejectAppCodes = getGpsIsBackListByAppCodes(appCodeScope);
final List<Integer> skipStatusScope = skipAppStatus();
log.info("module={},skipStatusScope={}",module,skipStatusScope.toArray());
//vo转换
GpsQueryListVo vo;
for(GpsFlow record : flowQueryList ) {
String appCode = record.getAppCode();
vo = new GpsQueryListVo();
SimpleAppRelationInfoRe simpleAppRelationInfoRe = relationMapping.get(appCode);
super.setValueFromRelationInfo(appCode,vo,simpleAppRelationInfoRe);
if(skipCurrentRecord){
continue;
}
List<CarGps> carGpsList = gpsDeviceMapping.getOrDefault(appCode,Collections.emptyList());
List<String> gpsNoList = collects(carGpsList,CarGps::getGpsNo);
List<String> auditSupplement = collects(carGpsList,CarGps::getAuditSupplement);
List<String> autoAuditResult = collects(carGpsList,CarGps::getAutoAuditResult);
List<String> manualAuditResult = collects(carGpsList,CarGps::getManualAuditResult);
vo.setAppCode(record.getAppCode());
vo.setGpsDeviceNo(StringTools.valueOfList(gpsNoList,DELIM));
vo.setAuditSupplement(StringTools.valueOfList(auditSupplement,DELIM));
vo.setAutoAuditResult(StringTools.valueOfList(autoAuditResult,DELIM));
vo.setManualAuditResult(StringTools.valueOfList(manualAuditResult,DELIM));
vo.setFlowStepDesc(getFlowStep(vo));
vo.setAppCodeSuffix(rejectAppCodes.contains(record.getAppCode()) ? SUFFIX : Collections.emptyList());
vo.setUpdateTime(record.getSubmitTime());
if(null != record.getDealerCode()){
vo.setDealerCode(String.valueOf(record.getDealerCode()));
}
setFieldValues(record,vo);
voList.add(vo);
}
return PageVO.newInstance(totalCount,voList);
}
@Override
public void setFieldValues(GpsFlow record, GpsQueryListVo vo) {
}
}
2.1.7 AbstractQueryExportListHandler
该类的作用,对于几个查询导出业务场景处理的进一步抽象。
/**
* @description: 查询数据导出抽象类
* @Date : 2019/6/14 下午4:57
* @Author : 石冬冬-Seig Heil
*/
@Slf4j
public abstract class AbstractQueryExportListHandler extends AbstractQueryListHandler<GpsCompositeQueryForm,GpsCompositeQuery> implements QueryFlowListHandle{
/**
* 类型转换定义
*/
final TypeReference<DiamondConfig.QueryListSkipStatusScopeConfig> TYPES = new TypeReference<DiamondConfig.QueryListSkipStatusScopeConfig>(){};
/**
* 状态过滤集合额
*/
protected DiamondConfig.QueryListSkipStatusScopeConfig scopeConfig;
/**
* 构造函数
* @param module 模块名称
* @param flowStatusEnums 流程状态枚举
*/
public AbstractQueryExportListHandler(String module, GpsFlowStatusEnum[] flowStatusEnums) {
super(module);
this.flowStatusEnums = flowStatusEnums;
}
@Override
public void prepare(QueryListContext ctx) {
super.prepare(ctx);
scopeConfig = ctx.config.of(ctx.config.queryListSkipStatusScope,TYPES);
skipStatusScope = skipAppStatus();
includeOperateInfo = true;
}
@Override
public PageVO<GpsCompositeQuery> view() {
if(flowQueryList.size() == 0){
return PageVO.newInstance(totalCount,Collections.emptyList());
}
List<GpsQueryListVo> voList = Lists.newArrayListWithExpectedSize(flowQueryList.size());
final List<Integer> skipStatusScope = skipAppStatus();
log.info("module={},skipStatusScope={}",module,skipStatusScope.toArray());
//vo转换
GpsQueryListVo vo;
for(GpsFlow record : flowQueryList) {
String appCode = record.getAppCode();
vo = new GpsQueryListVo();
SimpleAppRelationInfoRe simpleAppRelationInfoRe = relationMapping.get(appCode);
super.setValueFromRelationInfo(appCode,vo,simpleAppRelationInfoRe);
if(skipCurrentRecord){
continue;
}
List<CarGps> carGpsList = gpsDeviceMapping.getOrDefault(appCode,Collections.emptyList());
List<String> gpsNoList = collects(carGpsList,CarGps::getGpsNo);
List<String> auditSupplement = collects(carGpsList,CarGps::getAuditSupplement);
List<String> autoAuditResult = collects(carGpsList,CarGps::getAutoAuditResult);
List<String> manualAuditResult = collects(carGpsList,CarGps::getManualAuditResult);
List<String> wireDevices = carGpsList.stream().filter(each -> each.getGpsDealer().endsWith("YX")).map(CarGps::getGpsNo).collect(toList());
List<CarGps> wirelessDevices = carGpsList.stream().filter(each -> each.getGpsDealer().endsWith("WX")).collect(toList());
vo.setAppCode(record.getAppCode());
vo.setGpsDeviceNo(StringTools.valueOfList(gpsNoList,DELIM));
vo.setAuditSupplement(StringTools.valueOfList(auditSupplement,DELIM));
vo.setAutoAuditResult(StringTools.valueOfList(autoAuditResult,DELIM));
vo.setManualAuditResult(StringTools.valueOfList(manualAuditResult,DELIM));
vo.setFlowStepDesc(getFlowStep(vo));
vo.setFlowRemark(record.getRemark());
vo.setAppCodeSuffix(Collections.emptyList());
vo.setUpdateTime(record.getSubmitTime());
vo.setAuditResult(GpsFlowStatusEnum.APPROVAL_SUCCESS.getIndex() == record.getFlowStatus() ? "审核成功" : "审批失败");
vo.setProvince(simpleAppRelationInfoRe.getProvince());
vo.setCarIdentify(simpleAppRelationInfoRe.getCarIdentify());
vo.setSaleName(simpleAppRelationInfoRe.getSaleName());
if(StringTools.isNotEmpty(record.getApprovalTime())){
vo.setAuditDate(record.getApprovalTime().substring(0,10));
}else{
vo.setAuditDate("");
}
vo.setWireDeviceNo(StringTools.valueOfList(wireDevices,DELIM));
List<Integer> wirelessInstallWay = collects(wirelessDevices,CarGps::getGpsInstallWay);
vo.setWirelessInstallType(wirelessInstallWay.isEmpty() ? "" : CarGpsConstant.GpsInstallTypeEnum.getNameByIndex(wirelessInstallWay.get(0)));
vo.setCarSeries(simpleAppRelationInfoRe.getCarSeries());
vo.setCarColor(simpleAppRelationInfoRe.getCarColor());
vo.setWirelessDeviceNo(StringTools.valueOfList(collects(wirelessDevices,CarGps::getGpsNo),DELIM));
vo.setWirelessGpsPosition(StringTools.valueOfList(collects(wirelessDevices,CarGps::getGpsPosition),DELIM));
vo.setLoanPeriods(simpleAppRelationInfoRe.getLoanPeriod());
vo.setApprovalUserName(record.getApprovalUserName());
setFieldValues(record,vo);
voList.add(vo);
}
return PageVO.newInstance(totalCount,voList);
}
@Override
public void setFieldValues(GpsFlow record, GpsQueryListVo vo) {
}
}
2.1.8 流程类 RejectedQueryListHandler、InstalledQueryListHandler、MineLeadQueryListHandler
他们共同继承了AbstractQueryFlowListHandler。
GPS安装单已驳回,可见代码清晰简洁。
/**
* @description: GPS安装单已驳回
* @Date : 2019/5/24 上午11:04
* @Author : 石冬冬-Seig Heil
*/
@Slf4j
public class RejectedQueryListHandler extends AbstractQueryFlowListHandler{
public RejectedQueryListHandler() {
super("queryListForRejectedGps", new GpsFlowStatusEnum[]{GpsFlowStatusEnum.REJECTED,GpsFlowStatusEnum.MASTER_BACK});
}
@Override
public List<Integer> skipAppStatus() {
return scopeConfig.rejectedList;
}
}
GPS安装单已上传
/**
* @description: GPS安装单已上传
* @Date : 2019/5/25 下午6:15
* @Author : 石冬冬-Seig Heil
*/
@Slf4j
public class InstalledQueryListHandler extends AbstractQueryFlowListHandler{
public InstalledQueryListHandler() {
super("queryListForInstalledGps",new GpsFlowStatusEnum[]{GpsFlowStatusEnum.PROCESSING});
}
@Override
public GpsFlowForm buildQueryParam() {
GpsFlowForm flowForm = super.buildQueryParam();
if(null != params.getTest() && params.getTest().booleanValue()){
flowForm.setDealerCodeScope(getTestDealers());
}
return flowForm;
}
@Override
public void setFieldValues(GpsFlow record, GpsQueryListVo vo) {
vo.setGpsFlowStep(record.getFlowStep());
vo.setGpsFlowStepDesc(GpsFlowStepEnum.getNameByIndex(record.getFlowStep()));
vo.setGpsFlowStatus(record.getFlowStatus());
vo.setGpsFlowStatusDesc(GpsFlowStatusEnum.getNameByIndex(record.getFlowStatus()));
vo.setApprovalUserName(record.getApprovalUserName());
vo.setTestDealer(getTestDealers().contains(record.getDealerCode()));
}
@Override
public List<Integer> skipAppStatus() {
return scopeConfig.installedList;
}
}
我的GPS领单
/**
* @description: 我的GPS领单
* @Date : 2019/5/29 上午11:45
* @Author : 石冬冬-Seig Heil
*/
@Slf4j
public class MineLeadQueryListHandler extends AbstractQueryFlowListHandler{
public MineLeadQueryListHandler() {
super("queryListForMineGps",new GpsFlowStatusEnum[]{GpsFlowStatusEnum.PROCESSING});
}
@Override
public GpsFlowForm buildQueryParam() {
GpsFlowForm flowForm = super.buildQueryParam();
flowForm.setApprovalUserName(params.getApprovalUserName());
return flowForm;
}
@Override
public List<Integer> skipAppStatus() {
return scopeConfig.mineList;
}
}
2.1.9 数据到类MineLeadExportQueryListHandler、GeneralQueryListExportHandler
他们共同继承了AbstractQueryExportListHandler。
我的GPS领单-数据导出
/**
* @description: 我的GPS领单-数据导出
* @Date : 2019/5/29 上午11:45
* @Author : 石冬冬-Seig Heil
*/
@Slf4j
public class MineLeadExportQueryListHandler extends AbstractQueryExportListHandler{
public MineLeadExportQueryListHandler() {
super("queryListForMineLeadExport", new GpsFlowStatusEnum[]{GpsFlowStatusEnum.PROCESSING});
}
@Override
public GpsFlowForm buildQueryParam() {
GpsFlowForm flowForm = GpsFlowForm.builder().build();
if(null != flowStatusEnums && flowStatusEnums.length > 0){
flowForm.setFlowStatusScope(Stream.of(flowStatusEnums).map(EnumValue::getIndex).collect(Collectors.toList()));
}
flowForm.setApprovalUserName(params.getApprovalUserName());
return flowForm;
}
@Override
public void handle() {
PageForm<GpsFlowForm> pageQueryForm = PageForm.newInstance(pageForm.getStart(),pageForm.getLimit(),buildQueryParam());
totalCount = gpsFlowService.queryCount(pageQueryForm);
if(totalCount != 0){
flowQueryList = gpsFlowService.pageQuery(pageQueryForm);
appCodeScope = collects(flowQueryList, GpsFlow::getAppCode);
}
}
@Override
public List<Integer> skipAppStatus() {
return scopeConfig.mineList;
}
}
/**
* @description: GPS综合查询-数据导出
* @Date : 2019/5/26 上午11:17
* @Author : 石冬冬-Seig Heil
*/
@Slf4j
public class GeneralQueryListExportHandler extends AbstractQueryExportListHandler{
final GpsFlowStatusEnum[] DEFAULT_STATUS = new GpsFlowStatusEnum[]{GpsFlowStatusEnum.APPROVAL_SUCCESS,GpsFlowStatusEnum.REJECTED,GpsFlowStatusEnum.PROCESSING,GpsFlowStatusEnum.MASTER_BACK};
public GeneralQueryListExportHandler() {
super("queryListForGeneralExport",new GpsFlowStatusEnum[]{GpsFlowStatusEnum.APPROVAL_SUCCESS,GpsFlowStatusEnum.REJECTED,GpsFlowStatusEnum.PROCESSING,GpsFlowStatusEnum.MASTER_BACK});
}
@Override
public List<Integer> skipAppStatus() {
return Collections.emptyList();
}
@Override
public GpsFlowForm buildQueryParam() {
String auditResult = Optional.ofNullable(params.getAuditResult()).orElse("-1");
GpsFlowForm flowForm = GpsFlowForm.builder()
.approvalUserName(params.getApprovalUserName())
.flowStatusScope(Stream.of(DEFAULT_STATUS).map(EnumValue::getIndex).collect(Collectors.toList()))
.build();
switch (auditResult){
//审核成功
case "1":
flowForm.setFlowStatusScope(Arrays.asList(GpsFlowStatusEnum.APPROVAL_SUCCESS.getIndex()));
break;
//审核失败
case "2":
flowForm.setFlowStatusScope(Arrays.asList(GpsFlowStatusEnum.REJECTED.getIndex()));
break;
}
if(StringTools.isNotEmpty(params.getBeginTime())){
flowForm.setBeginTime(TimeTools.appendBeginOfDay(params.getBeginTime()));
}
if(StringTools.isNotEmpty(params.getEndTime())){
flowForm.setEndTime(TimeTools.appendEndOfDay(params.getEndTime()));
}
return flowForm;
}
@Override
public void handle() {
PageForm<GpsFlowForm> pageQueryForm = PageForm.newInstance(pageForm.getStart(),pageForm.getLimit(),buildQueryParam());
totalCount = gpsFlowService.queryCount(pageQueryForm);
if(totalCount != 0){
flowQueryList = gpsFlowService.pageQuery(pageQueryForm);
appCodeScope = collects(flowQueryList, GpsFlow::getAppCode);
}
}
}
2.1.10 通用类 GeneralQueryListHandler
他们共同继承了AbstractQueryListHandler。
/**
* @description: GPS综合查询
* @Date : 2019/5/25 下午6:15
* @Author : 石冬冬-Seig Heil
*/
@Slf4j
public class GeneralQueryListHandler extends AbstractQueryListHandler<GpsQueryListForm,GpsQueryListVo>{
protected List<GpsGeneralQueryRe> resultList;
public GeneralQueryListHandler() {
super("queryListForGpsGeneral");
}
/**
* 构建查询参数
* @return
*/
GpsQueryListForm buildQueryParam() {
GpsQueryListForm queryForm = GpsQueryListForm.builder()
.appCode(params.getAppCode())
.customerName(params.getCustomerName())
.carIdentify(params.getCarIdentify())
.build();
return queryForm;
}
@Override
public void handle() {
GpsGeneralQueryDTO dto = new GpsGeneralQueryDTO();
BeanUtils.copyProperties(buildQueryParam(),dto);
resultList = poseidonClient.gpsGeneralQueryList(dto);
totalCount = resultList.size();
}
@Override
public PageVO<GpsQueryListVo> view() {
if(resultList.size() == 0){
return PageVO.newInstance(totalCount,Collections.emptyList());
}
List<GpsQueryListVo> voList = Lists.newArrayListWithExpectedSize(resultList.size());
GpsQueryListVo vo;
for(GpsGeneralQueryRe record : resultList ) {
vo = new GpsQueryListVo();
vo.setAppCode(record.getAppCode());
vo.setCustomerName(record.getCustomerName());
vo.setCompanyName(record.getCompanyName());
vo.setDealerName(record.getDealerName());
vo.setCarIdentify(record.getCarIdentify());
vo.setCarSeries(record.getCarSeries());
vo.setGpsDesc(CarGpsConstant.AppInfoIsGpsEnum.getNameByIndex(Integer.valueOf(record.getIsGps())));
vo.setAppStatus(Integer.valueOf(record.getStatus()));
vo.setFlowStepDesc(getFlowStep(vo));
//冗余字段AppCodeSuffix,不给个空集合,前端页面无法正常显示页面。
vo.setAppCodeSuffix(Collections.emptyList());
voList.add(vo);
}
return PageVO.newInstance(totalCount,voList);
}
}
GpsListForFengYunHandler
/**
* @description: 对接风云系统-GPS安装列表
* @Date : 2019/6/6 下午6:52
* @Author : 石冬冬-Seig Heil
*/
@Slf4j
public class GpsListForFengYunHandler extends AbstractQueryListHandler<GpsQueryListForm,GpsInstalledListVo>{
/**
* 查询列表集合
*/
protected List<GpsInstalledListVo> queryList = Collections.emptyList();
/**
* 构建查询DTO
* @return
*/
PageForm<GpsQueryListDTO> buildDTO(){
GpsQueryListDTO dto = GpsQueryListDTO.builder()
.appCode(params.getAppCode()).customerName(params.getCustomerName())
.companyName(params.getCompanyName())
.beginTime(params.getBeginTime()).endTime(params.getEndTime())
.dealerCodeScope(params.getDealerCodeScope())
.saleName(params.getSaleName()).operationName(params.getOperationName())
.build();
return PageForm.newInstance(pageForm.getStart(),pageForm.getLimit(),dto);
}
@Override
public void handle() {
Result<PageVO<GpsInstalledListVo>> result = poseidonClient.queryListForFengYun(buildDTO());
if(result.isSuccess() && CollectionsTools.isNotEmpty(result.getData().getData())) {
totalCount = result.getData().getRecordsTotal();
queryList = result.getData().getData();
}
}
@Override
public PageVO<GpsInstalledListVo> view() {
if (!queryList.isEmpty()) {
//2、设置相关属性
queryList.forEach(record -> {
String appCode = record.getAppCode();
List<CarGps> carGpsList = gpsDeviceMapping.getOrDefault(appCode, new ArrayList<>());
List<String> autoAuditResult = collects(carGpsList,CarGps::getAutoAuditResult);
List<String> manualAuditResult = collects(carGpsList,CarGps::getManualAuditResult);
record.setAutoAuditResult(StringUtils.collectionToDelimitedString(autoAuditResult, DELIM));
record.setManualAuditResult(StringUtils.collectionToDelimitedString(manualAuditResult, DELIM));
record.setGpsCount(com.mljr.gps.common.util.StringUtils.killNull(record.getGpsCount()));
record.setFlowRemark(com.mljr.gps.common.util.StringUtils.killNull(record.getFlowRemark()));
record.setSaleName(com.mljr.gps.common.util.StringUtils.killNull(record.getSaleName()));
record.setOperationName(com.mljr.gps.common.util.StringUtils.killNull(record.getOperationName()));
record.setAppStatusDesc(AppInfoStatusEnum.getNameByIndex(record.getAppStatus()));
});
}
return PageVO.newInstance(totalCount,queryList);
}
}
2.1.11 代理类 QueryListProxy
具体作用详见类说明
/**
* @description: GPS查询列表代理类
* 该类的作用:
* (1)、对于客户端,无需关注底层是如何控制新接口和老接口的切换逻辑,把交互细节委托给代理类。
* (2)、该类通过xdiamond配置中心维护一个JSON控制各个接口开关逻辑。
* {@link DiamondConfig#callPoseidonQueryList}
* {@link com.mljr.gps.common.config.DiamondConfig.CallPoseidonQueryListEnable}
* (3)、代理新接口交互调用。{@link PoseidonClient}
* (4)、代理数据导出老接口调用,{@link GpsCompositeQueryFacade} 以及其他相关查询列表老接口调用。{@link GpsQueryListFacade}
* (5)、对于GPS安装单已驳回、已上传、我的领单 则通过ca_gps_flow通过flow_status等相关条件过滤。
* {@link com.mljr.gps.engine.handler.AbstractQueryFlowListHandler}
* (6)、风云系统-GPS列表,查看
* {@link GpsListForFengYunHandler} {@link QueryListProxy#queryListForFengYun(PageForm)}
* @Date : 2019/5/23 下午12:17
* @Author : 石冬冬-Seig Heil
*/
@Component
@Slf4j
public class QueryListProxy {
@Autowired
GpsFlowService gpsFlowService;
@Autowired
GpsQueryListFacade gpsQueryListFacade;
@Autowired
GpsCompositeQueryFacade gpsCompositeQueryFacade;
@Autowired
PoseidonClient poseidonClient;
@Autowired
DiamondConfig diamondConfig;
/**
* 车贷审批管理-GPS安装单已驳回
* @param pageForm
* @return
*/
public Result<PageVO<GpsQueryListVo>> queryListForRejectedGps(PageForm<GpsQueryListForm> pageForm) {
boolean enable = diamondConfig.of(diamondConfig.callPoseidonQueryList,new TypeReference<DiamondConfig.CallPoseidonQueryListEnable>(){}).queryListForRejectedGps;
if(!enable){
return gpsQueryListFacade.queryListForUnInstalledGps(pageForm);
}
AbstractQueryListHandler handler = QueryListHandlerFactory.create(RejectedQueryListHandler.class);
QueryListContext<GpsQueryListForm,GpsQueryListVo> context = buildContext(pageForm);
handler.execute(context);
return Result.suc(context.getPageVo());
}
/**
* 车贷审批管理-GPS安装单已上传
* @param pageForm
* @return
*/
public Result<PageVO<GpsQueryListVo>> queryListForInstalledGps(PageForm<GpsQueryListForm> pageForm) {
boolean enable = diamondConfig.of(diamondConfig.callPoseidonQueryList,new TypeReference<DiamondConfig.CallPoseidonQueryListEnable>(){}).queryListForInstalledGps;
if(!enable){
return gpsQueryListFacade.queryListForInstalledGps(pageForm);
}
AbstractQueryListHandler handler = QueryListHandlerFactory.create(InstalledQueryListHandler.class);
QueryListContext<GpsQueryListForm,GpsQueryListVo> context = buildContext(pageForm);
handler.execute(context);
return Result.suc(context.getPageVo());
}
/**
* 车贷审批管理-我的领单
* @param pageForm
* @return
*/
public Result<PageVO<GpsQueryListVo>> queryListForMineGps(PageForm<GpsQueryListForm> pageForm) {
boolean enable = diamondConfig.of(diamondConfig.callPoseidonQueryList,new TypeReference<DiamondConfig.CallPoseidonQueryListEnable>(){}).queryListForGpsApproveMine;
if(!enable){
return gpsQueryListFacade.queryListForGpsApproveMine(pageForm);
}
AbstractQueryListHandler handler = QueryListHandlerFactory.create(MineLeadQueryListHandler.class);
pageForm.getForm().setApprovalUserName(AccessToken.get().getUserName());
QueryListContext<GpsQueryListForm,GpsQueryListVo> context = buildContext(pageForm);
handler.execute(context);
return Result.suc(context.getPageVo());
}
/**
* 我的gps领单-数据导出
* @param pageForm
* @return
*/
public Result<PageVO<GpsCompositeQuery>> mineApprExportQuery(PageForm<GpsCompositeQueryForm> pageForm){
boolean enable = diamondConfig.of(diamondConfig.callPoseidonQueryList,new TypeReference<DiamondConfig.CallPoseidonQueryListEnable>(){}).queryListForGpsApproveMineExport;
if(!enable){
return gpsCompositeQueryFacade.mineApprExportQuery(pageForm);
}
AbstractQueryListHandler handler = QueryListHandlerFactory.create(MineLeadExportQueryListHandler.class);
pageForm.getForm().setApprovalUserName(AccessToken.get().getUserName());
QueryListContext<GpsCompositeQueryForm,GpsCompositeQuery> context = buildContextForExport(pageForm);
handler.execute(context);
return Result.suc(context.getPageVo());
}
/**
* GPS综合查询-列表查询
* @param pageForm
* @return
*/
public Result<PageVO<GpsQueryListVo>> queryListForGpsGeneral(PageForm<GpsQueryListForm> pageForm){
boolean enable = diamondConfig.of(diamondConfig.callPoseidonQueryList,new TypeReference<DiamondConfig.CallPoseidonQueryListEnable>(){}).queryListForGpsGeneral;
if(!enable){
return gpsQueryListFacade.queryListForGpsGeneral(pageForm);
}
AbstractQueryListHandler handler = QueryListHandlerFactory.create(GeneralQueryListHandler.class);
handler.execute(buildContext(pageForm));
return Result.suc(handler.view());
}
/**
* GPS综合查询-数据导出
* @param pageForm
* @return
*/
public Result<PageVO<GpsCompositeQuery>> queryListForGpsGeneralExport(PageForm<GpsCompositeQueryForm> pageForm){
boolean enable = diamondConfig.of(diamondConfig.callPoseidonQueryList,new TypeReference<DiamondConfig.CallPoseidonQueryListEnable>(){}).queryListForGpsGeneralExport;
if(!enable){
return gpsCompositeQueryFacade.query(pageForm);
}
AbstractQueryListHandler handler = QueryListHandlerFactory.create(GeneralQueryListExportHandler.class);
handler.execute(buildContextForExport(pageForm));
return Result.suc(handler.view());
}
/**
* 风云系统调用-GPS安装单列表
* @param queryForm
* @return
*/
public Result<PageVO<GpsInstalledListVo>> queryListForFengYun(PageForm<GpsQueryListDTO> queryForm) {
boolean enable = diamondConfig.of(diamondConfig.callPoseidonQueryList,new TypeReference<DiamondConfig.CallPoseidonQueryListEnable>(){}).queryGpsListForFengYun;
if(!enable){
return gpsQueryListFacade.queryListForInstalled(queryForm);
}
GpsQueryListDTO queryListDTO = queryForm.getForm();
GpsQueryListForm queryListForm = GpsQueryListForm.builder()
.appCode(queryListDTO.getAppCode()).customerName(queryListDTO.getCustomerName())
.companyName(queryListDTO.getCompanyName())
.appTimeStart(queryListDTO.getBeginTime()).appTimeEnd(queryListDTO.getEndTime())
.dealerCodeScope(queryListDTO.getDealerCodeScope())
.saleName(queryListDTO.getSaleName()).operationName(queryListDTO.getOperationName()).build();
PageForm<GpsQueryListForm> pageForm = PageForm.newInstance(queryForm.getStart(),queryForm.getLimit(),queryListForm);
GpsListForFengYunHandler handler = new GpsListForFengYunHandler();
QueryListContext<GpsQueryListForm,GpsInstalledListVo> context = QueryListContext.<GpsQueryListForm,GpsInstalledListVo>builder()
.gpsQueryListFacade(gpsQueryListFacade).poseidonClient(poseidonClient)
.gpsFlowService(gpsFlowService).pageForm(pageForm).build();
handler.execute(context);
return Result.suc(context.getPageVo());
}
/**
* 构建上下文
* @param pageForm
* @return
*/
QueryListContext<GpsQueryListForm,GpsQueryListVo> buildContext(PageForm<GpsQueryListForm> pageForm){
QueryListContext<GpsQueryListForm,GpsQueryListVo> queryListContext = QueryListContext.<GpsQueryListForm,GpsQueryListVo>builder()
.gpsQueryListFacade(gpsQueryListFacade).poseidonClient(poseidonClient)
.config(diamondConfig)
.gpsFlowService(gpsFlowService).pageForm(pageForm).build();
return queryListContext;
}
/**
* 构建上下文
* 适用业务场景:数据导出(譬如 我的领单、GPS综合查询)
* @param pageForm
* @return
*/
QueryListContext<GpsCompositeQueryForm,GpsCompositeQuery> buildContextForExport(PageForm<GpsCompositeQueryForm> pageForm){
QueryListContext<GpsCompositeQueryForm,GpsCompositeQuery> queryListContext = QueryListContext.<GpsCompositeQueryForm,GpsCompositeQuery>builder()
.gpsQueryListFacade(gpsQueryListFacade).poseidonClient(poseidonClient)
.config(diamondConfig)
.gpsFlowService(gpsFlowService).pageForm(pageForm).build();
return queryListContext;
}
}
3. 综述总结
重构是个比较类的活,因此你的设计思路决定你重构的成本以及决定对现有代码的影响,是兼容,是适配,还是大量的各种调用方各种if判断,但是如果经过深思熟虑,并引入相关设计模式综合运用,就能够巧妙的发挥设计模式的作用意义。而设计模式又不是孤立存在的,因此会各种组合使用,发挥各自的优势,同时弥补各自的缺点。这样,我们最终才能写出称心如意的代码,容易对于后期扩展也奠定了基础。
下面的是我的公众号二维码图片,欢迎关注,搜索【秋夜无霜】。