一、概述
定义一个操作中的算法的骨架,将一些步骤延迟到子类中。 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 纷享事件变更订阅处理基类:纷享事件变更返回值有书大局id,通过id查询详情;根据变更事件类型,天翎进行相应操作* @createTime 2022年09月24日 14:35:00*/
@Slf4j
public abstract class FxBaseListenerAbstract<T, E> {private final AccessTokenServcie accessTokenServcie = SpringUtil.getBean(AccessTokenServcie.class);/* private final MongoService mongoService = SpringUtil.getBean(MongoService.class);public E pick(String dataId, Class<E> var2){return mongoService.findById(dataId,var2);}*/public E pickFx(BaseObjectDataBO baseObjectDataBO, String url, Class<E> var2){String detalString = pickFxResult(baseObjectDataBO,url);return JSON.parseObject(detalString, var2);}protected abstract T getConverter(E resource);public void dataHandle(String dataObjectApiName, String dataId , String url, IService<T> iService, LambdaUpdateWrapper<T> updateWrapper, Class<E> var2){GetDetailObjectDataBO getDetailObjectDataBO = buildGetDetailObjectDataBO(dataObjectApiName, dataId);E fxObjBO = this.pickFx(getDetailObjectDataBO, url, var2);if(Objects.nonNull(fxObjBO)){T tlkObjDO = getConverter(fxObjBO);log.info("FxBaseListenerAbstract==>dataHandle getConverter对象转换结果tlkObjDO=>{}", JSON.toJSONString(tlkObjDO));if(Objects.nonNull(tlkObjDO)){iService.saveOrUpdate(tlkObjDO, updateWrapper);}else {log.warn("FxBaseListenerAbstract==>dataHandle getConverter对象转换出现异常");}}else {log.warn("FxBaseListenerAbstract==>dataHandle没有pickFx到纷享对象数据");}}public String pickFxResult(BaseObjectDataBO baseObjectDataBO, String url){Assert.notNull(accessTokenServcie,"没有获取到纷享token");//获取接口授权tokenFxiaokeAccessToken fxiaokeAccessToken = accessTokenServcie.getAccessToken();Assert.notNull(fxiaokeAccessToken,"没有获取到纷享token");//组装接口入参FxiaokeApiReqBO fxiaokeApiReqBO = FxiaokeUtil.buildInvalidOrGetParam(fxiaokeAccessToken, accessTokenServcie.getCurrentOpenUserId(), baseObjectDataBO);//入参转换jsonString jsonString = JSON.toJSONString(fxiaokeApiReqBO);log.info("数据处理同步纷享接口入参=>{}", jsonString);//调用纷享预设对象(属性对象)APIString result = OsHttpClient.create().post(url, jsonString);return FxiaokeUtil.handleResponseResult(result);}
}
使用
/*** @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,不用关注具体的细节,不仅大大减少了代码重复量和工作量,也大大降低了易错率。
PS:在实际工作过程中,中高级同事不仅询问Java不是单继承吗?耐心解说abstract类的特点并po出Spring源码解答;帮解决属性属性值等全量增量对接问题并重写、自测。某说某在哪用到了啥啥啥设计模式,他们也说他们也用到了啥啥啥设计模式。问啥啥啥设计模式用在哪了、能把路径发某学习下吗?然后支支吾吾,这样子就比较不太好沟通。