到底面向?編程
面向過程編程(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)的新插座,一切照樣工作泉粉!
同理连霉,實際代碼設(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)行一層隔離:
這種接口定義和具體實現(xiàn)邏輯的分開铐望,非常有利于后續(xù)擴(kuò)展和維護(hù)!
小結(jié)
面向接口編程開發(fā)茂附,對代碼架構(gòu)的解耦和擴(kuò)展確實很有好處正蛙,這種編碼思想也值得平時開發(fā)結(jié)合實踐反復(fù)理解和回味!