模板模式
意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
主要解决:一些方法通用,却在每一个子类都重新写了这一方法。
如何解决:将这些通用算法抽象出来。
关键代码:在抽象类实现,其他步骤在子类实现。
需求: 多个资源设计表,关联多个资源,重复操作
表结构
CREATE TABLE `materials` (
`id` bigint(20) NOT NULL COMMENT '主键id',
`title` varchar(255) DEFAULT NULL COMMENT '标题',
`task` varchar(255) DEFAULT NULL COMMENT '适用课题',
`scope` tinyint(4) DEFAULT NULL COMMENT '共享范围 0 自己 1 公开',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='教学资源信息表';
CREATE TABLE `files` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
`resource_id` bigint(20) DEFAULT NULL COMMENT '资源id',
`resource_url` varchar(255) DEFAULT NULL COMMENT '资源地址',
`resource_name` varchar(32) DEFAULT NULL COMMENT '资源名称',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='文件表';
函数接口
/**
* 资源函数
*
* @author bai
* @date 2022/10/05
*/
@FunctionalInterface
public interface ResourceFunc<E, F extends FilesEntity> {
/**
* 应用
*
* @param list 列表
* @param b b
* @return {@code E}
*/
void apply(List<E> list, BaseMapper<F> b );
}
提取公共的类属性,关联属性
public class Entity {
private Long id;
/**
* 资源url
*/
@TableField(exist = false)
String resourceUrl;
}
相同的实体
public class BlackboardEntity extends Entity{
/**
* id
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 标题
*/
@TableField(value = "title")
private String title;
/**
* 阶段学习
*/
@TableField(value = "phase_studying")
private String phaseStudying;
/**
* 主题
*/
@TableField(value = "subject")
private String subject;
}
public class FilesEntity extends Entity{
/**
* id
*/
@TableId(type = IdType.AUTO)
Long id;
/**
* 资源id
*/
@TableField
Long resourceId;
/**
* 资源url
*/
@TableField
String resourceUrl;
/**
* 资源名称
*/
@TableField
String resourceName;
}
接口抽象
/**
* 资源服务
*
* @author bcf
* @date 2022/09/16
*/
public interface ResourceService<E extends Entity,F extends FilesEntity> {
/**
* 制定战略
*
* @param strategy 策略
*/
void setStrategy(IService<E> strategy);
/**
* 教学文件集实体基础映射器
*
* @param FilesEntityBaseMapper 教学文件实体基础映射器
*/
void setFilesEntityBaseMapper(BaseMapper<F> FilesEntityBaseMapper);
/**
* 页面
*
* @param ResourceDto 教学资源dto
* @return {@code IPage<E>}
*/
IPage<E> page(ResourceDto ResourceDto);
/**
* 通过id
*
* @param id id
* @return {@code E}
*/
E getById(Long id);
/**
* 更新通过id
*
* @param e e
* @param eClass e类
* @return {@code Boolean}
* @throws InstantiationException 实例化异常
* @throws IllegalAccessException 非法访问异常
*/
Boolean updateById(E e, Class<F> eClass) throws InstantiationException, IllegalAccessException;
}
策略实现
/**
* 资源服务实现
*
* @date 2022/09/16
*/
public class ResourceServiceImpl<E extends Entity, F extends FilesEntity> implements ResourceService<E, F> {
/**
* 策略
*/
private IService<E> strategy;
/**
* 教学文件实体基础映射器
*/
private BaseMapper<F> FilesEntityBaseMapper;
/**
*
* 资源服务实现
*/
public ResourceServiceImpl() {
}
/**
* 制定战略
*
* @param strategy 策略
*/
@Override
public void setStrategy(IService<E> strategy) {
this.strategy = strategy;
}
/**
* 教学文件集实体基础映射器
*
* @param FilesEntityBaseMapper 教学文件实体基础映射器
*/
@Override
public void setFilesEntityBaseMapper(BaseMapper<F> FilesEntityBaseMapper) {
this.FilesEntityBaseMapper = FilesEntityBaseMapper;
}
/**
* 获得资源函数
*
* @return {@code ResourceFunc<E, F>}
*/
public ResourceFunc<E, F> getResourceFunc() {
return (e, b) -> {
List<Long> ids = e.stream().map(Entity::getId).collect(Collectors.toList());
if (ids.size() > 0) {
Map<Long, List<F>> map = b.selectList(
new LambdaQueryWrapper<F>().in(F::getResourceId, ids)
).stream().collect(
Collectors.groupingBy(F::getResourceId)
);
e.forEach(
item -> {
if (map.containsKey(item.getId())) {
item.setResourceUrl(map.get(item.getId()).get(0).getResourceUrl());
}
}
);
}
};
}
/**
* 页面
*
* @param ResourceDto 教学资源dto
* @return {@code IPage<E>}
*/
@Override
public IPage<E> page(ResourceDto ResourceDto) {
IPage<E> page = new Page<>(ResourceDto.getCurrent(), ResourceDto.getSize());
LambdaQueryWrapperUtils<E> lambdaQueryWrapperUtils = new LambdaQueryWrapperUtils<>();
lambdaQueryWrapperUtils.schoolFilter(ResourceDto);
lambdaQueryWrapperUtils.eq(StringUtils.isNotEmpty(ResourceDto.getSubject()), "subject", ResourceDto.getSubject());
lambdaQueryWrapperUtils.eq(StringUtils.isNotEmpty(ResourceDto.getTitleName()), "title", ResourceDto.getTitleName());
page = this.strategy.page(page, lambdaQueryWrapperUtils);
getResourceFunc().apply(page.getRecords(), FilesEntityBaseMapper);
return page;
}
/**
* 通过id
*
* @param id id
* @return {@code E}
*/
@Override
public E getById(Long id) {
E obj = this.strategy.getById(id);
List<F> list = FilesEntityBaseMapper.selectList(
new LambdaQueryWrapper<F>().eq(F::getResourceId, id)
);
if (list.size() > 0) {
obj.setResourceUrl(list.get(0).getResourceUrl());
}
return obj;
}
/**
* 更新通过id
*
* @param e e
* @param fClass f类
* @return {@code Boolean}
* @throws InstantiationException 实例化异常
* @throws IllegalAccessException 非法访问异常
*/
@Override
public Boolean updateById(E e, Class<F> fClass) throws InstantiationException, IllegalAccessException {
this.FilesEntityBaseMapper.delete(
new LambdaQueryWrapper<F>().eq(F::getResourceId, e.getId())
);
F obj = fClass.newInstance();
obj.setResourceUrl(e.getResourceUrl());
obj.setResourceId(e.getId());
this.FilesEntityBaseMapper.insert(obj);
return this.strategy.updateById(e);
}
}
测试
@Resource
private BlackboardService BlackboardService;
@Resource
private FilesDao FilesDao;
public static final ResourceService<BlackboardEntity, FilesEntity> ResourceService =
new ResourceServiceImpl<>();
/**
* 初始化后
*/
@PostConstruct
public void afterInit(){
ResourceService.setStrategy(BlackboardService);
ResourceService.setFilesEntityBaseMapper(FilesDao);
}
/**
* 页面
*
* @param ResourceDto 教学资源dto
* @return {@code Result<IPage<BlackboardEntity>>}
*/
@PostMapping("/page")
public Result<IPage<BlackboardEntity>> page(@RequestBody ResourceDto ResourceDto) {
return Result.ok(ResourceService.page(ResourceDto));
}