理論六:為什么基于接口而非實現(xiàn)編程叛复?有必要為每個類都定義接口嗎仔引?

”基于接口而非實現(xiàn)編程“這個原則非常重要扔仓,是一種非常有效的提高代碼質(zhì)量的手段咖耘,在平時的開發(fā)中特別經(jīng)常被用到鲤看。

如何解讀原則中的“接口”二字义桂?

理解這條接口的原則慷吊,關(guān)鍵在與理解“接口”兩個字。從本質(zhì)上看堰酿,“接口”就是一組協(xié)議和“約定”疾宏,是功能提供者提供給使用者的一個功能列表〈ゴ矗“接口”在不同的應用場景下會有不同的解讀坎藐,比如服務端與客戶端之間的“接口”,類庫提供的“接口”哼绑,甚至是一組通信的協(xié)議都可以叫作“接口”岩馍。剛剛對“接口”的理解,都比較偏上層抖韩、偏抽象蛀恩,與實際的寫代碼離得有點遠。如果落實到具體的編碼茂浮,“基于接口而非實現(xiàn)編程”這條原則中的“接口”双谆,可以理解為編程語言中的接口或者抽象類。

這條原則能非常有效地提高代碼質(zhì)量席揽,之所以這么說佃乘,那是因為,應用這條原則驹尼,可以將接口和實現(xiàn)相分離趣避,封裝不穩(wěn)定的實現(xiàn),暴露穩(wěn)定的接口新翎。上游系統(tǒng)面向接口而非實現(xiàn)編程程帕,不依賴不穩(wěn)定的實現(xiàn)細節(jié)住练,這樣當實現(xiàn)發(fā)生變化的時候,上游系統(tǒng)的代碼基本上不需要做改動愁拭,以此來降低耦合性讲逛,提高擴展性。

越抽象岭埠、越頂層盏混、越脫離具體某一實現(xiàn)的設計,越能提高代碼的靈活性惜论,越能應對未來的需求變化许赃。好的代碼設計,不僅能應對當下的需求馆类,而且在將來需求發(fā)生變化的時候混聊,仍然能夠在不破壞原有代碼設計的情況下靈活應對。而抽象就是提高代碼擴展性乾巧、靈活性句喜、可維護性最有效的手段之一。

如何將這條原則應用到實戰(zhàn)中沟于?
對于這條原則咳胃,我們結(jié)合一個具體的實戰(zhàn)案例來進一步講解一下。

假設我們的系統(tǒng)中有很多涉及圖片處理和存儲的業(yè)務邏輯旷太。圖片經(jīng)過處理之后被上傳到阿里云上展懈。為了代碼復用,我們封裝了圖片存儲相關(guān)的代碼邏輯泳秀,提供了一個統(tǒng)一的 AliyunImageStore 類标沪,供整個系統(tǒng)來使用榄攀。具體的代碼實現(xiàn)如下所示:


public class AliyunImageStore {
  //...省略屬性嗜傅、構(gòu)造函數(shù)等...
  
  public void createBucketIfNotExisting(String bucketName) {
    // ...創(chuàng)建bucket代碼邏輯...
    // ...失敗會拋出異常..
  }
  
  public String generateAccessToken() {
    // ...根據(jù)accesskey/secrectkey等生成access token
  }
  
  public String uploadToAliyun(Image image, String bucketName, String accessToken) {
    //...上傳圖片到阿里云...
    //...返回圖片存儲在阿里云上的地址(url)...
  }
  
  public Image downloadFromAliyun(String url, String accessToken) {
    //...從阿里云下載圖片...
  }
}

// AliyunImageStore類的使用舉例
public class ImageProcessingJob {
  private static final String BUCKET_NAME = "ai_images_bucket";
  //...省略其他無關(guān)代碼...
  
  public void process() {
    Image image = ...; //處理圖片,并封裝為Image對象
    AliyunImageStore imageStore = new AliyunImageStore(/*省略參數(shù)*/);
    imageStore.createBucketIfNotExisting(BUCKET_NAME);
    String accessToken = imageStore.generateAccessToken();
    imagestore.uploadToAliyun(image, BUCKET_NAME, accessToken);
  }
  
}

整個上傳流程包含三個步驟:創(chuàng)建 bucket(你可以簡單理解為存儲目錄)檩赢、生成 access token 訪問憑證吕嘀、攜帶 access token 上傳圖片到指定的 bucket 中。代碼實現(xiàn)非常簡單贞瞒,類中的幾個方法定義得都很干凈偶房,用起來也很清晰,乍看起來沒有太大問題军浆,完全能滿足我們將圖片存儲在阿里云的業(yè)務需求棕洋。

不過,軟件開發(fā)中唯一不變的就是變化乒融。過了一段時間后掰盘,我們自建了私有云摄悯,不再將圖片存儲到阿里云了,而是將圖片存儲到自建私有云上愧捕。為了滿足這樣一個需求的變化奢驯,我們該如何修改代碼呢?

新的 PrivateImageStore 類需要設計實現(xiàn)哪些方法次绘,才能在盡量最小化代碼修改的情況下瘪阁,替換掉 AliyunImageStore 類呢?這就要求我們必須將 AliyunImageStore 類中所定義的所有 public 方法邮偎,在 PrivateImageStore 類中都逐一定義并重新實現(xiàn)一遍管跺。而這樣做就會存在一些問題,總結(jié)了下面兩點:
1钢猛、首先:AliyunImageStore 類中有些函數(shù)命名暴露了實現(xiàn)細節(jié)伙菜。
2、其次:將圖片存儲到阿里云的流程命迈,跟存儲到私有云的流程贩绕,可能并不是完全一致的。

那這兩個問題該如何解決呢壶愤?解決這個問題的根本方法就是淑倾,在編寫代碼的時候,要遵從“基于接口而非實現(xiàn)編程”的原則征椒,具體來講娇哆,我們需要做到下面這 3 點:
1.函數(shù)的命名不能暴露任何實現(xiàn)細節(jié)。比如勃救,前面提到的 uploadToAliyun() 就不符合要求碍讨,應該改為去掉 aliyun 這樣的字眼,改為更加抽象的命名方式蒙秒,比如:upload()勃黍。
2.封裝具體的實現(xiàn)細節(jié)。比如晕讲,跟阿里云相關(guān)的特殊上傳(或下載)流程不應該暴露給調(diào)用者覆获。我們對上傳(或下載)流程進行封裝,對外提供一個包裹所有上傳(或下載)細節(jié)的方法瓢省,給調(diào)用者使用弄息。為實現(xiàn)類定義抽象的接口。
3.具體的實現(xiàn)類都依賴統(tǒng)一的接口定義勤婚,遵從一致的上傳功能協(xié)議摹量。使用者依賴接口,而不是具體的實現(xiàn)類來編程。

總結(jié)一下:
我們在做軟件開發(fā)的時候缨称,一定要有抽象意識废亭、封裝意識、接口意識具钥。在定義接口的時候豆村,不要暴露任何實現(xiàn)細節(jié)。接口的定義只表明做什么骂删,而不是怎么做掌动。而且,在設計接口的時候宁玫,我們要多思考一下粗恢,這樣的接口設計是否足夠通用,是否能夠做到在替換具體的接口實現(xiàn)的時候欧瘪,不需要任何接口定義的改動眷射。

是否需要為每個類定義接口?
前面我們也提到佛掖,這條原則的設計初衷是妖碉,將接口和實現(xiàn)相分離,封裝不穩(wěn)定的實現(xiàn)芥被,暴露穩(wěn)定的接口欧宜。上游系統(tǒng)面向接口而非實現(xiàn)編程,不依賴不穩(wěn)定的實現(xiàn)細節(jié)拴魄,這樣當實現(xiàn)發(fā)生變化的時候冗茸,上游系統(tǒng)的代碼基本上不需要做改動,以此來降低代碼間的耦合性匹中,提高代碼的擴展性夏漱。從這個設計初衷上來看,如果在我們的業(yè)務場景中顶捷,某個功能只有一種實現(xiàn)方式挂绰,未來也不可能被其他實現(xiàn)方式替換,那我們就沒有必要為其設計接口焊切,也沒有必要基于接口編程扮授,直接使用實現(xiàn)類就可以了芳室。除此之外专肪,越是不穩(wěn)定的系統(tǒng),我們越是要在代碼的擴展性堪侯、維護性上下功夫嚎尤。相反,如果某個系統(tǒng)特別穩(wěn)定伍宦,在開發(fā)完之后芽死,基本上不需要做維護乏梁,那我們就沒有必要為其擴展性,投入不必要的開發(fā)時間关贵。

重點回顧
1.“基于接口而非實現(xiàn)編程”遇骑,這條原則的另一個表述方式,是“基于抽象而非實現(xiàn)編程”揖曾。后者的表述方式其實更能體現(xiàn)這條原則的設計初衷落萎。我們在做軟件開發(fā)的時候,一定要有抽象意識炭剪、封裝意識练链、接口意識。越抽象奴拦、越頂層媒鼓、越脫離具體某一實現(xiàn)的設計,越能提高代碼的靈活性错妖、擴展性绿鸣、可維護性。

  1. 我們在定義接口的時候暂氯,一方面枚驻,命名要足夠通用,不能包含跟具體實現(xiàn)相關(guān)的字眼株旷;另一方面再登,與特定實現(xiàn)有關(guān)的方法不要定義在接口中。
    3.“基于接口而非實現(xiàn)編程”這條原則晾剖,不僅僅可以指導非常細節(jié)的編程開發(fā)锉矢,還能指導更加上層的架構(gòu)設計、系統(tǒng)設計等齿尽。比如沽损,服務端與客戶端之間的“接口”設計、類庫的“接口”設計循头。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末绵估,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子卡骂,更是在濱河造成了極大的恐慌国裳,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件全跨,死亡現(xiàn)場離奇詭異缝左,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門渺杉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蛇数,“玉大人,你說我怎么就攤上這事是越《耍” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵倚评,是天一觀的道長挽放。 經(jīng)常有香客問我,道長辑畦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任腿倚,我火速辦了婚禮纯出,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘敷燎。我一直安慰自己暂筝,他們只是感情好,可當我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布硬贯。 她就那樣靜靜地躺著焕襟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪饭豹。 梳的紋絲不亂的頭發(fā)上鸵赖,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天,我揣著相機與錄音拄衰,去河邊找鬼它褪。 笑死,一個胖子當著我的面吹牛翘悉,可吹牛的內(nèi)容都是我干的茫打。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼妖混,長吁一口氣:“原來是場噩夢啊……” “哼老赤!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起制市,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤抬旺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后息堂,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嚷狞,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年荣堰,在試婚紗的時候發(fā)現(xiàn)自己被綠了床未。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡振坚,死狀恐怖薇搁,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情渡八,我是刑警寧澤啃洋,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站屎鳍,受9級特大地震影響宏娄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜逮壁,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一孵坚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧窥淆,春花似錦卖宠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至词裤,卻和暖如春刺洒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吼砂。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工作媚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人帅刊。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓纸泡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親赖瞒。 傳聞我的和親對象是個殘疾皇子女揭,可洞房花燭夜當晚...
    茶點故事閱讀 44,614評論 2 353

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