[英]Implements a Factory Pattern in java with using Generics
我需要 Java Generics 方面的帮助。 我的 model 是:我有一些扩展 Dto(数据传输对象)的类和一些扩展实体的类(我的 object 的 model 到 DB)。 我有
interface Mapper<D extends Dto, E extends Entity>{
//Convert a Entity to Dto.
D toDto(E entity);
而且我有一些实现这个接口的类(即PersonMapper、BookMapper 等等)。
@Component
public class PersonMapper implements Mapper<PersonDto, PersonEntity> {
//implementation
}
@Component
public class BookMapper implements Mapper<BookDto, BookEntity> {
//implementation
}
我想要做的是使用工厂模式以便在我的 Mapper 运行时使用 select,这取决于我在输入中传递的字符串。
@Autowired
private PersonMapper personMapper;
@Autowired
private BookMapper bookMapper;
public <D extends Dto, E extends Entity> Mapper<D, E> selectMapper(String entity){
if ("Person".equalsIgnoreCase(entity))
return personMapper;
if("Book".equalsIgnoreCase(entity))
return bookMapper;
...
}
在这种情况下,我有以下编译错误:
Type mismatch: cannot convert from PersonMapper to Mapper<D,E>
我的解决方案:
1)
return (Mapper<D, E>) personMapper;
但我有一个警告:
Type Safety: `Unchecked class from personMapper to Mapper<D,H>`
2)
使用通配符和 castb
public Mapper<Dto, Entity> selectMapper(String entity){
Mapper<? extends Dto, ? extends Entity> toReturn = null;
if ("Person".equalsIgnoreCase(entity))
toReturn = personMapper;
else if("Book".equalsIgnoreCase(entity))
toReturn = bookMapper;
...
return (Mapper<Dto, Entity>) toReturn;
}
但在这种情况下,我还有一次警告:
Type safety: Unchecked cast from Mapper<capture#29-of ? extends Dto,capture#30-of ? extends Entity> to Mapper<Dto,Entity>
它有效,但似乎不是一个干净的解决方案
3)使用通配符作为返回类型:
public Mapper<? extends Dto, ? extends HistoryEntity> selectMapper(String entity)
但是您知道,根本不建议使用通配符作为返回类型,也对我没有帮助,因为我想使用这个映射器并调用 mapper.toDto 以确保返回类型是扩展 Dto 的东西。
==================================================== ===================
我不解释为什么如果我这样写一个 class 构造函数
public Service<D extends Dto, E extends Entity>{
public Service(Mapper<D,E> mapper){
this.mapper = mapper;
}
}
并且比我注入(例如) bookMapper 它有效。
相反,如果 Mapper<D,E> 是返回类型,则我无法执行此类操作。
==================================================== ===================
我向您请求的帮助是:
为了实现这种逻辑,我如何使用干净的代码原则(避免编译警告、sonarlint 问题等)编写解决方案?
非常感谢您,如果您能抽出一点时间帮助我解决我的问题,我将不胜感激。
那些关于调用者而不是你的代码的变量(D 和 E)。 D 和 E 由调用者决定,因此绝对无法保证PersonDTO
适合。
制作那个Mapper<? extends DTO, ? extends Entity>
Mapper<? extends DTO, ? extends Entity>
Mapper<? extends DTO, ? extends Entity>
(并且没有变量),并且鉴于这些已经是下限,只需Mapper<?, ?>
- 这将起作用,您可以编写return
语句而无需任何强制转换,也不会出现编译器错误或警告。
当然,这意味着调用者有一个几乎没用的类型。
Generics 完全基于“编译时间/写入时间”。 JVM ( java.exe
) 不知道 generics 是什么,实际上它们中的大多数都无法在编译过程中存活下来。 generics 的唯一目的是使编译器标记不正确的代码并避免一些强制转换,仅此而已。
将该字符串转换为 Mapper 的性质完全是运行时的。
因此,如果Mapper<?, ?>
还不够,那么您想要的就是不可能的。 您需要编写编译/写入时可检查的内容,因此在您使用字符串的那一刻,这是不可能的。 例如,方法getPersonMapper()
当然可以返回Mapper<PersonDTO, PersonEntity>
,没问题。
更一般地说(呵呵)听起来你在这里严重地重新发明了各种轮子。 查看 JDBI、JOOQ 和 Hibernate 的教程,了解如何编写 java 代码以与数据库交互。
工厂模式是通过工厂方法组装或创建某些东西的模式,在您的情况下,您只需要按名称获取相应的映射器,因此有一种简单的方法可以做到这一点,因为映射器 bean 是自动装配的,将String getName()
添加到然后 Mapper 接口为每个实现实现它,例如在 BookMapper 中
@Override
public String getName() { return "Book"; }
使用映射器名称作为键和映射器 bean 作为值将映射器 bean 存储在 map 中,然后您可以通过其名称检索它:
@Service
public class SimpleService {
private BookMapper bookMapper;
private PersonMapper personMapper;
private Map<String, Mapper<? extends DTO, ? extends Entity>> mappers = new HashMap<>();
public SimpleService(BookMapper bookMapper, PersonMapper personMapper) {
this.bookMapper = bookMapper;
this.personMapper = personMapper;
mappers.put(bookMapper.getName(), bookMapper);
mappers.put(personMapper.getName(), personMapper);
}
public Mapper<? extends DTO, ? extends Entity> getMapperByName(String mapperName) {
return mappers.get(mapperName);
}
}
您可以将其投射到相应的映射器而不会发出警告。
PersonMapper p = (PersonMapper) simpleService.getMapperByName("Person");
或者您可以在他们的服务中放置不同的映射器并使用该服务来处理您喜欢的代码,毕竟您需要指定的映射器来执行指定的操作:
if(personThings){
personService.doSomeThing();
}
if(bookThings){
bookService.doSomething();
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.