java - 将父方法映射到不同类的子类的相同擦除
问题描述
我正在尝试重构 DAO 以使其在我们的代码库中更有用。我们目前有一个参数化的 AbstractDao,它接受三种类型:
- 数据库表
- 数据库 pojo
- 2) 的不同映射 pojo 表示
所以它最终看起来像:
public class AbstractDao<T extends DatabaseTable, R extends DatabaseRecord, M> {
public AbstractDao(Connection connection, Mapper<R,M> mapper) {
//save connection and mapper to protected variables
}
public List<M> insert(List<M> records) {
connection.insertBulk(
StreamEx.of(records).map(mapper::map).toList()
);
}
}
但是,这不适用于我们只处理 pojo 和 table 的经典 DAO 案例。
但是,这里有一个通用功能可以抽象成一个更基本的 AbstractDao,它可以跨项目使用。就像是:
AbstractDao<T extends DatabaseTable, R extends Record>
它有一个子类
AbstractMappedDao<T extends DatabaseTable, R extends Record, M> extends AbstractDao<T, R>
Abstract 有一个方法,如:
public List<R> insert(List<R> records) {
connection.insertBulk(records);
}
并且 Mapped 应该有一个类似的方法:
public List<M> insert(List<M> records) {
super.insert(StreamEx.of(records).map(mapper::map).toList());
}
但是,这会产生“相同的擦除”问题,因为 insert 接受泛型列表。
我尝试将其抽象为一个接口:
public interface Dao<T> {
public List<T> insert(List<T> records);
}
并使 Abstract 实现 Dao 和 Mapped 实现 Dao,但同样的问题。
所以我的问题是如何最好地解决这个问题?如果我将地图的签名更改为以下内容,这将按预期工作:
insertMapped(List<M> mapped);
但我宁愿保持合同不变。
谢谢您的帮助。期待讨论!
解决方案
当涉及组合行为时,最好使用组合而不是继承,这实际上是你的情况。mapper
不会增加你已经存在的行为,Dao
而是增加行为,它是一个额外的间接层;这不一定是 的关注点Dao
,例如方面/横切关注点。
所以,我的建议是创建一个AbstractDao
具有组合能力的类mappers
(你可以只拥有一个你想要的;但是组合很容易让一个Dao
对象支持多个映射器):
private Map<Class, Function> mappers;
public <M> void registerMapper(Class<M> mappingClass, Function<M, R> mapper) {
mappers.put(mappingClass, mapper);
}
然后创建一个insert
方法,该方法允许处理未使用它已注册的扩展的记录的预转换,如下所示:Record
mappers
public <M> List<M> insert(List<M> records) {
if (records.isEmpty()) return records;
M rec = records.get(0);
List<? extends Record> actualRecords = (rec instanceof Record) ?
(List<Record>)records : createMappedRecords(records, rec.getClass());
connection.insertBulk(actualRecords);
return records;
}
这更简洁、更健壮和更可扩展,因为您insert
可以通过组合关注点以集中方式处理各种关注点。完整的编译代码如下所示:
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class ParentErasure {
public abstract class AbstractDao<T extends DatabaseTable, R extends Record> {
private Connection connection;
private Map<Class, Function> mappers = new HashMap<>();
public <M> void registerMapper(Class<M> mappingClass, Function<M, R> mapper) {
mappers.put(mappingClass, mapper);
}
public <M> List<M> insert(List<M> records) {
if (records.isEmpty()) return records;
M rec = records.get(0);
List<? extends Record> actualRecords = (rec instanceof Record) ?
(List<Record>)records : createMappedRecords(records, rec.getClass());
connection.insertBulk(actualRecords);
return records;
}
private <M> List<R> createMappedRecords(List<M> records, Class<? extends Object> recordsClazz) {
Function<M, R> mapper = mappers.get(recordsClazz);
return records.stream()
.map(mapper::apply)
.collect(Collectors.toList());
}
}
public interface Dao<T> {
public List<T> insert(List<T> records);
}
}
class Record {}
class DatabaseTable {}
class DatabaseRecord {}
class Connection {
public void insertBulk(List<? extends Record> records) {}
}
希望这可以帮助。
推荐阅读
- excel - VBA - 不确定我的 if 语句中的错误在哪里(有多个条件)
- javascript - 使用 Reduce、Map 和 Concat 与数组组合的 Javascript 嵌套函数
- python-3.x - 列格式大树的Tkinter Treeview双击问题
- vue.js - 如何在大型 VUE 项目中组织导入 mixins、过滤器和指令
- c# - 如何将两个连接表中的 IQueryable LINQ 结果返回到列表中
? - flutter - 列表
' 不是类型 'Map 的子类型 - docker - 无法从文件安装包,上次元数据过期检查,错误:找不到匹配项,Centos 8
- react-native - React Native:使用钩子和 TextInput 更新状态对象
- android - 公版通知无法展开
- mysql - 在第 1 行的配置文件 /etc/my.cnf 中找到没有前面组的选项