2012-09-10 48 views
3

我想更好地理解C#和其他OOP語言中的接口。界面做什麼?爲什麼需要?我知道c#和Java不允許多重繼承。大多數書籍都說接口是解決單一繼承限制的一種方法,並允許不同的類具有通用功能。接口只是定義方法並強制類實現它們。爲什麼不讓類自己定義和實現方法而不處理接口呢?例如:接口在C#中的概念

4: using System; 
5: 
6: public interface IShape 
7: { 
8: double Area(); 
9: double Circumference(); 
10: int Sides(); 
11: } 
12: 
13: public class Circle : IShape 
14: { 
15: public int x; 
16: public int y; 
17: public double radius; 
18: private const float PI = 3.14159F; 
19: 
20: public double Area() 
21: { 
22: double theArea; 
23: theArea = PI * radius * radius; 
24: return theArea; 
25: } 
. 
. 
. 

爲什麼Circle類不能定義和實現Area(),Circumference()和Sides()方法本身?如果方形類繼承IShape,則Circumference()方法將不得不未實現。我是否理解接口?

+0

接口只聲明合同。所以客戶可以依靠合同,而不是特定的實施。 – zerkms

+0

它可以。實際上,您正在使Circle類定義和實施該區域。 –

回答

5

接口適用於當你想說「我不在乎你是如何做到的,但這是你需要完成的事情」。請參閱此link以獲取更多說明。

1

在這種情況下,你可以肯定地做到這一點。但是有一些例如mouse listeners這些是接口。有些虛擬方法在其中聲明。稍後在程序中作爲開發人員,您可以重寫這些方法並實現自己的自定義邏輯。

來源:MouseListener接口定義了五種方法:mouseClicked,mouseEntered,mouseExited,mousePressed和mouseReleased。它們都是無效方法,並將一個MouseEvent對象作爲它們的參數。請記住,您必須定義這五種方法;否則,你的類必須被聲明爲抽象的。 MouseListener接口不跟蹤事件,如鼠標的顯式移動。

+0

所以我希望這應該讓你知道接口在編程中的確切位置 – Cdeez

1

Circle類爲什麼不能定義和實現Area(), Circumference()和Sides()方法本身?

好吧,但它會獨立於接口。主要的優勢界面之一是「運行時綁定」。

對於您當前的示例,您只有一個類Circle,可以說您創建了另一個類Rectangle,並且您的兩個類都實現了IShape。稍後在運行時的代碼中,您可以創建CircleRectangle的對象,並將其放入實例類型變量中。

I形狀;

if(用戶需要一個Circle)//只是一個示例測試來顯示運行時綁定。 shape = new Circle(); else shape = new Rectangle();

現在,當你做

double area = shape.Area(); 

您將獲得形狀的區域。在運行時,您不會知道RectangleCircle是否需要區域。但是接口對象將調用它所持有引用的形狀的實現方法。

1

接口是對象實現一組特定方法或屬性的保證。是的,你的形狀類可以在沒有接口的情況下實現這些方法,但是沒有辦法告訴其他代碼實現它們,並且你可能會意外地忘記實現它或者改變它的參數。

接口是一個定義。它允許其他代碼知道定義是什麼,而不知道實現是什麼。換句話說,並不是圓圈不能實現這些方法,而是如果一個圓圈實現了接口,它就保證它實現了方法。

例如,您可能有一個名爲IWriter的接口。 IWriter有一種方法:

public interface IWriter 
{ 
    void Write(string s); 
} 

請注意這是多麼普遍。它沒有說它正在寫什麼,或者它是如何寫的......只是它寫道。

然後,您可以擁有實現IWriter的具體方法,例如MemoryWriter,ConsoleWriter,PrinterWriter,HTMLWriter等......每個都以不同的方式寫入,但它們都只用一種方法實現相同的簡單接口。

public class ConsoleWriter : IWriter 
{ 
    public void Write(string s) { 
     Console.WriteLine(s); 
    } 
} 

public class MemoryWriter : IWriter 
{ 
    public void Write(string s) { 
     // code to create a memory object and write to it 
    } 
} 

你可以做到相同的事情與基類,但這將依賴於實際的實施,並會創建一個你可能不希望依賴。接口將實現與定義分開。

關於正方形沒有圓周和沒有邊的圓......這隻意味着你的IShape定義沒有很好的設計。您試圖將對象放入可能不適用的單個定義中。

1

一個接口也被稱爲合約,它將在兩個或更多類之間的交互過程中使用。

在這種情況下,當一個類被稱爲執行IShape時,調用者將知道該類具有合同中定義的所有方法,稱爲IShape。來電者不用擔心課程是否爲Square/Rectangle/Circle

要回答你的問題

如果一個正方形類繼承的IShape,則Circumference()方法 將必須得到執行。

您需要設計您的接口,使其足夠通用。在這種情況下,界面應該有一個稱爲周長而不是周長的方法。

對於sides屬性,我相信圓應該返回一個預定義常量,例如int.MaxValue來表示無窮大。

要了解接口的優點,您需要了解調用方將如何調用這些方法。

e。克

public double DisplayArea(IShape shape) 
{ 
    Console.WriteLine(shape.Area().ToString()); 
} 

可以通過

//Code to create a Circle 
this.DisplayArea(circle); 

//Code to create a Square 
this.DisplayArea(square); 

這是可能的,因爲,DisplayArea知道IShape類型的任何對象將有一個稱爲Area方法,該方法將返回一個double調用上述方法。所以,它根本不用擔心類名。將來如果您實施Ellipse類並使其實現接口IShape,那麼DisplayArea方法將無需任何修改即可使用。

1

當然,您可以自行添加方法,而無需實現接口。但接口確保你不會離開你的願望。這就像義務減去仲裁一樣。如上所述,界面是必須履行的合同,最重要的是你如何履行。

0

實現一個接口和繼承的區別在於,繼承指定了一個「是」的行爲,如同樣在「A」也是「B」(一隻貓也是動物)。一個接口實現了更多的「 「(或者是)行爲(一隻鳥'擁有'翅膀,而'飛機也'擁有'翅膀),這是粗略的簡化,但你明白了這個主意

這就是爲什麼在這些語言中只能繼承一個對象,但實現了很多接口,Interface提供了更多的Mixin功能(它可以讓你像混合油漆一樣混合各種東西)。 ÿ ou來設計你的對象結構,以便你使用正確的結構。

例如。僅僅因爲它們都可以飛行,你不會從Bird類繼承你的飛機類。是的,飛機和鳥有很多共同之處,但是飛機不是鳥。 (說過你的特定應用程序中的語義可能會有所不同,你可能並不在意這些區別,或者你可能從未在你的應用程序中處理過飛機)。在這裏,您將有說像

public Interface ICanFly 
{ 
    void Fly(); 
    void Land(); 
} 

這樣兩架飛機和鳥類可以實現ICanFly接口,而試圖假裝飛機是鳥的接口。不管是人造的還是自然的,你可能還有其他一些班級只關心是否飛行。比方說你有跡法

public class Radar 
{ 
    // This method doesn't care whether the object is a bird or plane. 
    public void Track(ICanFly flyingObject) 
    { 

    } 
} 

雷達類你可以可以有專門適用於動物的其他類

public class MigrationTracker 
{ 
    // I only deal with birds since aeroplanes don't migrate. 
    public void Track(Bird birdsToTrack) 
    { 

    } 
} 

正如你所看到的,接口和繼承提供如何不同的選項想要設計你的課程,但是明智地使用它們取決於你。例如,您在本地動物園的應用程序可能永遠不需要處理飛機,在這種情況下,飛行功能可以添加到鳥類並繼承,而不是與界面混合。

1

在現實世界中,你將不僅與圓形工作。您將需要其他形狀,例如矩形/正方形等。接口允許您定義一些常用合約,您將在應用程序中使用這些合約,然後您可以輕鬆添加新形狀。當你添加一個新的形狀時,你不會被強制改變你的代碼 - 你只需要實現IShape接口並且你就完成了(當然,有些改變是必須的,但不是那麼多)。

2

C#中的對象可能具有實際上是不同類別組合的功能;這方面的一個典型的例子是教師例子:

Multiple Inheritance

在這個例子中,教師有一個人(比如眼睛的顏色)的特徵(雖然我有一些教師可能會打破這個例子)和僱員的特徵(例如薪水)

C#不允許多重繼承,所以我們可以看看使用接口的想法composition。它往往是這樣描述的:

繼承意味着,如果貓的動物,然後貓繼承「是一個」動物

組成意味着,如果Cat實現噪音,那麼貓「有,一個」噪聲

爲什麼這個區別很重要?好吧,想象我們的貓。我們實際上可以讓Cat繼承自Feline,而Feline繼承自Animal。有一天,我們決定支持其他類型的動物,所以我們決定修改動物,但是後來我們意識到我們將會把這些變化推向其他所有的動物類型。我們的設計和層次結構突然變得相當複雜,如果我們一開始就沒有正確的做法,我們必須廣泛地重新設計我們所有的孩子班。

規則是Prefer Composition over Inheritance,但請注意Prefer這個詞的用法 - 這很重要,因爲它並不意味着我們應該只使用組合,而是我們的設計應該考慮繼承何時有用以及何時組合也很有用。它也提醒我們,在基類中堅持每一種可能的方法是一個壞主意。

我喜歡你的形狀的例子。試想一下:

Small Shape Sorter

我們ShapeSorter可能有這樣的方法:

public bool Sort(Shape shape) 
{ 
    foreach(Hole hole in Holes) 
    { 
    if(Hole.Type == HoleType.Circular && shape is Circle) 
    { 
     return true; 
    } 
    if(Hole.Type == HoleType.Square && shape is Square) 
    { 
     return true; 
    } 
    if(Hole.Type == HoleType.Triangular && shape is Triangle) 
    { 
     return true; 
    } 
    } 

    return false; 

} 

或者,我們可以做控制的一些輕微的反轉:

public bool Sort(Shape shape, Hole hole) 
{ 
    return hole.Accepts(shape); //We end up pushing the `if` code into Hole 
} 

或其某種變體。我們已經編寫了很多依賴於我們知道確切形狀的代碼。想象一下,它會多麼乏味維護,當你有其中之一:

Large Shape Sorter

所以,相反,我們認爲自己 - 有沒有我們可以通過蒸餾下來的相關說明我們的問題一個更通用的方法屬性?

你已經把它稱爲IShape的:然後

public interface IShape{ 
    double Area {get;} 
    double Perimeter { get; } //Prefer Perimeter over circumference as more applicable to other shapes 
    int Sides { get; } 
    HoleType ShapeType { get; } 
} 

我們的排序方法將成爲:

public Hole Sort(IShape shape) 
{ 
    foreach(Hole hole in Holes) 
    { 
     if(hole.HoleType == shape.ShapeType && hole.Area >= shape.Area) 
     { 
     return hole; 
     } 
    } 
    return null; 
} 

它看起來整潔,但是是不是真的任何可能不會被通過做直接形狀。

事實是,沒有事實。最常見的方法將涉及使用繼承和組合,因爲現實世界中的許多事物都將是一種類型,並且還將具有由接口最好地描述的其他屬性。要避免的最重要的事情是堅持每一種可能的方法在基類中,並且擁有巨大的if語句來確定派生類型可以做什麼和不可以做什麼 - 這是很多難以維護的代碼。另外,在基類中加入太多的功能可能會導致您的代碼具體化 - 您將不會因爲所有潛在的副作用而後來修改代碼。

+0

+1 nice圖片;) – ghostJago