簡體   English   中英

使用 Generics 在 java 中實現工廠模式

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM