抽象出 api 服務(wù)層的好處

0 偽代碼示例

0.1 demo結(jié)構(gòu)

-| src
 -| editCenter
    -| A.js  // 獲取渠道列表,獲取媒體素材列表
 -| resourceCenter
    -| B.js  // 獲取媒體素材列表鳞尔,獲取標(biāo)簽列表
 -| mangeConfig
    -| C.js // 獲取渠道列表捐名,獲取標(biāo)簽列表
 -| service
    -| api
     -| tagApi.js
     -| mediaApi.js
     -| chanelApi.js

0.2 未抽象出api服務(wù)層的業(yè)務(wù)層代碼

A.js

……
    let ChanelListParams = {
      //……
    };
    let mediaListParams = {
        //...
    };
    // 獲取渠道列表
    function getChanelList(){
        params = dealParams(ChanelListParams);
        trshttpService.httpServer(trshttpService.getWCMROOT(),params,'post').
        then(function(data){
            dealData(data);
        });
    }

// 獲取素材列表
    function getMediaList(){
        params = dealParams(mediaListParams);
        trshttpService.httpServer(trshttpService.getBigdataROOT(),params,'get').
        then(function(data){
            dealData(data);
        });
    }
……

B.js

……  
    let TagListParams = {
        //...
    };
    let mediaListParams = {
        //...
    };

// 獲取標(biāo)簽列表
    function getTagList(){
        params = dealParams(TagListParams);
        trshttpService.httpServer(trshttpService.getBigdataROOT(),params,'post').
        then(function(data){
            dealData(data);
        });
    }

// 獲取素材列表
    function getMediaList(){
        params = dealParams(mediaListParams);
        trshttpService.httpServer(trshttpService.getBigdataROOT(),params,'get').
        then(function(data){
            dealData(data);
        });
    }
……

C.js

……
    let TagListParams = {
        //...
    };
    let ChanelListParams = {
       
    };
// 獲取標(biāo)簽列表
    function getTagList(){
        params = dealParams(TagListParams);
        trshttpService.httpServer(trshttpService.getBigdataROOT(),params,'post').
        then(function(data){
            dealData(data);
        });
    }

// 獲取渠道列表
    function getChanelList(){
        params = dealParams(ChanelListParams);
        trshttpService.httpServer(trshttpService.getWCMROOT(),params,'post').
        then(function(data){
            dealData(data);
        });
    }
……

0.3 抽象了api服務(wù)層的代碼

A.js

    let ChanelListParams = chanelApi.getListParams();
    let mediaListParams = mediaApi.getListParams();
// 獲取渠道列表
    function getChanelList(){
        params = dealParams(ChanelListParams);
        chanelApi.getChanelList(params).
        then(function(data){
            dealData(data);
        });
    }

// 獲取素材列表
    function getMediaList(){
        params = dealParams(mediaListParams);
        mediaApi.getMediaList(params).
        then(function(data){
            dealData(data);
        });
    }

B.js

let TagListParams = tagApi.getListParams();
let mediaListParams = mediaApi.getListParams();

// 獲取標(biāo)簽列表
    function getTagList(){
        params = dealParams(TagListParams);
        tagApi.getTagList(params).
        then(function(data){
            dealData(data);
        });
    }

// 獲取素材列表
    function getMediaList(){
        params = dealParams(mediaListParams);
        mediaApi.getMediaList(params).
        then(function(data){
            dealData(data);
        });
    }

C.js

let TagListParams =  tagApi.getListParams();
let ChanelListParams = mediaApi.getListParams();
// 獲取標(biāo)簽列表
    function getTagList(){
        params = dealParams(TagListParams);
        tagApi.getTagList(params).
        then(function(data){
            dealData(data);
        });
    }

// 獲取渠道列表
    function getChanelList(){
        params = dealParams(ChanelListParams);
        chanelApi.getChanelList(params).
        then(function(data){
            dealData(data);
        });
    }

tagApi.js

……
    return {
        getList: getTagList,
        getListParams: getListParams,
        ……
    };
    
    function getTagList(params) {
        return mockdata;
        return new Promise(function(resolve,reject){
            let params = dealParams(params);
            trshttpService.httpServer(trshttpService.getBigdataROOT(),params,'post')
            .then(function(data){
                let result = dealData(data);
                resolve(result);
            },function(){reject});
        });
    }
    
    function getListParams(){
        return {
            ……
        }
    }
……

mediaApi.js

……
    return {
        getMediaList: getMediaList,
        getListParams: getListParams,
        ……
    };
    
    function getMediaList(params) {
        return new Promise(function(resolve,reject){
            let params = dealParams(params);
            trshttpService.httpServer(trshttpService.getBigdataROOT(),params,'post')
            .then(function(data){
                let result = dealData(data);
                resolve(result);
            },function(){reject});
        });
    }
    
    function getListParams(){
        return {
            ……
        }
    }
……

chanelApi.js

……
    return {
        getChanelList: getChanelList,
        getListParams: getListParams,
        ……
    };
    
    function getChanelList(params) {
        return new Promise(function(resolve,reject){
            let params = dealParams(params);
            trshttpService.httpServer(trshttpService.getBigdataROOT(),params,'post')
            .then(function(data){
                let result = dealData(data);
                resolve(result);
            },function(){reject});
        });
    }
    
    function getListParams(){
        return {
        }
    }
……

1. 復(fù)用和語(yǔ)義化

將相同的請(qǐng)求提取到公共服務(wù)中鹊奖,可以明顯提高代碼復(fù)用率摩瞎。

我們從上面的未抽象api服務(wù)層的代碼看到,現(xiàn)在我們有三種請(qǐng)求娃弓,分別是:

  • 請(qǐng)求標(biāo)簽列表
  • 請(qǐng)求素材列表
  • 請(qǐng)求渠道列表

盡管我們?cè)诘讓右呀?jīng)層狀了一層trsHttpService.httpServer服務(wù)來(lái)實(shí)現(xiàn)http請(qǐng)求的細(xì)節(jié)烛恤,做到了很高的復(fù)用性,但這里還是存在著重復(fù)性代碼冻河,比如在這三個(gè)文件中箍邮,每次都要重新定義請(qǐng)求參數(shù)對(duì)象,如果這個(gè)參數(shù)對(duì)象只有少數(shù)幾個(gè)屬性叨叙,倒也還可以锭弊,但是如果這個(gè)參數(shù)對(duì)象包含十幾二十個(gè)參數(shù),每次要用到這個(gè)請(qǐng)求前擂错,都需要定義一遍味滞,那重復(fù)率就太高了。另外钮呀,像trshttpService.httpServer(trshttpService.getBigdataROOT(),params,'post') 這樣的代碼也出現(xiàn)了多次剑鞍,不僅冗長(zhǎng),而且從這個(gè)方法本身并無(wú)法看出它是做什么事的爽醋,無(wú)法做到語(yǔ)義化蚁署。

在抽象了api服務(wù)層后,我們明顯看到兩個(gè)變化:

  • 參數(shù)對(duì)象不再在各自的控制器里定義蚂四,而是統(tǒng)一從api服務(wù)層獲取光戈。定義只在api層發(fā)生一次,而在控制器中可以重用無(wú)數(shù)次遂赠,并且只需要一個(gè)方法調(diào)用久妆。
  • 獲取數(shù)據(jù)的方法更加語(yǔ)義化,消除了無(wú)語(yǔ)義的功能性代碼

2. 應(yīng)對(duì)變化

無(wú)論后端接口的參數(shù)改動(dòng)跷睦,還是地址改動(dòng)筷弦,或者返回值改變等,都可以在該層做適配抑诸,不需要影響業(yè)務(wù)層烂琴。

我們來(lái)假設(shè)三個(gè)場(chǎng)景爹殊。

場(chǎng)景一:請(qǐng)求參數(shù)變化。

加入我們未抽象api服務(wù)層時(shí)监右,控制器里定義的獲取標(biāo)簽列表的請(qǐng)求參數(shù)如下:

let TagListParams = {
    tagModel:'aaa',
    tagTime: '...',
    pageSize: 8,
    curpage: 1
    ……
}

在進(jìn)行請(qǐng)求前边灭,我們肯定要根據(jù)用戶交互對(duì)參數(shù)進(jìn)行賦值,比如:

function dealParams(params){
    params.tagModel = userSelectModel;
    params.tagTime = userInputTime;
    ...
    return params;
}

好了健盒,功能開(kāi)發(fā)完成后,后端告訴你称簿,要把tagModel參數(shù)名稱改成 ModelName扣癣, 現(xiàn)在你會(huì)怎么做呢?當(dāng)然憨降,你可能想到了用編輯器的全局查找替換功能父虑,可是,在某個(gè)其它模塊的某個(gè)前輩開(kāi)發(fā)的代碼中授药,有下面這樣的代碼:

function someFun(){
    params.tagModel = abc;
    ...
}

而這個(gè)params可能和你要改的接口毫無(wú)關(guān)系士嚎,但全局替換可能會(huì)將它也替換掉,所以悔叽,為了避免這種問(wèn)題莱衩,你不得不一個(gè)一個(gè)去替換,如果整個(gè)系統(tǒng)中請(qǐng)求標(biāo)簽列表的地方有四五十處娇澎,那將是非常惱人的笨蚁。

如果抽象出了api服務(wù)層,那就好辦了趟庄,我們看在api層里的代碼:

tagApi.js

 //……
 function dealParams(params) {
    //……
    params.ModelName = params.tagModel;
    delete params.tagModel;
    //……
    return params;
 }

只需要在api服務(wù)層發(fā)送強(qiáng)求前的參數(shù)處理函數(shù)里統(tǒng)一處理一次即可括细,業(yè)務(wù)層的代碼完全不用做任何改動(dòng)。

場(chǎng)景二: 接口地址變化

在未抽象api層的代碼中我們看到戚啥,請(qǐng)求標(biāo)簽列表和請(qǐng)求素材列表的接口地址是同一個(gè)奋单,都是trshttpService.getBigdataROOT() 方法返回的,現(xiàn)在如果請(qǐng)求素材接口的地址不變還是用原來(lái)的猫十,而請(qǐng)求標(biāo)簽的接口使用了新的接口览濒。那么按照老的做法,我們有如下步驟:

  1. trshttpService 中新增一個(gè)地址獲取方法 getNewRoot() 方法炫彩,并返回新的地址匾七;
  2. 找到項(xiàng)目中所有請(qǐng)求標(biāo)簽列表的地方,一個(gè)一個(gè)將getBigdataRoot方法替換為getNewRoot方法江兢,當(dāng)然昨忆,由于素材列表也使用的是getBigdataRoot方法,我們同樣無(wú)法使用編輯器的全局搜索替換功能杉允。

然而邑贴,這種方法不僅效率低席里,還有最大的問(wèn)題是,程序中那么多請(qǐng)求標(biāo)簽的地方拢驾,你在執(zhí)行替換的過(guò)程中很可能會(huì)遺漏掉那么一兩處奖磁,一旦發(fā)生這種情況,那就是生產(chǎn)事故繁疤。

那么咖为,在抽象了api服務(wù)層后,我們?cè)趺醋龅哪兀?/p>

只有兩步:

  1. trshttpService 中新增一個(gè)地址獲取方法 getNewRoot() 方法稠腊,并返回新的地址躁染。
  2. 進(jìn)入tagApi.js ,將 getBigdataRoot 方法替換為 ``getNewRoot` 方法架忌。

并且由于程序中所有請(qǐng)求標(biāo)簽的地方都是調(diào)用的tagApi服務(wù)吞彤,所以不用擔(dān)心有遺漏。

場(chǎng)景三: 響應(yīng)數(shù)據(jù)變化

假設(shè)原來(lái)的素材列表返回的相應(yīng)數(shù)據(jù)是這樣的:

{
    DATA:[
    {
        relationid: 1,
        resoucetitle: '測(cè)試素材'
        //……
    }叹放,
    //……
    ],
    PAGER: {……}
}

而我們的A.jsC.js中饰恕,獲取到數(shù)據(jù)后,有大概四五十處都是使用 item.resourcetitle 來(lái)使用這個(gè)值的井仰。

現(xiàn)在后端響應(yīng)的數(shù)據(jù)改了埋嵌,不再使用resourcetitle 來(lái)顯示素材名稱,而是改為了materialName糕档。那我們?cè)趺崔k呢莉恼?

當(dāng)然,我們一樣不能使用全局搜索替換速那,因?yàn)樵谙到y(tǒng)的四十萬(wàn)行代碼中你根本無(wú)法確定是否有個(gè)地方有一個(gè)與素材業(yè)務(wù)無(wú)關(guān)但也用了resourcetitle這個(gè)命名俐银。我們當(dāng)然也不可能一個(gè)一個(gè)去找到進(jìn)行替換,一是效率低端仰,二是有可能遺漏捶惜。

如果未抽象api服務(wù)層,我們最好的做法無(wú)非是:

在每一個(gè)控制器里荔烧,請(qǐng)求完素材的dealData()方法了做如下處理:

function dealData(data){
    data.foreach(function(item){
        item.resourcetitle  = item.materialName;
        delete item.materialName
    });
    this.items = data;
}

這樣做還得有一個(gè)前提是所有請(qǐng)求素材列表的地方獲得數(shù)據(jù)后都得有一個(gè)dealData方法吱七,如果有些地方?jīng)]有,還必須再寫一個(gè)鹤竭。

讓我們?cè)俅问褂贸橄?code>api服務(wù)層的方式來(lái)解決這個(gè)問(wèn)題吧踊餐。

我們只需要將上面的遍歷和替換在mediaApi.jsdealData 里實(shí)現(xiàn)一次即可,并且我們不要求所有調(diào)用者都去在調(diào)用接口后執(zhí)行我們規(guī)定的dealData操作臀稚。

3. 面向接口編程

調(diào)用者不用關(guān)心請(qǐng)求地址如何獲取吝岭,也不關(guān)心請(qǐng)求是用POST方法還是GET方法,他唯一應(yīng)該關(guān)心的是業(yè)務(wù)邏輯需要的數(shù)據(jù)如何獲取。

大家肯定也聽(tīng)過(guò)面向接口編程窜管,面向接口的核心思想有兩點(diǎn):

  • 調(diào)用者不關(guān)心接口內(nèi)部實(shí)現(xiàn)細(xì)節(jié)

  • 將定義與實(shí)現(xiàn)分離散劫。

業(yè)務(wù)層需要使用數(shù)據(jù)時(shí),只關(guān)心你給他一個(gè)獲取數(shù)據(jù)的方法幕帆,他并不需要去關(guān)心數(shù)據(jù)去哪個(gè)接口地址拿获搏,也不需要關(guān)心這個(gè)數(shù)據(jù)是使用POST方法還是GET方法去獲取,他唯一需要關(guān)心的就是傳遞參數(shù)失乾。就像我們?nèi)ワ堭^吃飯點(diǎn)餐常熙,我們并不需要關(guān)心飯店從哪里取進(jìn)菜,也不需要關(guān)心廚師是用鐵鍋炒還是不銹鋼鍋炒碱茁,我們唯一需要的就是告訴服務(wù)員我們想吃什么症概。

在未抽象出api服務(wù)層時(shí),我們將getWCMRoot早芭,POST 這樣涉及到實(shí)現(xiàn)的細(xì)節(jié)暴露到了業(yè)務(wù)層,讓業(yè)務(wù)調(diào)用者參與了具體的實(shí)現(xiàn)方式和細(xì)節(jié)诅蝶,這是不合理的退个。

而我們抽象出了api服務(wù)層后,調(diào)用者只需要知道有個(gè)叫tagApi的服務(wù)有一個(gè)getTagList()方法可以給我們想要的標(biāo)簽列表调炬,就夠了语盈。

上面我們解釋了面向接口編程的第一個(gè)特性:調(diào)用者不關(guān)心接口內(nèi)部實(shí)現(xiàn)細(xì)節(jié)。下面我們看下第二個(gè)特性: 將定義與實(shí)現(xiàn)分離缰泡。

我們看到刀荒,在抽象出的api服務(wù)層,我們是這樣返回的:

    return {
        getTagList: getTagList,
        getListParams: getListParams,
        ……
    };
    function getTagList(){//...}
    function getListParams(){//...}

而不是像這樣:

    return {
        getTagList: function(params){
            //...
        },
        getListParams: function(params){
            //...
        }
    }

或者這樣:

this.getTagList = function(params){//...}
this.getListParams = function(params){//...}

這種寫法上的細(xì)微差異在實(shí)際開(kāi)發(fā)中帶來(lái)的效果是完全不一樣的棘钞。后兩種寫法都是將定義和實(shí)現(xiàn)耦合在了一起缠借,而第一種方法實(shí)現(xiàn)了定義與實(shí)現(xiàn)的分離。這兩種思想的差異在這段代碼里看不出明顯差異宜猜,我們換個(gè)例子泼返。

我們前端很多時(shí)候都會(huì)涉及到存儲(chǔ),假設(shè)現(xiàn)在的存儲(chǔ)方案有cookielocalStorage兩種姨拥,一開(kāi)始我們采用了cookie方案绅喉,api 像下面這樣:

  • storeApi.js
return {
    this.getCookie = function(){...}
    this.setCookie = function(){...}
    this.removeCookie = function(){...}
}

調(diào)用者像下面這樣使用:

  • D.js

    storeApi.getCookie(...);
    storeApi.setCookie(...);
    storeApi.removeCookie(...);
    

后來(lái)發(fā)現(xiàn)cookie 方案不再滿足我們的需求了,更換為 localStorage方案了叫乌,那有這么幾種方式:

方式一:偷懶改原來(lái)的實(shí)現(xiàn)

  • store.js
this.getCookie = function(){//這里是localStorage實(shí)現(xiàn)}
this.setCookie = function(){//這里是localStorage實(shí)現(xiàn)}
this.removeCookie = function(){//這里是localStorage實(shí)現(xiàn)}

這樣做的好處是業(yè)務(wù)代碼不用更改柴罐,壞處很明顯,方法名和實(shí)現(xiàn)不一樣憨奸,造成后期維護(hù)成本上升.

方式二:新建一個(gè)服務(wù)

  • stroeForLocal.js
this.getLocalStorage = function(){...};
this.setLocalStorage = function(){...};
this.removeLocalStorage = function(){...};

然后修改業(yè)務(wù)代碼:

  • D.js
stroeForLocalApi.getLocalStorage(...)
stroeForLocalApi.setLocalStorage(...)
stroeForLocalApi.removeLocalStorage(...)

這樣做壞處顯而易見(jiàn):需要大量改動(dòng)業(yè)務(wù)代碼革屠。

如果我們一開(kāi)始就采用定義與實(shí)現(xiàn)分離的方式,看看代碼:

  • store.js

    return {
      get: getCookie,
      set: setCookie,
      remove: removeCookie
    };
    
    function getCookie(){//...}
    function setCookie(){//...}
    function removeCookie(){//...}
    
  • D.js

    storeApi.storemethod = 'local'
    storeApi.get(...);
    storeApi.set(...);
    storeApi.remove(...);
    

變更為localStorage方案后:

  • store.js

    this.storeMethod ='';
    if(this.stroeMethod === 'cookie'){
    return {
      get: getCookie,
      set: setCookie,
      remove: removeCookie
    };
    }else {
    return {
      get: getLocalStorage,
      set: setLocalStorage,
      remove: removeLocalStorage
      
    };
    }
    
    
    function getCookie(){//...}
    function setCookie(){//...}
    function removeCookie(){//...}
    function getLocalStorage(){...}
    function setLocalStorage(){...}
    function removeLocalStorage(){...}
    

而業(yè)務(wù)層則是無(wú)感知的。

這就是定義與實(shí)現(xiàn)分離所帶來(lái)的好處屠阻,業(yè)務(wù)層只需要知道有個(gè)存儲(chǔ)服務(wù)提供了set,get,remove方法就行了红省,它并不關(guān)心該服務(wù)采取哪種存儲(chǔ)方案,存儲(chǔ)方案的變化只需要體現(xiàn)在api服務(wù)層国觉,不影響業(yè)務(wù)層吧恃。

4. 減少依賴

在請(qǐng)求服務(wù)還沒(méi)開(kāi)發(fā)的情況下(也有可能是后端接口還沒(méi)開(kāi)發(fā)完),只要給調(diào)用者提供一份接口清單麻诀,調(diào)用者就可以進(jìn)行業(yè)務(wù)開(kāi)發(fā)了痕寓,等請(qǐng)求服務(wù)將這些接口全部實(shí)現(xiàn)后,調(diào)用者不用做任何修改就可以直接使用蝇闭。(可以配合mock方案)

在未抽離出api服務(wù)層的情況下呻率,我們一般的開(kāi)發(fā)過(guò)程是

  • 拿到一個(gè)需求
  • 等后端給接口文檔的同時(shí)先寫靜態(tài)頁(yè)面(可能會(huì)在業(yè)務(wù)代碼中造假數(shù)據(jù))
  • 等到后端給我們接口文檔后,我們開(kāi)始寫數(shù)據(jù)請(qǐng)求部分
    • 這時(shí)候如果后端接口已經(jīng)完成呻引,我們就直接對(duì)接數(shù)據(jù)礼仗,并在業(yè)務(wù)代碼中寫請(qǐng)求了;
    • 這時(shí)候如果后端接口還沒(méi)寫逻悠,我們就只能先按照接口文檔先寫拼參數(shù)發(fā)請(qǐng)求的部分元践,至于數(shù)據(jù),還是自己造的假數(shù)據(jù)童谒。
    • 后面如果后端通知我們接口有變動(dòng)单旁,我們又得挨個(gè)在業(yè)務(wù)代碼里做相應(yīng)的改動(dòng)。

這個(gè)過(guò)程中饥伊,我們對(duì)后端其實(shí)是存在著很強(qiáng)的依賴性的象浑。

比如按照未抽象api服務(wù)層的代碼邏輯,在后端只有文檔尚未開(kāi)發(fā)的時(shí)候琅豆,連后端都不知道接口要部署到哪個(gè)地址愉豺,那么前端就沒(méi)法去寫trshttpService.getBigdataRoot 這樣的代碼,因?yàn)槟愀静恢赖刂贰?/p>

而在抽象出api服務(wù)層后趋距,我們的過(guò)程可能變成了下面這樣:

  • 拿到一個(gè)需求粒氧,根據(jù)需求在api服務(wù)層定義數(shù)據(jù)請(qǐng)求接口,可以先不實(shí)現(xiàn)請(qǐng)求节腐,但可以根據(jù)頁(yè)面展示的元素定義并返回假數(shù)據(jù)(mock)外盯,這些假數(shù)據(jù)的字段名稱完全可以自己根據(jù)業(yè)務(wù)需求來(lái)定義。
  • 等后端給接口文檔的同時(shí)做開(kāi)發(fā)翼雀,同時(shí)利用假數(shù)據(jù)來(lái)進(jìn)行頁(yè)面功能測(cè)試饱苟,
  • 等后端給我們接口文檔后,我們?cè)?code>api層對(duì)參數(shù)和響應(yīng)數(shù)據(jù)進(jìn)行適配狼渊,而業(yè)務(wù)層代碼只需要因?yàn)閰?shù)的不同做少量適配就好箱熬。
  • 等后端完成后类垦,在api 服務(wù)層去掉假數(shù)據(jù),對(duì)接真實(shí)接口城须。

以上面的場(chǎng)景為例蚤认,即使后端還未開(kāi)發(fā),我們?cè)跇I(yè)務(wù)代碼中也只是調(diào)用tagApi.getTagList()這樣的方法糕伐,在api層我們直接返回假數(shù)據(jù)砰琢,這樣既不影響前期開(kāi)發(fā)節(jié)奏,后期即使對(duì)接了真實(shí)接口后良瞧,這一塊的代碼也不用變動(dòng)陪汽。

我們看到,這個(gè)過(guò)程最大程度地減少了前端開(kāi)發(fā)對(duì)后端接口的依賴褥蚯,使得我們的開(kāi)發(fā)更加順暢挚冤。

5. 單一職責(zé)

數(shù)據(jù)請(qǐng)求工作應(yīng)該由專門的數(shù)據(jù)請(qǐng)求服務(wù)來(lái)完成,包括發(fā)送請(qǐng)求前的參數(shù)處理和接收到數(shù)據(jù)后的數(shù)據(jù)適配工作

以上說(shuō)的都是實(shí)際開(kāi)發(fā)中api服務(wù)層帶給我們的好處赞庶,而之所以有那些好處训挡,是因?yàn)樗旧碜裱肆己玫脑O(shè)計(jì)原則,尤其是編程領(lǐng)域最重要的一條設(shè)計(jì)原則:?jiǎn)我宦氊?zé)原則歧强。

單一職責(zé)的核心點(diǎn)就是各司其職舍哄,業(yè)務(wù)的歸業(yè)務(wù),服務(wù)的歸服務(wù)誊锭。

就像我們?nèi)ワ堭^吃飯,我們不會(huì)自己把點(diǎn)菜單送去廚房弥锄,而是由服務(wù)員送去廚房一樣丧靡,涉及請(qǐng)求的東西也不應(yīng)該由調(diào)用者來(lái)參與(比如請(qǐng)求地址獲取、http方法指定籽暇、參數(shù)處理温治、響應(yīng)數(shù)據(jù)適配)等等。

所有涉及請(qǐng)求的工作(包括請(qǐng)求的前置工作和后置工作)都應(yīng)該統(tǒng)一交由api服務(wù)層來(lái)進(jìn)行戒悠,這才符合我們說(shuō)的單一職責(zé)原則熬荆。

6.便于統(tǒng)一管理

在業(yè)務(wù)邏輯還未開(kāi)發(fā)前就可以根據(jù)后端提供的接口文檔把所有和數(shù)據(jù)請(qǐng)求相關(guān)的事務(wù)集中完成

基于以上的工作嵌巷,進(jìn)行了api服務(wù)的抽象后论寨,所有和請(qǐng)求相關(guān)的工作都可以統(tǒng)一放置在api服務(wù)層進(jìn)行了,這樣便于統(tǒng)一管理所有接口請(qǐng)求洞焙,并且還帶來(lái)一個(gè)好處: 如果你在開(kāi)發(fā)前已經(jīng)拿到了后端給的接口文檔寒矿,那你就可以直接根據(jù)接口文檔把所有和請(qǐng)求相關(guān)的事務(wù)先集中完成突琳,然后再去做業(yè)務(wù),這樣不用在接口和業(yè)務(wù)之間來(lái)回切換符相,提升開(kāi)發(fā)效率拆融。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子镜豹,更是在濱河造成了極大的恐慌傲须,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趟脂,死亡現(xiàn)場(chǎng)離奇詭異泰讽,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)散怖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門菇绵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人镇眷,你說(shuō)我怎么就攤上這事咬最。” “怎么了欠动?”我有些...
    開(kāi)封第一講書人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵永乌,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我具伍,道長(zhǎng)翅雏,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任人芽,我火速辦了婚禮望几,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘萤厅。我一直安慰自己橄抹,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布惕味。 她就那樣靜靜地躺著楼誓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪名挥。 梳的紋絲不亂的頭發(fā)上疟羹,一...
    開(kāi)封第一講書人閱讀 51,590評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音禀倔,去河邊找鬼榄融。 笑死,一個(gè)胖子當(dāng)著我的面吹牛救湖,可吹牛的內(nèi)容都是我干的剃袍。 我是一名探鬼主播,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼捎谨,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼民效!你這毒婦竟也來(lái)了憔维?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤畏邢,失蹤者是張志新(化名)和其女友劉穎业扒,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體舒萎,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡程储,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了臂寝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片章鲤。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖咆贬,靈堂內(nèi)的尸體忽然破棺而出败徊,到底是詐尸還是另有隱情,我是刑警寧澤掏缎,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布皱蹦,位于F島的核電站,受9級(jí)特大地震影響眷蜈,放射性物質(zhì)發(fā)生泄漏沪哺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一酌儒、第九天 我趴在偏房一處隱蔽的房頂上張望辜妓。 院中可真熱鬧,春花似錦忌怎、人聲如沸嫌拣。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至捶索,卻和暖如春插掂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背腥例。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工辅甥, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人燎竖。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓璃弄,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親构回。 傳聞我的和親對(duì)象是個(gè)殘疾皇子夏块,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355

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