2010-11-20 73 views
1

因此,我對C#和使用接口仍然很陌生,當我認爲我理解他們時,我意識到我並不完全。混亂我發現,我在這裏尋求一些澄清,當你從它我想我錯過了「編程接口」概念

public Interface ISomeInterface 
{ 
    //some methods/properties 
} 

public class FooClass : ISomeInterface 
{ 
    //implemented ISomeInterfaces methods/properties 
} 

創建一個接口,並有一個類繼承和你在執行程序中的某個地方使用這個類對象

public class BarClass 
{ 
    private ISomeInterface _someInterface; 
    public BarClass(ISomeInterface someInterface) 
    { 
    _someInterface = someInterface; 
    } 
    //rest of class 
} 

我的困惑是爲什麼我看到它以這種方式設置。我以爲我會實例化類型FooClass的新對象,以及使用的類型FooClass的對象在構造這樣:

public class BarClass 
{ 
    private FooClass _fooClass; 
    public BarClass(FooClass fooClass) 
    { 
    _fooClass = fooClass; 
    } 
    //rest of class 
} 

缺少什麼我理解這一點?我不認爲我會直接聲明接口的對象?

在此先感謝。

回答

7

這個想法是,BarClass不應該緊密耦合到ISomeInterface的具體實現。

如果使用這樣的:

public BarClass(FooClass fooClass) 

這意味着BarClass可以用這個特定FooClass實施,並沒有別的才能正常工作。而如果你使用:

public BarClass(ISomeInterface fooClass) 

現在BarClass不再緊耦合FooClass。這意味着BarClass的消費者現在可以通過他希望的接口的任何實現,只要它遵守定義的合約(接口)即可。所以如果他想要FooClass他通過FooClass的實例,但是如果他不滿意FooClass,他可以編寫自己的實現並將其傳遞給構造函數,從BarClass的角度來看,這是絕對透明的(它不需要修改)。

您的類之間的弱耦合是OOP最基本的方面之一,因爲它允許您輕鬆地替換一個組件而不必重寫整個應用程序。

1

假設FooClass給數據庫寫了一些東西。您想要測試BarClass而不必實際設置數據庫。如果你創建了一個實現了相同接口的不同的TestFoo,你可以假裝成爲數據庫並且更容易測試你的類; BarClass不必知道它不是在與'真正的'FooClass交談。

+0

我相信,創建新的類如TestFoo僅僅用於購物並不是一個好主意。爲什麼不嘲笑? – 2010-11-20 15:11:45

+0

@Draco Ater,嘲笑通常是可取的,但我不想分心這個概念的主要觀點。無論是動態生成還是手動生成,其基本概念是可以用一個物體代替另一個物體,就像機械標準允許在建築機器中替換不同的兼容部件一樣。在軟件中,這非常有用,尤其對於啓用測試。 – 2010-11-20 15:13:26

0

你有C/C++背景嗎?然後,你應該知道,

private ISomeInterface _someInterface; 

將寫作

private: 
    ISomeInterface& _someInterface; 

在C++中(假設你有一個名爲ISomeInterface的抽象基類)。

這意味着您正在存儲對實現ISomeInterface的對象的引用,而不是此類對象本身。這樣做的好處是你可以將ANY對象傳遞給實現ISomeInterface的BarClass,這樣可以給你更多的靈活性,進行單元測試。

+0

我理解指針,但是在C#中,通過指向實現ISomeInterface的對象的指針會傳遞什麼好處,而不是傳遞對象本身。我想我錯過了你的部分解釋。 – pghtech 2010-11-20 15:04:29

+0

如果你需要的方法不是接口的一部分,那麼你需要另一個接口或實際上傳遞該對象。但正如我和其他人所說,它增加了耦合。假設ISomeInterface是「IDatabase」。爲什麼你想要傳遞MySqlDatabase。您的BarClass類應該可以與任何實現IDatabase的對象一起使用,而不僅僅用於MySqlDatabase。這意味着你對接口進行編碼,而不是實現。 – Philipp 2010-11-20 15:12:47

0

通過使用接口定義而不是具體的實現,您的代碼現在更鬆散耦合。該技術用於依賴注入。

此外,當需要以不同方式實施FooClass時,此功能非常實用。如果您使用了具體的實現,您將需要在聲明FooClass的地方更改代碼。根據界面進行編程可以避免此類變化的影響。

0

對ISomeInterface而不是FooClass進行編程的主要好處之一是,您可能會更改您的FooClass實現。例如,考慮一個數據庫驅動的博客應用程序:

interface IBlogStorage{ 
    getPosts(); 
} 

你再有一類這樣的:

class XMLBlogSotrage: IBlogStorage{} 

,並假設你實現一切的接口。後來,你認爲XML速度太慢,要使用RDBMS,則:

class MsSQLBlogStorage:IBlogStorage{} 

在這種情況下,你不需要在其他程序中改變什麼,你只需要創建一個新類,插上電源!那些已經存在的代碼,不需要打擾存儲的位置。

0

另一種考慮接口和類之間相互影響的方法是顛倒它們。這意味着首先從類開始。假設您有幾個公開名爲「Sort()」的方法的類。然後你有另一個類有一個方法,需要引用這些類,然後調用他們的「Sort()」方法。您可以創建並附加一個接口到這些類(非常快速地修復,因爲這些類已經包含實現),而不是使用不同的參數。

A.Sort() 
B.Sort() 
C.Sort() 

interface ISortable {void Sort();} 
A : ISortable 
B : ISortable 
C : ISortable 

D.SortSomething(ISortable foo) 
{ 
     foo.Sort() 
} 

也許這太抽象了。我最喜歡使用的接口是讓我的類參與foreach循環。

class SomeCollection : IEnumerable 
{ 
     List<SomeItem> _items = new List<SomeItem>(); 

     // This is the only code I need to enable this class to participate in foreach loop. 
     public Enumerator GetEnumerator() 
     { 
      return _items.GetEnumerator(); 
     } 
} 

一旦您發現接口如何簡化您的代碼,您甚至可以在編寫課程之前開始創建接口。