用《英雄聯(lián)盟》解釋一下面向?qū)ο笾薪涌诘淖饔?/h1>

在面向?qū)ο缶幊痰乃枷胫校涌谑且粋€(gè)非常重要的概念。按書上介紹的,使用接口,可以實(shí)現(xiàn)運(yùn)行時(shí)多態(tài)、易維護(hù)、易拓展等等優(yōu)點(diǎn)。擁有多年編程經(jīng)驗(yàn)的人應(yīng)該能理解這些話的含義,對(duì)于一個(gè)初學(xué)編程的萌新來(lái)說(shuō),看完這段話完全不知所云。那今天我用《英雄聯(lián)盟》為背景,詳細(xì)的分析一下接口在面向?qū)ο缶幊讨械淖饔茫约笆褂媒涌诘膬?yōu)勢(shì)。
這次使用java作為編寫demo的語(yǔ)言,主要原因有兩個(gè):

  1. java是最流行的編程語(yǔ)言,基本上學(xué)過(guò)編程的都會(huì)java語(yǔ)言;
  2. java是一門對(duì)面向?qū)ο筇匦灾С直容^好的語(yǔ)言;

還記得我剛開始學(xué)習(xí)java的時(shí)候,就很不理解接口的作用,感覺(jué)接口有點(diǎn)多余。

例如我定義了一個(gè)接口,但是我在實(shí)現(xiàn)這個(gè)接口的類中還要寫接口的實(shí)現(xiàn)方法,那我不如直接就在這個(gè)類中寫實(shí)現(xiàn)方法豈不是更便捷,還省去了定義接口

相信不止我一個(gè)人有過(guò)這樣的疑惑吧。
后來(lái)隨著寫代碼,看閱讀別人的代碼,逐漸開始理解接口的作用了,慢慢覺(jué)得接口是一個(gè)非常方便和牛逼的東西。
教材上,網(wǎng)上解釋接口的例子大多數(shù)使用定義一個(gè)Animal接口,然后Dog實(shí)現(xiàn)了這個(gè)接口,Cat實(shí)現(xiàn)了這個(gè)接口;還有一種用USB接口舉例。大多數(shù)人看完還是一臉懵逼。
現(xiàn)在用一種新的方式——《英雄聯(lián)盟》為背景介紹一下。
說(shuō)了這么半天,開始進(jìn)入正題吧。

先圈兩個(gè)重點(diǎn):
  1. Java之所以要有接口,是因?yàn)閖ava不支持多繼承,使用接口,可以間接的實(shí)現(xiàn)多繼承的一些特性;像C++就不存在接口這個(gè)東西,因?yàn)镃++支持多繼承
  2. 在面向?qū)ο蟮母拍钪校宇悾ㄅ缮悾┛梢宰詣?dòng)轉(zhuǎn)換為父類(基類)類型;也就是說(shuō),A類實(shí)現(xiàn)了接口B,那么A的實(shí)例化對(duì)象可以自動(dòng)轉(zhuǎn)換為B類型
public class Main {
    public static void main(String[] args) {
        B a = new A();
    }
}

interface B {

}

class A implements B {

}

這樣的代碼是正確的。


開始demo部分,我們定義一個(gè)Skill接口,里面有 QWER 四個(gè)方法,代表英雄的四個(gè)技能。為了簡(jiǎn)單,被動(dòng)技能和召喚師技能就不寫了。
然后從五個(gè)位置上單、打野、中單、ADC、輔助中各挑選一個(gè)英雄,作為例子。上路中我最喜歡的是銳雯,打野我玩的最多,糾結(jié)了半天選了盲僧。中單里必須選亞索,ADC里選擇了暴走蘿莉,輔助里選擇了錘石。

先上代碼再解釋:

//技能接口
interface Skill {
    void Q();

    void W();

    void E();

    void R();
}

//放逐之刃-銳雯
class RuiWen implements Skill {

    public RuiWen() {
        System.out.println("斷劍重鑄之日,騎士歸來(lái)之時(shí)");
    }

    @Override
    public void Q() {
        System.out.println("折翼之舞");
    }

    @Override
    public void W() {
        System.out.println("震魂怒吼");
    }

    @Override
    public void E() {
        System.out.println("勇往直前");
    }

    @Override
    public void R() {
        System.out.println("放逐之鋒");
    }
}

//盲僧-李青
class LiQing implements Skill {

    public LiQing() {
        System.out.println("我用雙手成就你的夢(mèng)想");
    }

    @Override
    public void Q() {
        System.out.println("天音波/回音擊");
    }

    @Override
    public void W() {
        System.out.println("金鐘罩/鐵布衫");
    }

    @Override
    public void E() {
        System.out.println("天雷破/摧筋斷骨");
    }

    @Override
    public void R() {
        System.out.println("猛龍擺尾");
    }
}

//疾風(fēng)劍豪-亞索
class YaSuo implements Skill {

    public YaSuo() {
        System.out.println("死亡如風(fēng),常伴吾生");
    }

    @Override
    public void Q() {
        System.out.println("斬鋼閃");
    }

    @Override
    public void W() {
        System.out.println("風(fēng)之障壁");
    }

    @Override
    public void E() {
        System.out.println("踏前斬");
    }

    @Override
    public void R() {
        System.out.println("狂風(fēng)絕息斬");
    }
}

//暴走蘿莉-金克斯
class JinKeSi implements Skill {

    public JinKeSi() {
        System.out.println("規(guī)則就是用來(lái)打破的");
    }

    @Override
    public void Q() {
        System.out.println("槍炮交響曲!");
    }

    @Override
    public void W() {
        System.out.println("震蕩電磁波!");
    }

    @Override
    public void E() {
        System.out.println("嚼火者手雷!");
    }

    @Override
    public void R() {
        System.out.println("超究極死神飛彈!");
    }
}

//魂鎖典獄長(zhǎng)-錘石
class ChuiShi implements Skill {

    public ChiShi() {
        System.out.println("我們要怎樣進(jìn)行這令人愉悅的折磨呢");
    }

    @Override
    public void Q() {
        System.out.println("死亡判決");
    }

    @Override
    public void W() {
        System.out.println("魂引之燈");
    }

    @Override
    public void E() {
        System.out.println("厄運(yùn)鐘擺");
    }

    @Override
    public void R() {
        System.out.println("幽冥監(jiān)牢");
    }
}

代碼有點(diǎn)多,但是很簡(jiǎn)單,寫了5類,對(duì)應(yīng)5個(gè)英雄。每個(gè)類的構(gòu)造方法中,打印了這個(gè)英雄在排位中被選中時(shí)的臺(tái)詞。每個(gè)類都實(shí)現(xiàn)了skill這個(gè)接口,并重寫了QWER這4個(gè)方法,在方法中打印了這個(gè)英雄技能的名稱。
在main方法中初始化這5個(gè)英雄,并調(diào)用每個(gè)英雄的QWER這四個(gè)技能,代碼:

public class Main {
    public static void main(String[] args) {
        //初始化銳雯釋,放技能
        Skill ruiWen = new RuiWen();
        ruiWen.Q();
        ruiWen.W();
        ruiWen.E();
        ruiWen.R();

        //初始化李青,釋放技能
        Skill liQing = new LiQing();
        liQing.Q();
        liQing.W();
        liQing.E();
        liQing.R();

        //初始化亞索,釋放技能
        Skill yaSuo = new YaSuo();
        yaSuo.Q();
        yaSuo.W();
        yaSuo.E();
        yaSuo.R();

        //初始化金克斯,釋放技能
        Skill jinKeSi = new JinKeSi();
        jinKeSi.Q();
        jinKeSi.W();
        jinKeSi.E();
        jinKeSi.R();

        //初始化錘石,釋放技能
        Skill chuiShi = new ChuiShi();
        chuiShi.Q();
        chuiShi.W();
        chuiShi.E();
        chuiShi.R();
    }
}
注意一點(diǎn):

我們?cè)趯?shí)例化這5個(gè)英雄時(shí),這5個(gè)英雄都是Skill類型的
看一下運(yùn)行結(jié)果:


可以看到,這5個(gè)英雄依次被實(shí)例化,并釋放了QWER這4個(gè)技能。


可能到這有的同學(xué)沒(méi)看懂,這和接口有什么關(guān)系?接口帶來(lái)了哪些好處?

簡(jiǎn)單分析一下:
  1. 接口這個(gè)概念,其實(shí)就是定義了一種規(guī)范。在 Skill 這個(gè)接口中,定義了Q、W、E、R這四個(gè)方法,只要是實(shí)現(xiàn)了這個(gè)接口的類,一定會(huì)有這四個(gè)方法。
  2. 接口可以看做是實(shí)現(xiàn)多繼承的一種方式(這樣說(shuō)可能不嚴(yán)謹(jǐn))。java中沒(méi)有多繼承這種機(jī)制,失去了一些靈活性。但是去掉多繼承后,語(yǔ)法簡(jiǎn)單了很多,像C++中,因?yàn)橛卸嗬^承,又引入了虛繼承的概念。說(shuō)多了,回到正題。一個(gè)類實(shí)現(xiàn)一個(gè)接口后,可以看做是這個(gè)接口的子類,所以,我們?cè)趯?shí)例化英雄時(shí)(new Ruiwen()等),可以直接實(shí)例化為 Skill 類型的。

結(jié)合這兩點(diǎn),所以我們每一個(gè)Skill類型的對(duì)象,都可以調(diào)用 Q、W、E、R 這四個(gè)方法。
有人會(huì)提出疑問(wèn),我在每個(gè)類中都定義 Q、W、E、R 這四個(gè)方法不就行了。但是如何保證每個(gè)類里都有這四個(gè)方法呢?通過(guò)接口約束,可以保證,所有實(shí)現(xiàn)這個(gè)接口的類中,一定有這四個(gè)方法。

再通過(guò)下面這個(gè)用法,看一下接口怎樣實(shí)現(xiàn)多態(tài)的:

import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Skill hero;
        Scanner scanner = new Scanner(System.in);
        switch (scanner.nextInt()) {
            case 1:
                hero = new RuiWen();
                break;
            case 2:
                hero = new LiQing();
                break;
            case 3:
                hero = new YaSuo();
                break;
            case 4:
                hero = new JinKeSi();
                break;
            case 5:
                hero = new ChuiShi();
                break;
            default:
                hero = new RuiWen();
        }

        hero.Q();
        hero.W();
        hero.E();
        hero.R();
    }
}

簡(jiǎn)單看一下代碼,定義了一個(gè)Skill類型的變量hreo。通過(guò)輸入不同的值,來(lái)判斷實(shí)例化哪一個(gè)英雄。最后調(diào)用英雄的 Q、W、E、R 方法。
先輸入 1 看一下,輸入 1 應(yīng)該是實(shí)例化銳雯這個(gè)英雄


沒(méi)有問(wèn)題,輸入1成功實(shí)例化了銳雯這個(gè)英雄,并調(diào)用了銳雯的四個(gè)技能。
再換一個(gè)輸入值看一下:

這次輸入了 2 ,實(shí)例化了李青這個(gè)英雄,并調(diào)用了李青的四個(gè)技能。

簡(jiǎn)單說(shuō)一下使用了接口后的優(yōu)勢(shì):
  1. 使用接口后,實(shí)現(xiàn)了運(yùn)行時(shí)多態(tài),也就是 hero 具體是哪個(gè)類的對(duì)象,在編譯階段我們是不知道的,只有當(dāng)程序運(yùn)行時(shí),通過(guò)我們輸入的值才能確定 hero 是哪個(gè)類的對(duì)象。
  2. 使用了接口后,所有實(shí)現(xiàn)了 Skill 接口的類,都可以實(shí)例化為 Skill 類型的對(duì)象。如果不是這樣,那有多少個(gè)英雄(類)就要定義多少個(gè)變量。現(xiàn)在英雄聯(lián)盟有145個(gè)英雄,那就要定義145個(gè)變量,這。。。。

總結(jié):

  1. 接口的作用是定義了定義了一些規(guī)范(也就是定義了一些方法),所有實(shí)現(xiàn)了這個(gè)接口的類,必須要遵守這些規(guī)范(類中一定有這些方法)
  2. 一個(gè)類實(shí)現(xiàn)了一個(gè)接口, 可以看做 是這個(gè)接口的子類,注意是可以看做。子類類型可以自動(dòng)轉(zhuǎn)換為父類類型,所以任何出現(xiàn)接口的地方,都可以使用實(shí)現(xiàn)這個(gè)接口的類的對(duì)象代替。最常見(jiàn)的就是方法中傳參,定義一個(gè)接口類型的變量,傳入一個(gè)實(shí)現(xiàn)了接口的對(duì)象。

最后:文章是下班后半夜寫的,加上自身能力有限,文中如有不正確的地方,歡迎評(píng)論區(qū)探討,共同提高。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容