最近接了个需求,有五个专业技术需要人工录入我们系统,每个技术的字段并不相同,于是设计了五张表,然后开始着手实现对应的接口,由于需要统一的入口可以录入这五个技术,所以我首先想到的使用策略模式,先定义好策略接口类,如下所示。
public interface OrganGraftingTechStrategy<T,R> {
void addTech(T request);
void editTech(T request);
void deleteTech(Long id);
R getTech(Long id);
void uploadExcelTech(MultipartFile file,T request) throws IOException;
}
定义了包括增删改查,还有一个通过excel上传数据的接口
如下贴出一个对应类的实现
@Service
@Slf4j
public class KidneyTechServiceImpl implements OrganGraftingTechStrategy<ReportMB20Request, ReportMB20> {
@Resource
private OfficeExcelApi officeExcelApi;
@Resource
private UploadExcelSlowThread uploadExcelSlowThread;
@Resource
private ReportMB20Service reportMB20Service;
@Override
public void addTech(ReportMB20Request request) {
ReportMB20 reportMb20 = BeanUtil.copyProperties(request, ReportMB20.class);
LoginUser loginUser = LoginContext.me().getLoginUser();
reportMb20.setHospitalCode(loginUser.getOrgCode());
reportMb20.setReportTime(DateUtil.format(new Date(), DatePattern.NORM_DATETIME_FORMAT));
reportMB20Service.save(reportMb20);
}
@Override
public void editTech(ReportMB20Request request) {
ReportMB20 reportMB20New = BeanUtil.toBean(request, ReportMB20.class);
reportMB20Service.updateById(reportMB20New);
}
@Override
public void deleteTech(Long id) {
reportMB20Service.removeById(id);
}
@Override
public ReportMB20 getTech(Long id) {
return reportMB20Service.getById(id);
}
@Override
public void uploadExcelTech(MultipartFile file, ReportMB20Request request) throws IOException {
LoginUser loginUser = LoginContext.me().getLoginUser();
List<ReportMB20BO> list = officeExcelApi.easyReadToList(file.getInputStream(), ReportMB20BO.class);
if (CollUtil.isEmpty(list)) {
throw new ReportException(ReportExceptionEnum.B60001, "无记录上传!");
}
List<ReportMB20> reportMB20List = BeanUtil.copyToList(list, ReportMB20.class);
for (ReportMB20 reportMB20 : reportMB20List) {
reportMB20.setStatus0("no_submit");
reportMB20.setHospitalCode(loginUser.getOrgCode());
reportMB20.setCreateUser(loginUser.getUserId());
}
Future<List<ReportMB20>> future = uploadExcelSlowThread.getList(
s -> {
List<ReportMB20> s1 = (List<ReportMB20>) s;
reportMB20Service.saveBatch(s1);
}
, reportMB20List);
try {
future.get();
} catch (Exception e) {
log.error("上传G01失败,{}", e);
throw new ReportException(ReportExceptionEnum.B60001, "上传异常!");
}
}
}
另外四个技术的实现类也是如此,代码写完并提交,部署测试完没有问题,过几天闲下来想仔细看看自己写的代码,总觉得不够优雅,重复代码也比较多,每个类里面实现的基本都是相同的逻辑,只不过对应的类不同而已,能不能通过抽取公共方法,来减少这些恶心的重复代码呢,答案肯定是可以的,以前一直知道可以通过模板方法抽取公共方法,于是上网查询下具体的用法,然后得出思路如下:
1.定义好抽象类,用它来实现我们定义好的策略接口类,然后具体的技术实现类去继承抽象类,这样我们的技术类就可以用抽象类里面定义好的公共逻辑
2.分析具体哪些方法中的那些逻辑可以抽取公共方法,第一个就是上述接口的增删改查,拿新增举例,代码逻辑:
- 将请求request转换成具体的entity类,
- 然后再有一些相同的塞入用户信息的操作
- 最后存入对应的表
这里我们可以将类的转换以及获取用户信息抽取出来,然后将塞入用户信息,以及入库定义成两个新的方法,其中的逻辑由子类实现,并且当前需要转换的类也是需要子类告诉当前抽象类的,所以还需要一个获取需要转换的entity类
@Service
@Slf4j
public abstract class AbstractOrganGraftingTechService<T, R> implements OrganGraftingTechStrategy<T, R> {
@Resource
protected OfficeExcelApi officeExcelApi;
@Resource
protected UploadExcelSlowThread uploadExcelSlowThread;
protected abstract void saveEntity(R entity);
protected abstract void updateEntityById(R entity);
protected abstract void removeEntityById(Long id);
protected abstract R getEntityById(Long id);
protected abstract Class<R> getEntityClass();
protected abstract void setAdditionalFields(R entity, LoginUser loginUser);
@Override
public void addTech(T request) {
R entity = BeanUtil.copyProperties(request, getEntityClass());
LoginUser loginUser = LoginContext.me().getLoginUser();
setAdditionalFields(entity, loginUser);
saveEntity(entity);
}
@Override
public void editTech(T request) {
R entity = BeanUtil.toBean(request, getEntityClass());
updateEntityById(entity);
}
@Override
public void deleteTech(Long id) {
removeEntityById(id);
}
@Override
public R getTech(Long id) {
return getEntityById(id);
}
@Override
public void uploadExcelTech(MultipartFile file, T request) throws IOException {
LoginUser loginUser = LoginContext.me().getLoginUser();
List<R> list = officeExcelApi.easyReadToList(file.getInputStream(), getEntityClass());
if (CollUtil.isEmpty(list)) {
throw new ReportException(ReportExceptionEnum.B60001, "无记录上传!");
}
for (R entity : list) {
setAdditionalFields(entity, loginUser);
}
Future<List<R>> future = uploadExcelSlowThread.getList(
s -> saveBatchEntities((List<R>) s),
list
);
try {
future.get();
} catch (Exception e) {
log.error("上传失败,{}", e);
throw new ReportException(ReportExceptionEnum.B60001, "上传异常!");
}
}
protected abstract void saveBatchEntities(List<R> entities);
}
对应的子类继承抽象类样例如下如下
@Service
@Slf4j
public class KidneyTechServiceImpl extends AbstractOrganGraftingTechService<ReportMB20Request, ReportMB20> {
@Resource
private ReportMB20Service reportMB20Service;
@Override
protected void saveEntity(ReportMB20 entity) {
reportMB20Service.save(entity);
}
@Override
protected void updateEntityById(ReportMB20 entity) {
reportMB20Service.updateById(entity);
}
@Override
protected void removeEntityById(Long id) {
reportMB20Service.removeById(id);
}
@Override
protected ReportMB20 getEntityById(Long id) {
return reportMB20Service.getById(id);
}
@Override
protected Class<ReportMB20> getEntityClass() {
return ReportMB20.class;
}
@Override
protected void setAdditionalFields(ReportMB20 entity, LoginUser loginUser) {
entity.setHospitalCode(loginUser.getOrgCode());
entity.setReportTime(DateUtil.format(new Date(), DatePattern.NORM_DATETIME_FORMAT));
}
@Override
protected void saveBatchEntities(List<ReportMB20> entities) {
reportMB20Service.saveBatch(entities);
}
}
由上明显看出,使用了模板方法后,减少了一些重复代码,代码看起来更简洁了,另外,当我们后续需要有新的技术加入进来,去实现的代码量明显会少了很多