為什么我們要面向接口編程码俩?度帮!

Profile

到底面向?編程

面向過程編程(Procedure Oriented稿存、簡稱PO面向?qū)ο缶幊蹋?code>Object Oriented笨篷、簡稱OO 我們一定聽過,然而實際企業(yè)級開發(fā)里受用更多的一種編程思想那就是:面向接口編程(Interface-Oriented瓣履!

接口這個概念我們一定不陌生率翅,實際生活中最常見的例子就是:插座!

我們只需要事先定義好插座的接口標(biāo)準(zhǔn)袖迎,各大插座廠商只要按這個接口標(biāo)準(zhǔn)生產(chǎn)冕臭,管你什么牌子、內(nèi)部什么電路結(jié)構(gòu)燕锥,這些均和用戶無關(guān)浴韭,用戶拿來就可以用;而且即使插座壞了脯宿,只要換一個符合接口標(biāo)準(zhǔn)的新插座,一切照樣工作泉粉!

image

同理连霉,實際代碼設(shè)計也是這樣榴芳!

我們在設(shè)計一個軟件的代碼架構(gòu)時,我們都希望事先約定好各個功能的接口(即:約定好接口簽名和方法)跺撼,實際開發(fā)時我們只需要實現(xiàn)這個接口就能完成具體的功能窟感!后續(xù)即使項目變化、功能升級歉井,程序員只需要按照接口約定重新實現(xiàn)一下柿祈,就可以達(dá)到系統(tǒng)升級和擴(kuò)展的目的!

正好哩至,Java中天生就有interface這個語法躏嚎,這簡直是為面向接口編程而生的!

所以接下來落實到代碼上菩貌,舉個通俗一點(diǎn)的小例子嘮一嘮卢佣,實際業(yè)務(wù)代碼雖然比這個復(fù)雜,但原理是一模一樣的箭阶。


做夢了

假如哪一天程序羊真發(fā)達(dá)了虚茶,一口豪氣買了兩輛豪車,一輛五菱宏光仇参、一輛飛度嘹叫、并且還專門聘請了一位駕駛員來幫助駕駛。

兩輛豪車在此:

public class Wuling {
    public void drive() {
        System.out.println("駕駛五菱宏光汽車");
    }
}

public class Fit {
    public void drive() {
        System.out.println("駕駛飛度汽車");
    }
}

駕駛員定義在此:

駕駛員定義了兩個drive()方法诈乒,分別用來駕駛兩輛車:

public class Driver {

    public void drive( Wuling wuling ) {
        wuling.drive(); // 駕駛五菱宏光的方法
    }
    
    public void drive( Fit fit ) {
        fit.drive();    // 駕駛飛度的方法
    }

    // 用于測試功能的 main()函數(shù)
    public static void main( String[] args ) {
    
        // 實例化兩輛新車
        Wuling wuling = new Wuling();
        Fit fit = new Fit();
        
        // 實例化駕駛員
        Driver driver = new Driver();
        
        driver.drive( wuling ); // 幫我開五菱宏光
        driver.drive( fit );    // 幫我開飛度
    }
}

這暫且看起來沒問題罩扇!日子過得很融洽。

但后來過了段時間抓谴,程序羊又變得發(fā)達(dá)了一點(diǎn)暮蹂,這次他又豪氣地買了一輛新款奧拓(Alto)!

可是現(xiàn)有的駕駛員類Driver的兩個drive()方法里都開不了這輛新買的奧拓該怎么辦呢癌压?


代碼的靈活解耦

這時候仰泻,我想應(yīng)該沒有誰會專門再去往Driver類中添加一個新的drive()方法來達(dá)到目的吧?畢竟誰也不知道以后他還會不會買新車滩届!

這時候如果我希望我聘請的這位駕駛員對于所有車型都能駕馭集侯,該怎么辦呢?

很容易想到帜消,我們應(yīng)該做一層抽象棠枉。畢竟不管是奧拓還是奧迪,它們都是汽車泡挺,因此我們定義一個父類叫做汽車類Car辈讶,里面只聲明一個通用的drive()方法,具體怎么開先不用管:

// 抽象的汽車類Car娄猫,代表所有汽車
public class Car {
    void drive() { } // 通用的汽車駕駛方法
}

這時贱除,只要我新買的奧拓符合Car定義的駕駛標(biāo)準(zhǔn)即可被我的駕駛員駕駛生闲,所以只需要新的奧拓來繼承一下Car類即可:

public class Alto extends Car {
    public void drive() {
        System.out.println("駕駛奧拓汽車");
    }
}

同理,只需要我的駕駛員具備通用汽車Car的駕駛能力月幌,那駕駛所有的汽車都不是問題碍讯,因此Drvier類的drive()方法只要傳入的參數(shù)是父類,那就具備了通用性:

public class Driver {

    public void drive( Car car ) {  // 方法參數(shù)使用父類來替代
        car.drive();
    }

    public static void main( String[] args ) {
        Alto alto = new Alto();
        Driver driver = new Driver();
        driver.drive( alto );
    }
}

問題暫且解決了扯躺!


但是再后來捉兴,程序羊他好像又更發(fā)達(dá)了一些,連車都不想坐了录语,想買一頭驢(Donkey)讓司機(jī)騎著帶他出行倍啥!

很明顯,原先適用于汽車的drive()方法肯定是不適合騎驢的钦无!但我們希望聘請的這位駕駛員既會開汽車逗栽,又會騎驢怎么辦呢?

害失暂!我們干脆直接定義一個叫做交通工具(TrafficTools)的通用接口吧彼宠!里面包含一個通用的交通工具使用方法,管你是駕駛汽車弟塞,還是騎驢騎馬凭峡,具體技能怎么實現(xiàn)先不管:

// 通用的交通工具接口定義
public interface TrafficTools {
    void drive();  // 通用的交通工具使用方法
}

有了這個接口約定,接下來就好辦了决记。我們讓所有的Car摧冀、或者驢、馬等系宫,都來實現(xiàn)這個接口:

public class Car implements TrafficTools {
    @Override
    public void drive() { }
}

public class Wuling extends Car {
    public void drive() {
        System.out.println("駕駛五菱宏光汽車");
    }
}

public class Fit extends Car {
    public void drive() {
        System.out.println("駕駛飛度汽車");
    }
}

public class Alto extends Car {
    public void drive() {
        System.out.println("駕駛奧拓汽車");
    }
}

public class Donkey implements TrafficTools {
    @Override
    public void drive() {
        System.out.println("騎一頭驢");
    }
}

這個時候只要我們的駕駛員師傅也面向接口編程索昂,就沒有任何問題:

public class Driver {

    // 方法參數(shù)面向接口編程
    public void drive( TrafficTools trafficTools ) {
        trafficTools.drive();
    }

    public static void main( String[] args ) {
        Driver driver = new Driver();
        driver.drive( new Wuling() );  // 開五菱
        driver.drive( new Fit() );     // 開飛度
        driver.drive( new Alto() );    // 開奧拓
        driver.drive( new Donkey() );  // 騎一頭驢
    }
}

很明顯,代碼完全解耦了扩借!這就是接口帶來的便利椒惨。


代碼的擴(kuò)展性

面向接口編程的優(yōu)點(diǎn)遠(yuǎn)不止上面這種代碼解耦的場景,在實際企業(yè)開發(fā)里潮罪,利用接口思想對已有代碼進(jìn)行靈活擴(kuò)展也特別常見康谆。

再舉一個例子:假設(shè)程序羊有一個非常豪氣的朋友,叫:程序牛嫉到,他們家出行可不坐車沃暗,全靠私人飛機(jī)出行:

// 通用的飛機(jī)飛行接口
public interface Plane {
    void fly();
}

// 程序牛的專用機(jī)長,受過專業(yè)訓(xùn)練(即:實現(xiàn)了通用飛行接口)
public class PlaneDriver implements Plane {
    @Override
    public void fly() {
        System.out.println("專業(yè)的飛行員操控飛機(jī)");
    }
}

// 出門旅行
public class Travel {

    // 此處函數(shù)參數(shù)也是面向接口編程:味瘛D踝丁!
    public void fly( Plane plane ) {
        plane.fly();
    }

    public static void main( String[] args ) {
        Travel travel = new Travel();  // 開啟一段旅行
        PlaneDriver planeDriver = new PlaneDriver(); // 聘請一個機(jī)長
        travel.fly( planeDriver ); // 由專業(yè)機(jī)長開飛機(jī)愉快的出去旅行
    }
}

但是突然有一天细层,他們家聘請的機(jī)長跳槽了忱叭,這時候程序牛一家就無法出行了隔崎,畢竟飛機(jī)不會駕駛。

于是他跑來問我借司機(jī)韵丑,想讓我的駕駛員來幫他駕駛飛機(jī)出去旅行。

我一看虚缎,由于他們的代碼面向的是接口撵彻,我就肯定地答應(yīng)了他!

這時候?qū)ξ疫@邊的擴(kuò)展來說就非常容易了实牡,我只需要安排我的駕駛員去培訓(xùn)一下飛行技能就OK了(實現(xiàn)一個方法就行):

// 讓我的駕駛員去培訓(xùn)一下飛行技能(即:去實現(xiàn)通用飛行接口)
public class Driver implements Plane {

    public void drive( TrafficTools trafficTools ) {
        trafficTools.drive();
    }
    
    // 實現(xiàn)了fly()方法陌僵,這下我的駕駛員也具備操控飛機(jī)的能力了!
    @Override
    public void fly() {
        System.out.println("普通駕駛員操控飛機(jī)");
    }
}

這時候我的駕駛員Driver類就可以直接服務(wù)于他們一家的出行了:

public class Travel {

    public void fly( Plane plane ) {
        plane.fly();
    }

    public static void main( String[] args ) {
        Travel travel = new Travel();

        // 專業(yè)飛行員操控飛機(jī)
        PlaneDriver planeDriver = new PlaneDriver();
        travel.fly( planeDriver );

        // 普通駕駛員操控飛機(jī)
        Driver driver = new Driver();
        travel.fly( driver );
    }
}

看到?jīng)]创坞,這一改造過程中碗短,我們只增加了代碼,卻并沒有修改任何已有代碼题涨,就完成了代碼擴(kuò)展的任務(wù)偎谁,非常符合開閉原則


實際項目

實際開發(fā)中纲堵,我們就暫且不說諸如Spring這種框架內(nèi)部會大量使用接口巡雨,并對外提供使用,就連我們自己平時寫業(yè)務(wù)代碼席函,我們也習(xí)慣于在Service層使用接口來進(jìn)行一層隔離:

image

這種接口定義和具體實現(xiàn)邏輯的分開铐望,非常有利于后續(xù)擴(kuò)展和維護(hù)!


小結(jié)

面向接口編程開發(fā)茂附,對代碼架構(gòu)的解耦和擴(kuò)展確實很有好處正蛙,這種編碼思想也值得平時開發(fā)結(jié)合實踐反復(fù)理解和回味!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末营曼,一起剝皮案震驚了整個濱河市乒验,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌溶推,老刑警劉巖徊件,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蒜危,居然都是意外死亡虱痕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進(jìn)店門辐赞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來部翘,“玉大人,你說我怎么就攤上這事响委⌒滤迹” “怎么了窖梁?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長夹囚。 經(jīng)常有香客問我纵刘,道長,這世上最難降的妖魔是什么荸哟? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任假哎,我火速辦了婚禮,結(jié)果婚禮上鞍历,老公的妹妹穿的比我還像新娘舵抹。我一直安慰自己,他們只是感情好劣砍,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布惧蛹。 她就那樣靜靜地躺著,像睡著了一般刑枝。 火紅的嫁衣襯著肌膚如雪香嗓。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天仅讽,我揣著相機(jī)與錄音陶缺,去河邊找鬼。 笑死洁灵,一個胖子當(dāng)著我的面吹牛饱岸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播徽千,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼苫费,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了双抽?” 一聲冷哼從身側(cè)響起百框,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎牍汹,沒想到半個月后铐维,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡慎菲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年嫁蛇,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片露该。...
    茶點(diǎn)故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡睬棚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情抑党,我是刑警寧澤包警,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站底靠,受9級特大地震影響害晦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜苛骨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一篱瞎、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧痒芝,春花似錦、人聲如沸牵素。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽笆呆。三九已至请琳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赠幕,已是汗流浹背俄精。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留榕堰,地道東北人竖慧。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像逆屡,于是被迫代替她去往敵國和親圾旨。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評論 2 355

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