java 对于将父方法Map到不同类的子类,擦除相同

bkkx9g8r  于 2023-02-02  发布在  Java
关注(0)|答案(1)|浏览(134)

我试图重构一个DAO,使它在我们的代码库中更有用。我们目前有一个参数化的AbstractDao,它有三种类型:
1.数据库表
1.数据库pojo

  1. 2)的不同Mappojo表示
    所以它看起来像这样:
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()
 );
 }
}

但是,这在经典的DAO情况下不起作用,在这种情况下,我们只处理pojo和表。
然而,这里有一个共同的功能,可以抽象成一个更基本的AbstractDao,在项目中很有用。

AbstractDao<T extends DatabaseTable, R extends Record>

它有一个子类

AbstractMappedDao<T extends DatabaseTable, R extends Record, M> extends AbstractDao<T, R>

摘要的方法如下:

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

并使抽象实现道和Map实现道,但同样的问题。
所以我的问题是如何最好地解决这个问题?如果我将map的签名更改为如下形式,它将按预期工作:

insertMapped(List<M> mapped);

但我希望合同保持不变。
谢谢你的帮助。期待讨论!

oaxa6hgo

oaxa6hgo1#

当涉及到 * 组合行为 * 时,最好是使用组合而不是继承,这实际上就是你的情况。mapper并没有 * 增加 * Dao中已经存在的行为,而是 * 增加 * 了行为,这是一个额外的间接层;这不一定是X1 M2 N1 X(如X1 E0 F1 X)所关心的。
因此,我的建议是创建一个能够组合mappersAbstractDao类(您可以根据需要只创建一个类;但是使用复合很容易允许单个Dao对象支持多个Map器):

private Map<Class, Function> mappers;
        
        public <M> void registerMapper(Class<M> mappingClass, Function<M, R> mapper) {
            mappers.put(mappingClass, mapper);
        }

然后创建一个insert方法,该方法允许使用已注册的mappers处理未扩展Record的记录的 * 预转换 *,如下所示:

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可以通过 composed concerns 以集中的方式处理各种关注点。完整的编译代码看起来如下所示:

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

Complete code on GitHub

相关问题