2012-06-02 60 views
1

我想提出這樣一個場景,這將是不錯的:擴展泛型類並實現其接口的第二次

  • 有一個通用的接口,
  • 有不同的通用基礎類實現它,
  • 爲泛型基類提供子類,
  • 以某種方式獲得子類的非泛型接口。

困惑?請閱讀並告訴我您對解決方案的看法。

比方說,我想要一個服務提供基於任何類型的鑰匙 和任何地方(DB,文件等)的任何類型的對象。

對於一個良好的開端讓我們創建一個合適的接口:

public interface ObjectProvider<K, V> { 

    V provide(K key); 
} 

這樣我就能提供任何種類的實現。 我想從數據庫和文件提供對象。

讓我們假設提供基於K的V可以使用相同的 數據庫邏輯完成,而不管對象類型如何。所以,我可以寫一個通用的基類 爲DB訪問:

public class DBObjectProvider<K, V> implements ObjectProvider<K, V> { 

    public V provide(K key) { 
     V v = null; 
     //some common DB logic to get V based on K 
     return v; 
    } 
} 

其實,從文件中獲取的對象也是對象類型獨立:

public class FileObjectProvider<K, V> implements ObjectProvider<K, V> { 

    public V provide(K key) { 
     V v = null; 
     //some common file reading logic to get V based on K 
     return v; 
    } 
} 

好了,現在我有兩個通用類,我可能會用它來得到我想要的任何東西。

現在,我想使用這些泛型實現之一來獲取基於數據庫中的String鍵的String對象。另外,我想將它定義爲使用Spring XML的bean。 我想在Spring XML中沒有辦法定義泛型bean(對嗎?),所以我 將創建一個適當的類。所有我需要做的是:

public class DBStringStringProvider extends DBObjectProvider<String, String> { 
} 

現在我可以這個bean注入到任何:

private ObjectProvider<String, String> objectProvider; 

一切都很好,現在的關鍵組成部分。 我可以在很多方面使用我的有用數據庫字符串字符串提供程序(hhmmm ...確定不是真的)。假設我想要製作一個Web服務。 假設我想將DBStringStringProvider作爲CXF Web服務公開並測試它。 的問題是,這樣的Web服務客戶端的代碼如下所示:

JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean(); 
factory.setServiceClass(<INTERFACE>.class); 
<INTERFACE> client = (<INTERFACE>) factory.create(); 

,所以我需要一個非通用接口DBStringStringProvider我沒有因爲它擴展了通用基礎類。

我可以做到以下幾點:

延長ObjectProvider <字符串,字符串>接口與另一個問題:

@WebService 
public interface StringStringProvider extends ObjectProvider<String, String> { 

    @WebMethod 
    String provide(String key); 
} 

而且實現它在那裏排序已經由通用基類實現:

public class DBStringStringProvider extends DBObjectProvider<String, String> 
implements StringStringProvider { 
} 

我感覺有點不滿意實施相同的界面,它是已經由基類引入。 但這種方式我能夠利用WS客戶端:

factory.setServiceClass(StringStringProvider.class); 
StringStringProvider client = (StringStringProvider) factory.create(); 

我的問題是:這是一個很好的做法,做什麼,我只是做了?如果沒有,有沒有其他辦法?

這只是一種情況,如果對於已定義爲通用的東西 有一個非通用接口將是非常好的。

+3

我總是設想繼承這種代碼的可憐傢伙。 –

+0

它有什麼問題? – Dzik

+3

@Dzik:回答你的問題:太複雜了...保持簡單,問問自己這樣的設計的優點真的是什麼(保持可維護性)。 – home

回答

1

我不喜歡批評所提問題中抽象層次的動機。爲了接口的一致性和重複代碼的減少,這些情況不時出現。特別是Java和icky服務框架使得這個任務比它需要的複雜得多;這種不必要的複雜性並不意味着原始設計是不好的。每天Java都會讓我因任何有關無意義冗長的原因而咬緊牙關。

我會高興地確認,定義一個無償的混凝土子接口是可以接受的和常見的做法。由於您正在解決服務框架中的限制(參數化接口不允許),您不得不採取此措施。

唯一需要關注的是你真的試圖在Java中使用typedef。我已經能夠做到的最好的事情是w.r.t. Java中的typedefs會導致一個令人震驚的醜陋結構,它會讓評論者在你的原始帖子上,根據他們對複雜性的厭惡來判斷他們想要衡量自己的眼睛。

我懷疑在實踐中你會遇到什麼問題,但一般而言psuedo-typedefs比如這個例子會引起意想不到的痛苦。在一般情況下,當您創建接口S的普通子接口T時,很容易使用T代替S,無論它應用於何處。例如,S可能是一個冗長的參數化Java類表達式,它可能是一種閱讀或鍵入的屁股,而T可能是很好很短的。但是對於方法/服務調用/構造函數參數,您仍然需要S作爲聲明的類型,否則您可能會限制方法或類的用處。一個人爲的例子:

public interface Info extends Map< String, List<FooBar> > {} 

public interface Service { 
    public Info doSomething(Info info); 
} 

沒有理由爲Service爲僅接受Info,如Info不會增加API來Map< String, List<FooBar> >;正如所寫,doSomething的調用者不能將任何舊地圖作爲輸入;他們必須以某種方式創建新的Info並使用該映射的內容對其進行初始化,因爲Java(與幾乎所有其他廣泛使用的企業語言一樣)使用主格式鍵入。

+0

感謝您提供了一個非常好的客觀答案以及僞類型定義的鏈接。 – Dzik