本來想寫一個怎么寫好代碼的主題,昨天突然看到了淘寶團隊的這篇文章《編寫「可讀」代碼的實踐》,把我想到的沒想到的基本都說了。然后我就想重復(fù)的內(nèi)容就不說了(別人講的比我透徹)饲鄙,我自己就補充下自己對于接口幾點想法,然后標(biāo)題呢圆雁,我就死皮賴臉的抱下大腿了忍级。
修改接口
一般來說,對于現(xiàn)有系統(tǒng)我們原則上不主張修改已有接口伪朽,如果原有接口有問題或者沒有辦法滿足要求了轴咱,那么我們會新增接口,然后在調(diào)用的地方替換掉老接口烈涮。如果直接修改現(xiàn)有接口會影響到所有調(diào)用此接口的地方朴肺,一旦修改后的接口出現(xiàn)問題,那么將影響整個系統(tǒng)的穩(wěn)定性坚洽。而新增接口戈稿,如果出現(xiàn)問題只要通過簡單的修改調(diào)用處的代碼即可回退。如果一定要修改老接口讶舰,那么要遵循一定的原則:
1. 輸入輸出不能有變化(此時有單元測試的好處就體現(xiàn)出來了)鞍盗。
2. 接口的行為不能有變化需了,同步的接口不能變成異步,反之亦然橡疼,原來不拋異常的不能變成拋異常。
接口實現(xiàn)要高內(nèi)聚
這里高內(nèi)聚主要談的是接口要把自身的異陈郏或者差異化在本身的實現(xiàn)中處理掉欣除,不能把自身的問題傳導(dǎo)到接口之外。譬如說前端通過瀏覽器獲得的 language code挪略,可能大小寫和格式因每個瀏覽器的實現(xiàn)有差異化历帚,那么我們封裝一個接口 getLanguageCode 就要把這個差異化在接口內(nèi)處理掉,保證這個接口返回的結(jié)果是標(biāo)準(zhǔn)化的杠娱,調(diào)用者拿到這個返回值在傳給其他模塊甚至后臺時一定是可預(yù)測的值(符合規(guī)范)挽牢。如果你把自身的差異化擴散到其他接口,模塊或者系統(tǒng)摊求,帶來得就不單單是可維護性的風(fēng)險:
1. 其他系統(tǒng)的維護者不一定明白為什么對這種情況需要做特殊處理禽拔,增加了維護的難度。
2. 即使其他系統(tǒng)對你的輸出做了特殊處理室叉,但是一旦你的異扯闷埽或者差異化變化了,那么修改會涉及到多個系統(tǒng)茧痕,整個系統(tǒng)的穩(wěn)定性就降低了野来。
3. 最壞的情況是某個系統(tǒng)的異常情況處理傳播到了整個系統(tǒng)鏈的多個環(huán)節(jié),一旦出現(xiàn)問題踪旷,非常難追查源頭曼氛。
接口輸出的一致性
籠統(tǒng)的說就是方法的返回類型最好能保持一致,即缺省的狀態(tài)下和非缺省的狀態(tài)下返回值的類型要一致令野,譬如getUsername舀患,如果能獲取到用戶名則返回一個 string 的用戶名,如果允許用戶不填用戶名气破,則此時調(diào)用 getUsername 應(yīng)該返回空字符串 ''构舟,而不是 undefined 或者 null。這點在前端也是非常好用堵幽,通常獲取到字符串后都需要在界面上展示狗超,如果缺省返回 undefined,則需要再增加一次判斷朴下,當(dāng) undefined 的時候在頁面上展示空字符串努咐,如果返回類型一致就不需要這一步。
原始類型的返回值比較好統(tǒng)一殴胧,非原始類型就需要分情況討論了渗稍。
第一種情況佩迟,返回類型的對象表示的是數(shù)據(jù)結(jié)構(gòu),譬如系統(tǒng)定義了用戶可以有多個聯(lián)系方式竿屹,那么 getUserContact 定義為返回一個數(shù)組报强,數(shù)組項是用戶的填寫的手機號,當(dāng)用戶未填或者查詢不到手機號時拱燃,getUserContact 應(yīng)該返回空數(shù)據(jù) [] 而不是 undefined秉溉。這么做的好處是,一般調(diào)用返回數(shù)據(jù)結(jié)構(gòu)的接口后碗誉,都需要對數(shù)據(jù)結(jié)構(gòu)進行操作召嘶,比如遍歷,篩選哮缺。如果返回類型一致就不需要對是否異常情況進行判斷(調(diào)用者不會迷惑說到底沒有聯(lián)系方式返回的結(jié)果是 undefined 還是 null 還是 []弄跌,對調(diào)用者友好)。
第二種情況尝苇,返回的對象本身表示的是一個 domain object铛只,譬如獲取賬戶信息 getUserAccountInfo,返回的應(yīng)該是一個 AccountInfo 的 domain object糠溜,那么如果用戶沒有填寫或者獲取不到格仲,返回應(yīng)該是一個 null,而不是一個空對象 {}诵冒。因為返回值本身是否為 null 是有現(xiàn)實意義的凯肋,表示是否存在對應(yīng)對象。和第一種情況的區(qū)別是汽馋,第一種情況數(shù)據(jù)如果不存在是通過數(shù)據(jù)項來表示的(array.length===0, map.entries().length===0)侮东,數(shù)據(jù)結(jié)構(gòu)本身是不應(yīng)該變化的。這里為什么選擇 null 而不是 undefined豹芯,也是想把沒有這個對象和_未定義這個對象_這兩種情況區(qū)分開來悄雅,當(dāng)然這個約定不是強制的。
接口輸出一致性原則我們在 js 的眾多 api 中都能找到很好的體現(xiàn)铁蹈,譬如 Array.prototype.findIndex 如果沒有找到 index 則返回的是-1宽闲,譬如 Array.prototype.filter 如果過濾不出符合條件的項目則返回的是空數(shù)組。譬如 Array.prototype.find 如果找到到符合條件的則返回 undefined握牧。