使用模板方法使你的代码更优雅

195 阅读4分钟

最近接了个需求,有五个专业技术需要人工录入我们系统,每个技术的字段并不相同,于是设计了五张表,然后开始着手实现对应的接口,由于需要统一的入口可以录入这五个技术,所以我首先想到的使用策略模式,先定义好策略接口类,如下所示。

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);
    }
}

由上明显看出,使用了模板方法后,减少了一些重复代码,代码看起来更简洁了,另外,当我们后续需要有新的技术加入进来,去实现的代码量明显会少了很多