程序員的精神,不該不止於實現,更要注重優化。不該止於表面,更要研究內部機制,方能青出於藍而勝於藍。javascript
在上家公司開發後臺管理系統的時候,頻繁要處理各類數據顯示的問題,一開始是實現就好。後來寫多了,本身看得也難受了。就想着怎麼優化代碼和複用了。下面就經過一個簡單的例子,怎麼讓 API 更加的實用,更好的複用。前端
1.代碼的實用性,只能儘可能,儘可能再儘可能。不會出現完美的API,或者是一次編寫,永不修改的 API 。java
2.關於實用性,API 命名和擴展性也很重要。但以前寫過文章,在這裏就不重複了。[前端開發]--分享我的習慣的命名方式,重構 - 設計API的擴展機制程序員
好比有一個需求,有這樣的數據數組
{
cashAmount: 236700,//回款金額(分)
cashDate: "2018-05-26 10:25:28",//回款時間
cashId: "SM2018022800020692",//回款ID
cashStatus: 0,//回款狀態
createTime: "2018-05-23 10:26:25",//建立時間
custoName: "廣州測試有限公司",//回款公司名稱
id: "SM2018022800020692",//回款ID
merchandisers: "守候",//回款公司聯繫人
ordId: "SO2018022800020692",//訂單ID
payChannel: null,//支付方式
remark: "",//備註
userMobile: "18819222363",//回款公司聯繫人電話
}
複製代碼
須要對數據進行如下處理,再渲染到頁面微信
1.cashAmount 轉換成元,並保留兩位小數函數
2.cashStatus 進行解析(0-未回款 1-已回款)post
3.payChannel 進行解析 ('zfb'-支付寶,'wx'-微信支付,'cash'-現金支付,'bankTransfer'-銀行轉帳)測試
4.全部值爲 '' , null , undefined 的字段,所有設置爲:'--'微信支付
面對這樣的須要,很簡單,順手就來
let obj = {
cashAmount: 236700,//回款金額(分)
cashDate: "2018-05-26 10:25:28",//回款時間
cashId: "SM2018022800020692",//回款ID
cashStatus: 0,//回款狀態
createTime: "2018-05-23 10:26:25",//建立時間
custoName: "廣州測試有限公司",//回款公司名稱
id: "SM2018022800020692",//回款ID
merchandisers: "守候",//回款公司聯繫人
ordId: "SO2018022800020692",//訂單ID
payChannel: null,//支付方式
remark: "",//備註
userMobile: "13226452474",//回款公司聯繫人電話
}
function setValue(obj) {
let _obj=JSON.parse(JSON.stringify(obj));
//設置金額
_obj.cashAmount = (_obj.cashAmount / 100).toFixed(2);
//解析回款狀態
_obj.cashStatus = _obj.cashStatus === 0 ? '未回款' : '已回款';
//解析支付方式
let payChannelLabel = {
'zfb': '支付寶',
'wx': '微信支付',
'cash': '現金支付',
'bankTransfer': '銀行轉帳'
}
_obj.payChannel=payChannelLabel[_obj.payChannel];
//設置默認值
for (let key in _obj){
if(_obj[key]===''||_obj[key]===null||_obj[key]===undefined){
_obj[key]='--'
}
}
return _obj;
}
obj=setValue(obj);
console.log(obj)
複製代碼
結果也正確,以下圖
可是若是之後需求變了,好比 userMobile 要改爲 xxx xxx xxxx 這種展現方式呢?
也很簡單,修改下
function setValue(obj) {
let _obj=JSON.parse(JSON.stringify(obj));
//設置金額
//解析回款狀態
//解析支付方式
/*和上面代碼同樣,不重複粘貼*/
//設置電話號碼格式
let _formatType="xxx xxx xxxx",i = 0;
_obj.userMobile= _formatType.replace(/x/g, function(){
return _obj.userMobile[i++]
});
//設置默認值
/*和上面代碼同樣,不重複粘貼*/
}
複製代碼
代碼寫好了,想必你們也開始難受了。由於每改一次需求,就要改一次 setValue 。改的多了,出現問題的機率就大了。並且,這樣沒複用性。試想,若是別的頁面有一個需求,一樣的數據。可是 cashDate 字段只須要精確到時分秒。這樣的需求,大同小異。但上面的代碼不適用,須要拷貝一個 setValue 方法(就叫 setValue2 吧),而後添加 cashDate 只顯示 時分秒的邏輯。代碼很好寫
function setValue2(obj) {
let _obj=JSON.parse(JSON.stringify(obj));
//設置金額
//解析回款狀態
//解析支付方式
//設置電話號碼格式
/*和上面代碼同樣,不重複粘貼*/
//設置 cashDate 只顯示時分秒
_obj.cashDate= _obj.cashDate.split(' ')[0];
//設置默認值
/*和上面代碼同樣,不重複粘貼*/
}
複製代碼
想必你們更難受了,由於沒發覆用,致使出現了幾乎徹底同樣的函數。這個問題解決方式不少,先說下第一個,也是一個 API 設計原則--單一職責原則。
顧名思義,單一職責原則就是讓每個函數只作一件事。下面把代碼改造下
/** * @description 設置默認值 * @param obj 待處理對象 * @return obj 已處理對象 */
function setDefault(obj) {
let _obj=JSON.parse(JSON.stringify(obj));
for (let key in _obj){
if(_obj[key]===''||_obj[key]===null||_obj[key]===undefined){
_obj[key]='--'
}
}
return _obj;
}
/** * @description 格式化電話號碼 * @param obj 待處理對象 * @return obj 已處理對象 */
function setFormatMobile(obj) {
let _obj=JSON.parse(JSON.stringify(obj));
let _formatType="xxx xxx xxxx",i = 0;
_obj.userMobile= _formatType.replace(/x/g, function(){
return _obj.userMobile[i++]
});
return _obj;
}
/** * @description 解析支付方式 * @param obj 待處理對象 * @return obj 已處理對象 */
function setPayChannelLabel(obj) {
let _obj=JSON.parse(JSON.stringify(obj));
let payChannelLabel = {
'zfb': '支付寶',
'wx': '微信支付',
'cash': '現金支付',
'bankTransfer': '銀行轉帳'
}
_obj.payChannel = payChannelLabel[_obj.payChannel];
return _obj;
}
/** * @description 設置回款金額 * @param obj 待處理對象 * @return obj 已處理對象 */
function setCashAmount(obj) {
let _obj=JSON.parse(JSON.stringify(obj));
_obj.cashAmount = (_obj.cashAmount / 100).toFixed(2);
return _obj;
}
/** * @description 解析回款狀態 * @param obj 待處理對象 * @return obj 已處理對象 */
function setCashStatus(obj) {
let _obj=JSON.parse(JSON.stringify(obj));
_obj.cashStatus = _obj.cashStatus === 0 ? '未回款' : '已回款';
return _obj;
}
obj=setFormatMobile(obj);
obj=setCashStatus(obj);
obj=setCashAmount(obj);
obj=setPayChannelLabel(obj);
obj=setDefault(obj);
複製代碼
結果同樣,若是須要加上 cashDate 只顯示 時分秒。加上邏輯就好了
/** * @description 設置匯款時間 * @param obj 待處理對象 * @return obj 已處理對象 */
function setCashDate(obj) {
let _obj=JSON.parse(JSON.stringify(obj));
_obj.cashDate = _obj.cashDate.split(' ')[0];
return _obj;
}
obj=setFormatMobile(obj);
obj=setCashStatus(obj);
obj=setCashAmount(obj);
obj=setCashDate(obj);
obj=setPayChannelLabel(obj);
obj=setDefault(obj);
console.log(obj)
複製代碼
讓 API 保持單一原則的好處是,複用性比複雜的 API 更好,並且編寫的難度更低。
上面的寫法雖然實現了複用,看着比以前好了一點,可是看着也是難受,畢竟賦值了幾回,並且還有那麼多的全局函數。
首先,全局函數這個容易解決,用一個對象包裹起來,全局函數少了,也方便管理。
重複的代碼和註釋,這裏忽略,不重複粘貼
let handle={
setDefault(obj) {
//省略的代碼
},
setFormatMobile(obj) {
//省略的代碼
},
setPayChannelLabel(obj) {
//省略的代碼
},
setCashAmount(obj) {
//省略的代碼
},
setCashStatus(obj) {
//省略的代碼
}
}
obj=handle.setFormatMobile(obj);
obj=handle.setCashStatus(obj);
obj=handle.setCashAmount(obj);
obj=handle.setPayChannelLabel(obj);
obj=handle.setDefault(obj);
console.log(obj)
複製代碼
第二個讓人難受的地方就是一個步驟,通過了幾回的賦值,這個不免有點難受,寫起來也麻煩,記憶成本高。解決起來也很簡單,就是另寫一個函數,把那些操做步驟封裝在一塊兒就好了。封裝的目的就是爲了讓使用的人,只須要記住一個函數的使用方式就能夠了,不須要記住多個函數的使用方式。
let handle={
/*省略代碼*/
setCash(obj){
let _obj=JSON.parse(JSON.stringify(obj));
_obj=this.setFormatMobile(_obj);
_obj=this.setCashStatus(_obj);
_obj=this.setCashAmount(_obj);
_obj=this.setPayChannelLabel(_obj);
_obj=this.setDefault(_obj);
return _obj;
}
}
obj=handle.setCash(obj);
console.log(obj)
複製代碼
上面的代碼,看着算是比較舒服了,可是問題仍是有,就是 setCash 函數寫得太死了。固定了五個方法 :setFormatMobile,setCashStatus,setCashAmount,setPayChannelLabel,setDefault 。若是之後不須要處理電話號碼,又要改 setCash ,把 _obj=this.setFormatMobile(_obj);
這行代碼去掉。雖然改動也很小,可是問題就出來了。若是其中一個地方須要執行 setFormatMobile
,就不能刪除。若是另外一個地方, 不須要執行 setFormatMobile
,就要刪除。這樣子就顧此失彼了。
解決的方案想必你們也知道了,就是須要執行什麼函數,就在函數上動態傳入。
let handle={
/*省略代碼*/
setCash(obj,fns='setFormatMobile,setCashStatus,setCashAmount,setPayChannelLabel,setDefault'){
let _obj=JSON.parse(JSON.stringify(obj));
let _fns=fns.split(',');
_fns.forEach(item => {
_obj=this[item](_obj);
});
return _obj;
}
}
obj=handle.setCash(obj);
console.log(obj)
//好比另外一個地方不須要執行 setFormatMobile
obj = {
cashAmount: 236700,//回款金額(分)
cashDate: "2018-05-26 10:25:28",//回款時間
cashId: "SM2018022800020692",//回款ID
cashStatus: 0,//回款狀態
createTime: "2018-05-23 10:26:25",//建立時間
custoName: "廣州測試有限公司",//回款公司名稱
id: "SM2018022800020692",//回款ID
merchandisers: "守候",//回款公司聯繫人
ordId: "SO2018022800020692",//訂單ID
payChannel: null,//支付方式
remark: "",//備註
userMobile: "13226452474",//回款公司聯繫人電話
}
obj=handle.setCash(obj,'setCashStatus,setCashAmount,setPayChannelLabel,setDefault');
console.log('好比另外一個地方不須要執行 setFormatMobile',obj)
複製代碼
看到這裏,好像差很少了。可是寫下去,你們纔會知道,通常的後臺管理系統的用戶列表,數據通常不會只有一條。通常而言是一個數組對象。以下
let arr=[
{
cashAmount: 236700,//回款金額(分)
cashDate: "2018-05-26 10:25:28",//回款時間
cashId: "SM2018022800020692",//回款ID
cashStatus: 0,//回款狀態
createTime: "2018-05-23 10:26:25",//建立時間
custoName: "廣州測試有限公司",//回款公司名稱
id: "SM2018022800020692",//回款ID
merchandisers: "守候",//回款公司聯繫人
ordId: "SO2018022800020692",//訂單ID
payChannel: null,//支付方式
remark: "",//備註
userMobile: "13226452474",//回款公司聯繫人電話
},
{/*省略的代碼*/},
{/*省略的代碼*/},
{/*省略的代碼*/},
//省略的代碼
]
複製代碼
寫起來的時候呢,要這樣寫
arr.forEach((item,index)=>{
arr[index]=handle.setCash(item);
})
console.log(arr)
複製代碼
雖然代碼很少,可是有更好的方案,就用更好的方案。好比使用批量處理的方式。就多寫一個函數就好了。
let handle={
/*省略代碼*/
batch(arr,fns,...orther){
let _arr=JSON.parse(JSON.stringify(arr));
let _fns=fns.split(',');
_arr.forEach((item,index)=>{
_fns.forEach(fn => {
_arr[index]=this[fn](_arr[index],...orther);
});
})
return _arr
}
}
複製代碼
調用的時候就比以前簡單了一點,結果也正確
arr=handle.batch(arr,'setCash')
console.log(arr)
複製代碼
要傳其餘參數也能夠
arr=handle.batch(arr,'setCash','setCashStatus,setCashAmount,setPayChannelLabel,setDefault')
console.log(arr)
複製代碼
若是要傳入多個操做函數
arr=handle.batch(arr,'setCashStatus,setCashAmount')
console.log(arr)
複製代碼
關於開發上,API 的實用性,暫時就先提這幾個方面,若是之後發現有其餘例子,還能從其餘方面提升 API 的實用性,就再發文章分享。關於這篇文章,也是我目前嘗試的一種方式,若是你們有更好的一個實現方式,歡迎在評論區留言。
-------------------------華麗的分割線--------------------
想了解更多,和我交流,內推職位,請添加我微信。或者關注個人微信公衆號:守候書閣