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)簽的接口使用了新的接口览濒。那么按照老的做法,我們有如下步驟:
- 在
trshttpService
中新增一個(gè)地址獲取方法getNewRoot()
方法炫彩,并返回新的地址匾七; - 找到項(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>
只有兩步:
- 在
trshttpService
中新增一個(gè)地址獲取方法getNewRoot()
方法稠腊,并返回新的地址躁染。 - 進(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.js
和C.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.js
的 dealData
里實(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ǔ)方案有cookie
和 localStorage
兩種姨拥,一開(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ā)效率拆融。