一、概述
定义一个操作中的算法的骨架,将一些步骤延迟到子类中。 TemplateMethod使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。
二、适用性
1.一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
2.各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。 首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。 最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
3.控制子类扩展。
三、参与者
1.AbstractClass 定义抽象的原语操作(primitive operation),具体的子类将重新定义它们以实现一个算法的各个步骤。 实现一个模板方法,定义一个算法的骨架。 该模板方法不仅调用原语操作,也调用定义在AbstractClass或其他对象中的操作。
2.ConcreteClass 实现原语操作以完成算法中与特定子类相关的步骤。
四、类图
五、示例
AbstractClass
public abstract class Template {public abstract void print();public void update() {System.out.println("开始打印");for (int i = 0; i < 10; i++) {print();}}
}
ConcreteClass
public class TemplateConcrete extends Template{@Overridepublic void print() {System.out.println("这是子类的实现");}
}
自测
@Test
public void testTemplate() {Template temp = new TemplateConcrete();temp.update();
}
自测结果
Connected to the target VM, address: '127.0.0.1:12824', transport: 'socket'
开始打印
这是TemplateConcrete类的实现
这是TemplateConcrete类的实现
这是TemplateConcrete类的实现
Disconnected from the target VM, address: '127.0.0.1:12824', transport: 'socket'
六、实践
封装
/*** @author lyonardo* @Description* @createTime 2022年09月20日 13:52:00*/@Slf4j
public abstract class FxBaseExtServiceAbstract<T, E> {private final AccessTokenServcie accessTokenServcie = SpringUtil.getBean(AccessTokenServcie.class);/* private final MongoService mongoService = SpringUtil.getBean(MongoService.class);public List<E> pick(Class<E> var2) {return mongoService.find(new Query(), var2);}*/public List<E> pickFx(String dataObjectApiName, List<Filter> filterList, String url, Class<E> var2) {Assert.notNull(accessTokenServcie,"没有获取到纷享token");//获取接口授权tokenFxiaokeAccessToken fxiaokeAccessToken = accessTokenServcie.getAccessToken();Assert.notNull(fxiaokeAccessToken,"没有获取到纷享token");List<E> all = new ArrayList<>();long startTime = System.currentTimeMillis();String dataId = "";for (; ; ) {List<Filter> filters = new ArrayList<>(filterList);filters.add(new Filter("_id", Lists.newArrayList(dataId), "GT"));List<Order> orderList = new ArrayList<>();orderList.add(new Order("_id", true));//组装接口入参FxiaokeBaseReq map = FxiaokeUtil.buildParamMapExt(dataObjectApiName, null, filters, orderList, fxiaokeAccessToken, accessTokenServcie.getCurrentOpenUserId());//入参转换jsonString jsonString = JSON.toJSONString(map);log.info("数据处理同步纷享接口入参=>{}", jsonString);//调用纷享预设对象(属性对象)APIString result = OsHttpClient.create().post(url, jsonString);String dataListString = FxiaokeUtil.handleResponseResult(result);if (!StringUtils.hasText(dataListString)) {break;}JSONArray isExit = JSON.parseArray(dataListString);if (isExit.isEmpty()) {break;}JSONObject jsonObject = JSONObject.parseObject(isExit.get(isExit.size() - 1).toString());if (jsonObject.isEmpty()) {break;}if (all.size()> 2000) {break;}dataId = jsonObject.getString("_id");log.info("dataId=>>>{}", dataId);all.addAll(JSON.parseArray(dataListString, var2));}long endTime = System.currentTimeMillis();long totalTime = (endTime - startTime) / 1000;log.info("请求接口全量数据耗时=>>>{}秒", totalTime);return all;}/*** @param resourceList 源list* @description list转换器 List<E> -> List<T>* @datetime 2022-09-20 16:31:04*/protected abstract List<T> getListConverter(List<E> resourceList);public void handle(List<T> list, IService<T> iService) {long start = System.currentTimeMillis();iService.remove(new QueryWrapper<T>().eq("ITEM_DATA_FROM", DataFromEnum.FXIAOKE.getCode()));iService.saveOrUpdateBatch(list);long end = System.currentTimeMillis();log.info("全量数据处理同步耗时:=>{}秒", (end - start)/ 1000);}public void incrHandle(List<T> list, IService<T> iService) {long start = System.currentTimeMillis();iService.saveOrUpdateBatch(list);long end = System.currentTimeMillis();log.info("增量据处理同步耗时:=>{}", (end - start));}/*** @param type 1 天翎业务表id是纷享的;2 天翎业务表id是天翎的* @param dataId* @param list* @param iService* @description* @datetime 2022-09-22 20:50:44*/public void incrHandlePlus(Integer type, String dataId, Wrapper<T> queryWrapper, List<T> list, IService<T> iService) {long start = System.currentTimeMillis();if (1 == type) {iService.removeById(dataId);} else {iService.remove(queryWrapper);}iService.saveOrUpdateBatch(list);long end = System.currentTimeMillis();log.info("增量数据处理同步耗时:=>{}秒", (end - start) / 1000);}public void dataHandle(String dataObjectApiName, Integer type, List<Filter> filterList, String dataId,String url, Wrapper<T> queryWrapper, IService<T> iService, Class<E> var2) {if (CollectionUtils.isEmpty(filterList) && !StringUtils.hasText(dataId)) {filterList = Arrays.asList(new Filter("is_deleted", Collections.singletonList(Boolean.FALSE), "EQ"),new Filter("life_status", Collections.singletonList("normal"), "EQ"));}if (StringUtils.hasText(dataId)) {filterList.add(new Filter("_id", Lists.newArrayList(dataId), "EQ"));}//请求解析出参List<E> resourceList = this.pickFx(dataObjectApiName, filterList, url, var2);//打印一条数据log.info("list size ==> {}\n, resourceList ==>\n {}", resourceList.size(), JSON.toJSONString(Optional.of(resourceList).map(t -> t.get(0)).orElse((E) "")));if (StringUtils.hasText(dataId)) {this.incrHandlePlus(type, dataId, queryWrapper, getListConverter(resourceList), iService);} else {this.handle(getListConverter(resourceList), iService);this.getListConverterFinall(resourceList);}}protected abstract void getListConverterFinall(List<E> resourceList);}
使用
/*** @author lyonardo* @Description 客户监听订阅事件* @createTime 2022年09月20日 09:38:00*/
@Slf4j
@Service
public class FxAccountListener extends FxBaseListenerAbstract<TlkAccountInfoDO, FxAccountObjBO> {private final ITlkAccountInfoService service = SpringUtil.getBean(ITlkAccountInfoService.class);@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)public void handle(String eventType, String dataId) {if (EventTypeConstants.UPDATE.equals(eventType) || EventTypeConstants.INSERT.equals(eventType)) {//请求解析出参LambdaUpdateWrapper<TlkAccountInfoDO> updateWrapper = new LambdaUpdateWrapper<TlkAccountInfoDO>().eq(TlkAccountInfoDO::getItemThirdId, dataId);this.dataHandle(DataObjectApiNameConstants.ACCOUNTOBJ, dataId, FxCommonEnum.GET.buildUrl(), service, updateWrapper, FxAccountObjBO.class);} else if ( EventTypeConstants.INVALID.equals(eventType) || EventTypeConstants.DELETE.equals(eventType)) {service.remove(new LambdaQueryWrapper<TlkAccountInfoDO>().eq(TlkAccountInfoDO::getItemThirdId, dataId));} else {throw new OsRuntimeException(FailCodeEnum.FAIL);}}@Overrideprotected TlkAccountInfoDO getConverter(FxAccountObjBO resource) {TlkAccountInfoDO tlkAccountInfoDO = TlkAccountInfoDOConverter.INSTANCE.fxAccountObjBo2Do(resource);String mark = CustomerLevelEnum.getMarkByCode(tlkAccountInfoDO.getItemCustomerLevelCode());if(StringUtils.isNotEmpty(mark)){tlkAccountInfoDO.setItemCustomerLevelName(mark);}else if("".equals(mark)){tlkAccountInfoDO.setItemCustomerLevelCode(CustomerLevelEnum.MSTSC.getCode());tlkAccountInfoDO.setItemCustomerLevelName(CustomerLevelEnum.MSTSC.getDescription());}else {tlkAccountInfoDO.setItemCustomerLevelCode("cooperation_price");tlkAccountInfoDO.setItemCustomerLevelName("项目合作价");}return tlkAccountInfoDO;}
}
通过对核心方法的抽取处理以及公共抽象方法的封装,让团队其他开发在进行几十上百个业务对象进行全量、增量、对接开发时,只需要关注和实现业务对象的handle方法和对象转换处理getConverter,不用关注具体的细节,不仅大大减少了代码重复量和工作量,也大大降低了易错率。