2009-08-26 60 views
11

我在一家公司工作,其中一些需要在我們的代碼(Visual Studio C#3.5)中使用接口的理由。接口的好例子

我想問一個Iron Clad推理,需要接口。 (我的目標是證明接口是編程的一個正常部分。)

我不需要說服力,我只需要一個很好的論據來用於說服別人。

的一種說法我要尋找的是基於事實,而不是基於比較(即「因爲.NET庫使用他們」是基於比較。)

對他們的說法是這樣的:如果一個類正確設置(使用其公共和私人成員),那麼接口只是額外的開銷,因爲使用該類的人僅限於公共成員。如果你需要一個由多於一個類實現的接口,那麼只需設置繼承/多態。

+1

我不明白反對... – 2009-08-26 15:36:52

+16

的論點,如果你需要向你的團隊解釋爲什麼接口是一個很好的編程標準,那麼你應該找到一份新的工作..沒有冒犯.. – 2009-08-26 15:37:18

+1

我同意找到一個新的工作評論。 – Finglas 2009-08-26 15:42:21

回答

26

代碼解耦。通過對接口進行編程,可以使用實現接口的代碼中的接口來解耦代碼。這使您可以更改實現,而無需重構所有使用它的代碼。這與繼承/多態性結合使用,允許您交換使用多種可能的實現中的任何一種。

模擬和單元測試。當方法是虛擬的時候,模擬框架是最容易使用的,默認情況下使用的是接口。這實際上是我創建界面的最大原因。

定義可能適用於許多不同類的行爲,即使在類之間沒有關係(定義的行爲除外)時也允許它們互換使用。例如,Horse和Bicycle類可能都有一個Ride方法。您可以定義一個定義Ride行爲的IRideable接口,任何使用此行爲的類都可以使用Horse或Bicycle對象,而不會強制它們之間的非自然繼承。

+0

我打算寫這樣的東西,但我沒有足夠的口才。所以我讓別人做得更好。解耦是它爲靈活性,可管理性和可重用性提供的最重要的部分編程。 – 2009-08-26 15:39:09

+2

作爲Horse/Bicycle評論的補充,關於接口的好處是能夠在你的類上概括操作而不會限制你的繼承:http://stackoverflow.com/questions/558152/asp-net-unfamiliar-with-接口/ 558342#558342 – Juliet 2009-08-26 16:21:35

+0

如果實現更改涉及新的方法簽名,則添加此類方法會使所有使用該接口的客戶端中斷。那是什麼樣的解耦?我們是否應該瞄準界面和抽象類的組合? – IrishChieftain 2010-01-12 20:00:51

1

如果您的商店正在執行自動化測試,那麼接口對依賴注入以及能夠獨立測試軟件單元都有很大的幫助。

2
  • 測試驅動開發
  • 單元測試

不會產生分離代碼的接口將是一個痛苦。最佳實踐是針對接口而不是具體實現進行編碼。接口看起來似乎是垃圾,但一旦你發現你將永遠使用它們的好處。

6

啓用該類的單元測試。

爲了有效地跟蹤依賴關係(如果接口未被檢出並且被觸及,只有該類的語義可能已經改變)。

因爲沒有運行時間開銷。

啓用依賴注入。

......也許是因爲它是2009年的,而不是70年代的,現代語言設計師實際上對他們在做什麼有了線索?

不應該在每個類接口拋出接口:只是那些對系統至關重要的接口,以及可能會經歷重大更改和/或擴展的接口。

2
  • 您可以實現多個接口。你不能從多個類繼承。
  • ..就是這樣。其他人關於代碼解耦和測試驅動開發的觀點並沒有涉及到問題的關鍵,因爲你也可以用抽象類來完成這些工作。
+0

不一定,抽象類將在有共同的邏輯共享時使用。我可能有2個類實現相同的接口,但具有完全不同的結構。 – 2009-08-26 15:43:51

+0

如果您只是要覆蓋基本實現,爲什麼還要抽象類呢?因此接口更適合單元測試。抽象類雖然有其自己的位置。 – Finglas 2009-08-26 15:45:55

+0

我同意抽象類顯然應該提供一些實現,否則它們應該是接口。但我直接回答了這個問題。 – 2009-08-26 15:47:56

10

除了事情在其他的答案解釋,interfaces讓你模仿,否則不允許在.NET 多重繼承

唉有人說

技術是由兩類人爲主:那些誰知道他們不管理,和那些誰管理他們不明白。

+2

這是一個很好的觀點。因爲.NET不支持多重繼承,所以接口的使用非常必要。 – 2009-08-26 15:42:43

+0

Meta-Knight:這是黨派,但我不確定我是否相信。難道你不能以不同的方式來組織結構,不需要在一個子類中使用多個超類?你是否也會說「因爲.NET不支持多派遣,所以編寫你自己的調度器是非常必要的」?總是有更強大的對象系統,但你並不總是必須模擬所有的功能才能完成某些工作。 – Ken 2009-08-26 20:13:25

2

接口允許你來聲明當中可以多種類型(IEnumerable),同時允許每個這些類型的有它自己的繼承層次共享的概念。

在這種情況下,我們所說的是「這件事可以枚舉,但這不是它的單一定義特徵」。

接口允許您在定義實施者的能力時做出最少量的決定。當你創建一個類而不是一個接口時,你已經聲明你的概念只是類而不能用於結構。在聲明班級中的成員時,您還可以做出其他決定,例如可見性和虛擬性。例如,您可以創建一個具有所有公共抽象成員的抽象類,並且這個抽象類與接口非常接近,但是您已經在所有子類中聲明該概念爲可覆蓋的,而您不必創建如果你使用了一個接口的話。他們也使單元測試更容易,但我不認爲這是一個強有力的論據,因爲你可以建立一個沒有單元測試(不推薦)的系統。

4

接口和抽象類建模不同的東西。當你有一個isA關係時,你從一個類派生出來,所以基類對某些具體的東西進行建模。當您的課程可以執行一組特定的任務時,您可以實現一個接口。

想到Serializable的東西,從一個設計/建模的角度來看,它沒有任何意義,因爲它有一個名爲Serializable的基類,因爲它沒有任何意義可說是一個Serializable。有一些實現Serializable接口更有意義,說'這是類可以做的事,而不是類是什麼'

+1

+1最後一句! – 2009-08-26 15:57:20

0

我不明白它的額外開銷。

接口提供了靈活性,可管理的代碼和可重用性。編碼到一個接口,你不必擔心你正在使用的特定類的具體實現代碼或邏輯。你只是期待一個結果。許多類對於同一個特性(StreamWriter,StringWriter,XmlWriter)都有不同的實現。你不必擔心它們如何實現寫作,你只需要調用它。

+3

如果您創建自己的接口,則額外開銷,因爲您必須創建並維護更多的代碼。對於每個要插入界面的方法,都必須複製方法簽名。然後,每次需要更改方法簽名時,都需要至少在兩個地方進行更改。 – airportyh 2009-08-26 16:28:59

+0

你在這裏支持兩個論點。 StreamWriter和StringWriter是抽象類(TextWriter)的實現;除了Object和IDisposable,XmlWriter是無關的。 – 2009-08-26 16:43:43

+0

是的好點,我沒有想到如何.NET實現它。我試圖想到一個共享一個功能(Write)的類的快速示例,但實現它們的方式不同。 – 2009-08-26 16:47:50

1

繼承參數的問題是你要麼有一個巨大的神類或層次如此之深,它會讓你的頭旋轉。最重要的是,你最終會在一個你不需要或者沒有任何意義的類上使用方法。

我看到很多「沒有多重繼承」,雖然這是真的,但它可能不會階段你的團隊,因爲你可以有多個級別的繼承來獲得他們想要的。

一個IDisposable實現浮現在腦海。你的團隊會在Object類上放置一個Dispose方法,並讓它在系統中傳播,而不管它是否適用於某個對象。

18

對他們的說法是這樣的:如果 一類是正確設置(其 公共和私有成員),那麼將 接口只是額外的開銷 因爲那些使用類是 僅限於公共成員。如果你的 需要有一個接口是 由多於一個類實現,那麼 只是設置繼承/多態。

考慮下面的代碼:

interface ICrushable 
{ 
    void Crush(); 
} 

public class Vehicle 
{ 
} 

public class Animal 
{ 
} 

public class Car : Vehicle, ICrushable 
{ 
    public void Crush() 
    { 
    Console.WriteLine("Crrrrrassssh"); 
    } 
} 

public class Gorilla : Animal, ICrushable 
{ 
    public void Crush() 
    { 
    Console.WriteLine("Sqqqquuuuish"); 
    } 
} 

它使任何意義,建立一個類層次結構涉及動物的車輛,即使兩者都可以通過我的巨大的破碎機粉碎?編號

+2

+1對於廣場大猩猩的好例子 – usr 2009-08-27 16:29:02

1

一個接口聲明一個約定,任何實現它的對象都將遵守。這使得確保代碼的質量比試圖強制執行書面(而非代碼)或口頭結構要容易得多,當類使用接口引用進行修飾時,需求/合同是明確的,代碼在您實施之前不會編譯該接口完全和類型安全。

有使用接口(這裏列出),但可能不會與管理層相當的共鳴,以及一個良好的,老式的「質量」語句其他許多偉大的理由;)

1

好了,我的第一反應是,如果你必須解釋爲什麼你需要接口,這是一場艱苦的戰爭:)無論如何,這是一場艱苦的戰鬥:)

除了上面提到的所有原因外,接口是鬆散耦合編程的唯一途徑,n層體系結構你需要在飛行中更新/替換組件等 - 但是在個人經驗中,這對於架構團隊負責人來說太深奧了,結果導致我們住在地獄裏 - 在.net世界不會少!

1

請原諒我提前僞代碼!

閱讀SOLID原則。使用接口的SOLID原則有幾個原因。接口允許你解耦你的依賴實現。您可以通過使用像StructureMap這樣的工具來進一步提高耦合度。

在那裏你可以用來

Widget widget1 = new Widget; 

這明確表示,要創建控件的新實例。但是,如果您在另一個對象的方法中執行此操作,則您現在正在說另一個對象直接依賴於Widget的使用。所以我們可以這樣說

public class AnotherObject 
{ 
    public void SomeMethod(Widget widget1) 
    { 
     //..do something with widget1 
    } 
} 

我們仍然綁定在這裏使用Widget。但至少這是更可測試的,因爲我們可以將Widget的實現注入到SomeMethod中。現在,如果我們要使用接口,我們可以進一步解耦。

public class AnotherObject 
{ 
    public void SomeMethod(IWidget widget1) 
    { 
     //..do something with widget1 
    } 
} 

請注意,我們現在不需要Widget的特定實現,而是我們要求的任何內容符合IWidget接口。這意味着可以注入任何東西,這意味着在代碼的日常使用中我們可以注入一個實際的Widget實現。但是這也意味着,當我們想測試這個代碼時,我們可以注入一個假/模擬/存根(取決於你對這些術語的理解)並測試我們的代碼。

但是我們怎樣才能更進一步。通過使用StructureMap,我們可以更多地解耦這些代碼。有了上次的代碼示例我們調用代碼我是這個樣子

public class AnotherObject 
{ 
    public void SomeMethod(IWidget widget1) 
    { 
     //..do something with widget1 
    } 
} 

public class CallingObject 
{ 
    public void AnotherMethod() 
    { 
     IWidget widget1 = new Widget(); 
     new AnotherObject().SomeMethod(widget1); 
    } 
} 

正如你在我們通過傳遞符合iWidget的物體移除了的someMethod的依賴上面的代碼中看到。但是在CallingObject()。AnotherMethod中我們仍然有依賴關係。我們也可以使用StructureMap去除這個依賴關係!

[PluginFamily("Default")] 
public interface IAnotherObject 
{ 
    ... 
} 

[PluginFamily("Default")] 
public interface ICallingObject 
{ 
    ... 
} 

[Pluggable("Default")] 
public class AnotherObject : IAnotherObject 
{ 
    private IWidget _widget; 
    public AnotherObject(IWidget widget) 
    { 
     _widget = widget; 
    } 

    public void SomeMethod() 
    { 
     //..do something with _widget 
    } 
} 

[Pluggable("Default")] 
public class CallingObject : ICallingObject 
{ 
    public void AnotherMethod() 
    { 
     ObjectFactory.GetInstance<IAnotherObject>().SomeMethod(); 
    } 
} 

注意,在上面的代碼中沒有我們實例化AnotherObject的實際實現。因爲一切都爲StructurMap連線,所以我們可以根據代碼運行的時間和地點,允許StructureMap傳遞適當的實現。現在代碼非常靈活,我們可以通過配置或編程方式指定我們想要使用哪種實現。這種配置可以實時完成或作爲構建過程的一部分等,但不一定要在任何地方進行硬連線。

1

因爲這個問題沒有回答你關於接口案例的問題。

不過我建議得到有關閱讀的人..

Head First Design Patterns

- 李

3

接口不「要求」可言,這是一個設計決策。我認爲你需要說服自己,爲什麼在個案的基礎上,使用一個接口是有益的,因爲在添加一個接口時會產生開銷。另一方面,爲了對抗接口的爭論,因爲你可以'簡單'使用繼承:繼承有其缺點,其中之一就是 - 至少在C#和Java中 - 你只能使用繼承一次(單一繼承)。但是第二個 - 也許更重要的是,繼承要求你不僅要理解父類的運作,還要理解所有的祖先類,它們使得擴展更難但也更脆弱,因爲父類的變化實現很容易破壞子類。這是GOF書教給我們的「構圖與繼承」論點的關鍵。

+0

什麼是GOF書? – Vaccano 2009-08-26 18:26:06

+0

http://www.amazon.com/Design-Patterns-Object-Oriented-Addison-Wesley-Professional/dp/0201633612 – airportyh 2009-08-26 19:33:07

3

您已獲得一套您的老闆認爲適合您的工作場所和問題領域的指導方針。因此,要改變這些指導原則,並不是要證明接口是一件好事,而是要證明在工作場所需要接口。

您如何證明您在工作場所編寫的代碼中需要接口?在基於接口的解決方案比基於繼承的解決方案更好的情況下,通過在實際的代碼庫中找到一個位置(不是在某人的某個產品的代碼中,當然也不是在某些關於Duck在IAnimal中實現makeNoise方法的玩具示例中)。向你的老闆展示你面臨的問題,並詢問修改準則以適應這種情況是否合理。這是一個可教的時刻,每個人都在看着相同的事實,而不是用普遍性和猜測來打擊對方。

該指南似乎是由避免過度工程和過早概括引起的。所以,如果你沿着的方向提出論點,我們應該在這裏有一個接口,以防萬一將來我們必須......,它的意圖很好,但對於你的老闆,它引發了同樣的過度工程警報鈴聲首先激發了指導原則。

等到它有一個好的客觀案例,這既適用於您在生產代碼中使用的編程技巧,也適用於您與管理人員開始爭論的事情。