2008-11-01 71 views
19

我讀過關於Spring如何鼓勵你在代碼中使用接口的地方。我沒看到它。在你的spring xml配置中沒有接口的概念。 Spring的哪個部分實際上鼓勵你使用接口(文檔除外)?彈簧和接口

+1

除了關於DI提出的觀點之外,像Spring remoting這樣的東西完全依賴於使用接口。 – Robin 2008-11-03 14:44:11

+0

這是一個非常好的問題。正如你從目前爲止的答案中可以看到的,沒有人給出具體的答案。 – 2012-06-06 15:53:34

回答

25

當你定義爲你的類的接口,它有助於依賴注入。你的Spring配置文件本身沒有關於接口的任何東西 - 你只需要輸入類的名字。

但是,如果你想注入另一個提供「等效」功能的類,使用接口確實有幫助。

例如,說你有一個分析網站內容的類,並且你正在使用Spring進行注入。如果你注入的類知道實際的類是什麼,那麼爲了改變它,你將不得不改變很多代碼來使用不同的具體類。但是如果你創建了一個Analyzer接口,你可以輕鬆地注入你的原始DefaultAnalyzer,就像你可以嘲笑DummyAnalyzer甚至是另一個基本上做同樣事情的東西,比如PageByPageAnalyzer或其他任何東西。爲了使用其中的一個,你只需要改變你在Spring配置文件中注入的類名,而不是遍歷你的代碼改變類。

在我真的開始看到它的用處之前,我花了一個半天的時間。就像大多數(在企業語言中)最終變得有用的東西一樣,在開始增長項目之前,它似乎毫無意義地增加了一些工作,然後通過多做一些工作,發現您節省了多少時間。

+6

雖然你說的是正確的,但它不是以春天爲中心的。它是以Java爲中心的。 – 2012-06-06 15:52:30

2

您可能想要嘗試使用它來更好地看到這一點,從文檔中可能不清楚Spring是如何鼓勵使用接口的。

這裏有幾個例子:

  1. 你正在寫一個需要從資源讀取類(如文件)可以以多種方式(例如,在類路徑中引用,絕對文件路徑,URL等)。你想要在你的類上定義一個org.springframework.core.io.Resource(接口)屬性。然後在你的Spring配置文件中,你只需選擇實際的實現類(例如,org.springframework.core.io.ClassPathResource,org.springframework.core.io.FileSystemResource,org.springframework.core.io.UrlResource等)。 Spring基本上是一個非常通用的工廠。

  2. 如果你想利用Spring的AOP集成(例如添加事務攔截器),你幾乎需要定義接口。您可以在Spring配置文件中定義攔截點,並根據您的界面爲您生成一個代理。

這些都是我個人有經驗的例子。我相信還有更多。

31

Dependency Inversion Principle解釋得很好。特別是,圖4.

答:高級模塊不應該依賴於低級模塊。兩者都應該取決於抽象。

B.抽象不應該依賴細節。細節應該取決於抽象。

翻譯從上面的鏈接的例子爲Java:

public class Copy { 
    private Keyboard keyboard = new Keyboard(); // concrete dependency 
    private Printer printer = new Printer(); // concrete dependency 
    public void copy() { 
     for (int c = keyboard.read(); c != KeyBoard.EOF) { 
      printer.print(c); 
     } 
    } 
} 
與依賴倒置

現在:

public class Copy { 
    private Reader reader; // any dependency satisfying the reader interface will work 
    private Writer writer; // any dependency satisfying the writer interface will work 
    public void copy() { 
     for (int c = reader.read(); c != Reader.EOF) { 
      writer.write(c); 
     } 
    } 
    public Copy(Reader reader, Writer writer) { 
     this.reader = reader; 
     this.writer = writer; 
    } 
} 

現在Copy支持比從鍵盤到打印機只是複製了。

它能夠從任何Reader複製到任何Writer而無需對其代碼進行任何修改。

現在同春:

<bean id="copy" class="Copy"> 
    <constructor-arg ref="reader" /> 
    <constructor-arg ref="writer" /> 
</bean> 

<bean id="reader" class="KeyboardReader" /> 
<bean id="writer" class="PrinterWriter" /> 

或者是:

<bean id="reader" class="RemoteDeviceReader" /> 
<bean id="writer" class="DatabaseWriter" /> 
9

這裏的大多數答案都是某種形式的「你可以輕鬆地換出實現」,但我認爲他們未能回答的原因是爲什麼?部分。對此,我認爲答案几乎是可測試性。無論您是否使用Spring或任何其他IOC框架,使用依賴注入都會使您的代碼更易於測試。在說作者而不是PrinterWriter的情況下,您可以在單元測試中模擬Writer接口,並確保您的代碼按照您期望的方式調用它。如果您直接依賴於課程實施,唯一的選擇是走到打印機並檢查它,這不是非常自動的。此外,如果您依賴於對類的調用結果,無法模擬它可能會阻止您在測試中訪問所有代碼路徑,從而降低其質量(可能)簡單地說,您應該將對象從應用程序邏輯創建圖表。這樣做使您的代碼更易於測試。

0

Spring不會強迫你在任何地方使用接口,這只是一個好習慣。如果你有一個擁有一些接口而不是具體類的屬性的bean,那麼你可以簡單地使用實現相同接口的模型來交換一些對象,這對於某些測試用例很有用。

如果您使用例如Hibernate支持類,您可以爲您的DAO定義一個接口,然後單獨實現它;擁有接口的好處是你可以使用Spring攔截器來配置它,這將允許你簡化你的代碼;您不必編寫任何代碼來引用HibernateExceptions並在最後一段中關閉會話,而且您也不必通過編程來定義任何事務,只需使用Spring聲明性地配置所有這些東西即可。

當您編寫快速且髒的應用程序時,您可以使用JDBC或一些簡單的框架來實現一些簡單的DAO,而這些框架最終不會在最終版本中使用;如果他們實現了一些通用接口,您將能夠輕鬆地將這些組件切換出來。

1

很容易從接口生成代理。

如果您查看任何Spring應用程序,您會看到服務和持久性界面。使得春天的成語確實鼓勵使用界面。它沒有比這更明確。

1

沒有人提到過,在很多情況下不需要創建一個接口,以便實現類可以快速切換,因爲不會有一個以上的實現類。

當不需要創建接口時,類將由對(接口和實現)創建,添加不必要的樣板接口並創建潛在的依賴關係混淆,因爲在XML配置文件中,組件有時會被其接口引用,它的實現,在運行時沒有任何影響,但在代碼約定方面卻不一致。

1

如果您不使用接口,將會導致自動裝配失敗: 有時Spring會爲Bean創建Proxy類。 此代理類不是服務實現的子類,但它重新實現了它的所有接口。 Spring將嘗試自動調用此Bean的實例,但是此Proxy類與Bean類不兼容。所以用Bean類聲明一個字段會導致「不安全的字段分配」異常。你不能合理地知道Spring何時要代理一個服務(也不應該),所以爲了保護自己免受這些意外,你最好的辦法是聲明一個接口並在聲明自動裝配字段時使用這個接口。