C# 接口《通俗解釋》

接口

爲何要用接口?好處在哪裏?spa

若是你的工做是一個修水管的,一天客戶找上你讓你幫裝水管,可是有個要求,就是客戶喜歡管子是三角形的。設計

你立馬買了三角形的水管回來,在牆上弄個三角形的口子,客戶付了錢,你很開心今天有了收入,以下圖,很好:code

可是好景不長,客戶過了一個星期又來找,由於他以爲三角形很差看,要讓你換成正方形的水管,你不得不換,由於顧客就是上帝。好吧,繼續在牆上弄個正方形的口子,而後又換成正方形的管子來接上。好了,以下圖:(可是可能以爲爲何一開始不說要正方形的?由於需求總在變化。。。)blog

你累得滿頭大汗,可是仍是完成了。惋惜不久,客戶又來找你,由於他想換成橢圓形的管子了。雖然你很無奈,可是你仍是不得不花了幾個小時完成。以下圖:繼承

安裝完成,這時你可能在考慮,爲何換不一樣形狀的水管,我都要大動干戈一番呢?因而你想到一個好方法,那就是牆上設計一個固定的水管而且是圓形的,當客戶喜歡什麼形狀的水管,那麼我只須要把客戶喜歡的水管的一頭作成圓形的,這樣,之後都不須要去動牆上的水管了。這是一個好辦法。就先在牆上弄個圓形的口,這個口就叫作接口。以下圖:接口

如你所見,牆上有個圓形的口,可是按照本來的:圖片

三角形水管兩端是三角形
正方形水管兩端是正方形
橢圓形水管兩端是橢圓形string

那是確定接不上的,由於三角形、正方形、橢圓形的口怎麼和牆壁上圓形的口對接呢?it

因此先要實現接口,把:class

三角形水管一端作成圓形
正方形水管一端作成圓形
橢圓形水管一端作成圓形

如圖因此,圓形接口作出來了,具體實現是客戶去安裝,接口自己並不會安裝其餘形狀的水管,換句話說就是接口沒有具體實現,只是告訴你,你的水管要接入,必須有一端是圓形的(接口的約束),由於我只留這個圓形的接口,你要裝別的形狀的管子,先把一個弄成圓形的就行了(子類去實現接口的方法),無論什麼形狀,都要一個必須作成圓形才能對接得上,它必需要你按照個人規範來作。這就是爲何新手會以爲接口什麼都不作,只定義接口,沒有任何實現,那不是畫蛇添足嗎?由於它的實現是子類去完成。這樣只要客戶喜歡什麼形狀的水管,只要實現了個人接口(圓形),都能對接得上,並且改變起來也很方便,只要把水管扭上去就好了,不用在去給牆壁挖洞了

下面咱們在代碼裏體現接口的做用,如下例子的場景不討論是否合理,因爲如下代碼是在txt編寫,不保證運行無誤。

需求:公司有兩我的分別寫了2個動物類,讓你寫一個類來輸出它們各自喜歡的食物。

//A寫的Dog類,裏面有個likeFood方法,以下:
class Dog 
{

    public void likeFood()
    {
        Console.WriteLine("我是小狗,我喜歡吃肉");
    }
    
}
//B寫的Cat類,裏面有個likeFood方法,以下:
class Cat 
{

    public void likeFood()
    {
        Console.WriteLine("我是小貓,我喜歡吃魚");
    }
    
}

你寫的Zoo類以下:

//動物園類
class Zoo {

    public void show(Dog dog){
        dog.likeFood();
    }

    public void show(Cat cat){
        cat.likeFood();
    }

}

在輸出的時候使用以下:

public static void Main(string[] args) 
{
    Zoo zoo = new Zoo();
    zoo.show(new Dog()); //"我是小狗,我喜歡吃肉"
    zoo.show(new Cat()); //"我是小貓,我喜歡吃魚"

}

這一切工做良好,但好景不長,公司又須要給動物園增長一個猴子,讓C去寫這個Monkey類,並能輸出它喜歡的食物。

class Dog 
{

    public void likeFood()
    {
        Console.WriteLine("我是小狗,我喜歡吃肉");
    }
    
}
//B寫的Cat類,裏面有個likeFood方法,以下:
class Cat 
{

    public void likeFood()
    {
        Console.WriteLine("我是小貓,我喜歡吃魚");
    }
    
}
//C寫的Monkey類,裏面有個likeFood方法,以下:
class Monkey 
{

    public void likeFood()
    {
        Console.WriteLine("我是猴子,我喜歡吃桃子");
    }
    
}

因而你的Zoo類就變成了這樣,僅僅多了一個重載方法:

class Zoo 
{

    public void show(Dog dog)
    {
        dog.likeFood();
    }

    public void show(Cat cat)
    {
        cat.likeFood();
    }

    public void show(Monkey money)
    {
        money.likeFood();
    }
}
public static void Main(string[] args) 
{

    Zoo zoo = new Zoo();
    zoo.show(new Dog()); //"我是小狗,我喜歡吃肉"
    zoo.show(new Cat()); //"我是小貓,我喜歡吃魚"
    zoo.show(new Monkey()); //"我是猴子,我喜歡吃桃子"

}

輸出結果也良好,你不由暗暗得意,一個變化而已,我只須要再Zoo類裏增長一個重載方法就行了。
但你仔細一想:「若是後面還有更多動物要輸出它們喜歡的食物,個人Zoo類都要修改,這對我來講不是一件好事。」
而後你再仔細觀察Zoo類,發現不變的是show方法,變化的是show方法是參數。由於每一個動物都不同,因此參數也就不同。因此原來就須要重載多個方法。
若是有一個類,能接收全部動物,那不就解決了?沒錯,因而你想到了定義一個父類叫Animal,裏面有個likeFood方法,讓全部動物類去繼承Animal。
最後你的Zoo類和Animal類代碼以下:

class Zoo 
{

    public void show(Animal animal)
    {
        animal.likeFood();
    }

}

class Animal 
{

    public void likeFood()
    {
        Console.WriteLine("我是Animal類");
    }

}

你告訴原來的A和B兩人,讓它們寫的動物類都去繼承Animal,而且裏面有個輸出動物喜歡食物的方法。
A、B、C寫的類修改後以下:

class Dog : Animal 
{

    public void likeFood()
    {
        Console.WriteLine("我是小狗,我喜歡吃肉");
    }
    
}

class Cat : Animal 
{

    public void likeFood()
    {
        Console.WriteLine("我是小貓,我喜歡吃魚");
    }
    
}

class Monkey : Animal 
{

    public void likeFood()
    {
        Console.WriteLine("我是猴子,我喜歡吃桃子");
    }
    
}

Zoo也須要修改,最後代碼以下:

class Zoo 
{

    public void show(Animal animal)
    {
        animal.likeFood();
    }

}

public static void Main(string[] args) 
{

    Zoo zoo = new Zoo();
    zoo.show(new Dog()); //"我是小狗,我喜歡吃肉"
    zoo.show(new Cat()); //"我是小狗,我喜歡吃肉"
    zoo.show(new Monkey()); //"我是猴子,我喜歡吃桃子"

}

 

運行也一切良好,無論你之後還有什麼類,只要讓須要添加的動物類,繼承Animal,並有個likeFood方法,那麼你無需修改Zoo,只須要再main方法裏傳入動物類的實例就能正常工做。
你大讚你聰明絕頂,這樣一來,你的Zoo根本不須要改變了。
有一天,公司新來一我的,暫時叫D吧,公司讓D寫個兔子類,你告訴D,你寫的Rabbit類必須繼承Animal,而且有一個它喜歡的食物的方法,。
D寫的兔子類以下:

class Rabbit : Animal 
{

    public void favoriteFood()
    {
        Console.WriteLine("我是兔子,我喜歡吃蘿蔔");
    }
    
}
public static void Main(string[] args) 
{

    Zoo zoo = new Zoo();
    zoo.show(new Dog()); //"我是小狗,我喜歡吃肉"
    zoo.show(new Cat()); //"我是小貓,我喜歡吃魚"
    zoo.show(new Monkey()); //"我是猴子,我喜歡吃桃子"
    zoo.show(new Rabbit()); //"我是Animal類"

}

Raabit並無輸出預想的結果,你不得不花了點時間去排查緣由,最後你發現這不是什麼大問題,由於新來的D雖然寫了Rabbit類,可是他並不知道大家以前約定的動物喜歡的食物命名爲:likeFood()
D寫的Rabbit類,裏面的方法叫:favoriteFood()
雖然最後D修改他的Rabbit類的方法爲likeFood(),但你仍是對這個問題作了一番思考,爲何會致使這個問題?
那是由於沒有一種約束,使得子類繼承父類的時候必須實現父類的方法。有沒有一個類,能讓它的子類必須實現它定義的方法?有,那就是接口。
因而你修改Animal爲接口,代碼以下:

interface Animal {

    public void likeFood();
    
}

因爲Animal接口有個likeFood()方法,那麼Rabbit子類去實現Animal接口必須實現likeFood(),不然程序不能經過。

代碼正常工做,由於Animal是接口,裏面有個likeFood()方法,之後再添加各類動物進來,只須要實現Animal接口,而且也不會出現有的人會由於子類的方法命名問題而致使出錯了。
這時你再想,雖然用繼承一個普通父類也能夠知足要求,可是一個普通父類根本沒有約束力
而用了接口就不同了,子類必須實現父類的全部方法,由於Zoo類裏調用的是likeFood(),因爲子類必須實現父類,那麼全部子類都會有likeFood(),你根本不用擔憂子類有沒有這個方法。
因此接口能在多人協做下,定義一系列方法,讓子類必須存在接口定義的類,防止在另外的類裏調用一我的寫的接口的子類時,找不到方法的問題。

 

開放閉關原則:

對擴展開放,意味着有新的需求或變化時,能夠對現有代碼進行擴展,以適應新的狀況。
對修改封閉,意味着類一旦設計完成,就能夠獨立完成其工做,而不要對類進行任何修改。
 
這兩句話是概念性的東西。通俗的意思就是:
對於上面的圖片來講,牆上的接口作好了,不能去改變了,可是能夠擴展,你要什麼形狀的水管就去實現它的接口。
相關文章
相關標籤/搜索