自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

重構(gòu) - 改善代碼的各方面問題

開發(fā) 前端
重構(gòu)不是對以前代碼的全盤否定,而是利用更好的方式,寫出更好,更有維護(hù)性代碼。不斷的追求與學(xué)習(xí),才有更多的進(jìn)步。

[[227146]]

重構(gòu)不是對以前代碼的全盤否定,而是利用更好的方式,寫出更好,更有維護(hù)性代碼。不斷的追求與學(xué)習(xí),才有更多的進(jìn)步。

1.前言

做前端開發(fā)有一段時(shí)間了,在這段時(shí)間里面,對于自己的要求,不僅僅是項(xiàng)目能完成,功能正常使用這一層面上。還盡力的研究怎么寫出優(yōu)雅的代碼,性能更好,維護(hù)性更強(qiáng)的代碼,通俗一點(diǎn)就是重構(gòu)。這篇文章算是我一個(gè)小記錄,在此分享一下。該文章主要針對介紹,例子也簡單,深入復(fù)雜的例子等以后有適合的實(shí)例再進(jìn)行寫作分享。如果大家對怎么寫出優(yōu)雅的代碼,可維護(hù)的代碼,有自己的見解,或者有什么重構(gòu)的實(shí)力,歡迎指點(diǎn)評論。

關(guān)于重構(gòu),準(zhǔn)備寫一個(gè)系列的文章,不定時(shí)更新,主要針對以下方案:邏輯混亂重構(gòu),分離職責(zé)重構(gòu),添加擴(kuò)展性重構(gòu),簡化使用重構(gòu),代碼復(fù)用重構(gòu)。其中會穿插以下原則:單一職責(zé)原則,最少知識原則,開放-封閉原則。如果大家對重構(gòu)有什么好的想法,或者有什么好的實(shí)例,歡迎留言評論,留下寶貴的建議。

2.什么是重構(gòu)

首先,重構(gòu)不是重寫。重構(gòu)大概的意思是在不影響項(xiàng)目的功能使用前提下,使用一系列的重構(gòu)方式,改變項(xiàng)目的內(nèi)部結(jié)構(gòu)。提高項(xiàng)目內(nèi)部的可讀性,可維護(hù)性。

無論是什么項(xiàng)目,都有一個(gè)從簡單到復(fù)雜的一個(gè)迭代過程。在這個(gè)過程里面,在不影響項(xiàng)目的使用情況下,需要不斷的對代碼進(jìn)行優(yōu)化,保持或者增加代碼的可讀性,可維護(hù)性。這樣一來,就可以避免在團(tuán)隊(duì)協(xié)作開發(fā)上需要大量的溝通,交流。才能加入項(xiàng)目的開發(fā)中。

3.為什么重構(gòu)

衣服臟了就洗,破了就補(bǔ),不合穿就扔。

隨著業(yè)務(wù)需求的不斷增加,變更,舍棄,項(xiàng)目的代碼也難免會出現(xiàn)瑕疵,這就會影響代碼的可讀性,可維護(hù)性,甚至影響項(xiàng)目的性能。而重構(gòu)的目的,就是為了解決這些瑕疵,保證代碼質(zhì)量和性能。但是前提是不能影響項(xiàng)目的使用。

至于重構(gòu)的原因,自己總結(jié)了一下,大概有以下幾點(diǎn)

  1.     函數(shù)邏輯結(jié)構(gòu)混亂,或因?yàn)闆]注釋原因,連原代碼寫作者都很難理清當(dāng)中的邏輯。
  2.     函數(shù)無擴(kuò)展性可言,遇到新的變化,不能靈活的處理。
  3.     因?yàn)閷ο髲?qiáng)耦合或者業(yè)務(wù)邏輯的原因,導(dǎo)致業(yè)務(wù)邏輯的代碼巨大,維護(hù)的時(shí)候排查困難。
  4.     重復(fù)代碼太多,沒有復(fù)用性。
  5.     隨著技術(shù)的發(fā)展,代碼可能也需要使用新特性進(jìn)行修改。
  6.     隨著學(xué)習(xí)的深入,對于以前的代碼,是否有著更好的一個(gè)解決方案。
  7.     因?yàn)榇a的寫法,雖然功能正常使用,但是性能消耗較多,需要換方案進(jìn)行優(yōu)化

4.何時(shí)重構(gòu)

在合適的時(shí)間,在合適的事情

在我的理解中,重構(gòu)可以說是貫穿整一個(gè)項(xiàng)目的開發(fā)和維護(hù)周期,可以當(dāng)作重構(gòu)就是開發(fā)的一部分。通俗講,在開發(fā)的任何時(shí)候,只要看到代碼有別扭,激發(fā)了強(qiáng)迫癥,就可以考慮重構(gòu)了。只是,重構(gòu)之前先參考下面幾點(diǎn)。

  • 首先,重構(gòu)是需要花時(shí)間去做的一件事?;ǖ臅r(shí)間可能比之前的開發(fā)時(shí)間還要多。
  • 其次,重構(gòu)是為了把代碼優(yōu)化,前提是不能影響項(xiàng)目的使用。
  • ***,重構(gòu)的難度大小不一,可能只是稍微改動(dòng),可能難度比之前開發(fā)還要難。

基于上面的幾點(diǎn),需要大家去評估是否要進(jìn)行重構(gòu)。評估的指標(biāo),可以參考下面幾點(diǎn)

  • 數(shù)量: 需要重構(gòu)的代碼是否過多。
  • 質(zhì)量: 可讀性,可維護(hù)性,代碼邏輯復(fù)雜度,等問題,對代碼的質(zhì)量影響是否到了一個(gè)難以忍受的地步。
  • 時(shí)間: 是否有充裕的時(shí)間進(jìn)行重構(gòu)和測試。
  • 效果: 如果重構(gòu)了代碼,得到哪些改善,比如代碼質(zhì)量提高了,性能提升了,更好的支持后續(xù)功能等。

5.怎么重構(gòu)

選定目標(biāo),針對性出擊

怎么重構(gòu),這個(gè)就是具體情況,具體分析了。如同“為什么重構(gòu)一樣”。發(fā)現(xiàn)代碼有什么問題就針對什么情況進(jìn)行改進(jìn)。

重構(gòu)也是寫代碼,但是不止于寫,更在于整理和優(yōu)化。如果說寫代碼需要一個(gè)‘學(xué)習(xí)--了解-熟練’的過程,那么重構(gòu)就需要一個(gè)‘學(xué)習(xí)-感悟-突破-熟練’的過程。

針對重構(gòu)的情況,下面簡單的用幾個(gè)例子進(jìn)行說明

5-1.函數(shù)無擴(kuò)展性

如下面一個(gè)例子,在我一個(gè)庫的其中一個(gè) API

 

  1. //檢測字符串  
  2. //checkType('165226226326','mobile' 
  3. //result:false  
  4. let checkType=function(str, type) {  
  5.     switch (type) {  
  6.         case 'email' 
  7.             return /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(str);  
  8.         case 'mobile' 
  9.             return /^1[3|4|5|7|8][0-9]{9}$/.test(str);  
  10.         case 'tel' 
  11.             return /^(0\d{2,3}-\d{7,8})(-\d{1,4})?$/.test(str);  
  12.         case 'number' 
  13.             return /^[0-9]$/.test(str);  
  14.         case 'english' 
  15.             return /^[a-zA-Z]+$/.test(str);  
  16.         case 'text' 
  17.             return /^\w+$/.test(str);  
  18.         case 'chinese' 
  19.             return /^[\u4E00-\u9FA5]+$/.test(str);  
  20.         case 'lower' 
  21.             return /^[a-z]+$/.test(str);  
  22.         case 'upper'
  23.             return /^[A-Z]+$/.test(str);  
  24.         default 
  25.             return true 
  26.     }  

這個(gè) API 看著沒什么毛病,能檢測常用的一些數(shù)據(jù)。但是有以下兩個(gè)問題。

  1. 但是如果想到添加其他規(guī)則的呢?就得在函數(shù)里面增加 case 。添加一個(gè)規(guī)則就修改一次!這樣違反了開放-封閉原則(對擴(kuò)展開放,對修改關(guān)閉)。而且這樣也會導(dǎo)致整個(gè) API 變得臃腫,難維護(hù)。
  2. 還有一個(gè)問題就是,比如A頁面需要添加一個(gè)金額的校驗(yàn),B頁面需要一個(gè)日期的校驗(yàn),但是金額的校驗(yàn)只在A頁面需要,日期的校驗(yàn)只在B頁面需要。如果一直添加 case 。就是導(dǎo)致A頁面把只在B頁面需要的校驗(yàn)規(guī)則也添加進(jìn)去,造成不必要的開銷。B頁面也同理。

建議的方式是給這個(gè) API 增加一個(gè)擴(kuò)展的接口

 

  1. let checkType=(function(){  
  2.     let rules={  
  3.         email(str){  
  4.             return /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(str);  
  5.         },  
  6.         mobile(str){  
  7.             return /^1[3|4|5|7|8][0-9]{9}$/.test(str);  
  8.         },  
  9.         tel(str){  
  10.             return /^(0\d{2,3}-\d{7,8})(-\d{1,4})?$/.test(str);  
  11.         }, 
  12.         number(str){  
  13.             return /^[0-9]$/.test(str);  
  14.         },  
  15.         english(str){  
  16.             return /^[a-zA-Z]+$/.test(str);  
  17.         },  
  18.         text(str){  
  19.             return /^\w+$/.test(str);  
  20.         },  
  21.         chinese(str){  
  22.             return /^[\u4E00-\u9FA5]+$/.test(str);  
  23.         },  
  24.         lower(str){  
  25.             return /^[a-z]+$/.test(str);  
  26.         },  
  27.         upper(str){  
  28.             return /^[A-Z]+$/.test(str);  
  29.         }  
  30.     };  
  31.     //暴露接口  
  32.     return {  
  33.         //校驗(yàn)  
  34.         check(str, type){  
  35.             return rules[type]?rules[type](str):false 
  36.         },  
  37.         //添加規(guī)則  
  38.         addRule(type,fn){  
  39.             rules[type]=fn;  
  40.         }  
  41.     }  
  42. })();  
  43.  
  44. //調(diào)用方式  
  45. //使用mobile校驗(yàn)規(guī)則  
  46. console.log(checkType.check('188170239','mobile'));  
  47. //添加金額校驗(yàn)規(guī)則  
  48. checkType.addRule('money',function (str) {  
  49.     return /^[0-9]+(.[0-9]{2})?$/.test(str)  
  50. });  
  51. //使用金額校驗(yàn)規(guī)則  
  52. console.log(checkType.check('18.36','money')); 

上面的代碼,是多了一些,但是理解起來也沒怎么費(fèi)勁,而且拓展性也有了。

上面這個(gè)改進(jìn)其實(shí)是使用了策略模式(把一系列的算法進(jìn)行封裝,使算法代碼和邏輯代碼可以相互獨(dú)立,并且不會影響算法的使用)進(jìn)行改進(jìn)的。策略模式的概念理解起來有點(diǎn)繞,但是大家看著代碼,應(yīng)該不繞。

這里展開講一點(diǎn),在功能上來說,通過重構(gòu),給函數(shù)增加擴(kuò)展性,這里實(shí)現(xiàn)了。但是如果上面的 checkType是一個(gè)開源項(xiàng)目的 API ,重構(gòu)之前調(diào)用方式是:checkType('165226226326','phone') 。重構(gòu)之后調(diào)用方式是: checkType.check('188170239','phone') ;或者 checkType.addRule() ;。如果開源項(xiàng)目的作者按照上面的方式重構(gòu),那么之前使用了開源項(xiàng)目的 checkType 這個(gè) API 的開發(fā)者,就可能悲劇了,因?yàn)橹灰_發(fā)者一更新這個(gè)項(xiàng)目版本,就有問題。因?yàn)樯厦娴闹貥?gòu)沒有做向下兼容。

如果要向下兼容,其實(shí)也不難。加一個(gè)判斷而已。

 

  1. let checkType=(function(){  
  2.     let rules={  
  3.         email(str){  
  4.             return /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(str);  
  5.         },  
  6.         mobile(str){  
  7.             return /^1[3|4|5|7|8][0-9]{9}$/.test(str);  
  8.         },  
  9.         tel(str){  
  10.             return /^(0\d{2,3}-\d{7,8})(-\d{1,4})?$/.test(str);  
  11.         },  
  12.         number(str){  
  13.             return /^[0-9]$/.test(str);  
  14.         },  
  15.         english(str){  
  16.             return /^[a-zA-Z]+$/.test(str);  
  17.         },  
  18.         text(str){  
  19.             return /^\w+$/.test(str);  
  20.         },  
  21.         chinese(str){  
  22.             return /^[\u4E00-\u9FA5]+$/.test(str);  
  23.         },  
  24.         lower(str){  
  25.             return /^[a-z]+$/.test(str);  
  26.         },  
  27.         upper(str){  
  28.             return /^[A-Z]+$/.test(str);  
  29.         }  
  30.     };  
  31.     //暴露接口  
  32.     return function (str,type){  
  33.         //如果type是函數(shù),就擴(kuò)展rules,否則就是驗(yàn)證數(shù)據(jù)  
  34.         if(type.constructor===Function){  
  35.             rules[str]=type;  
  36.         }  
  37.         else 
  38.             return rules[type]?rules[type](str):false 
  39.         }  
  40.     }  
  41. })();  
  42.  
  43. console.log(checkType('188170239','mobile'));  
  44.  
  45. checkType('money',function (str) {  
  46.     return /^[0-9]+(.[0-9]{2})?$/.test(str)  
  47. });  
  48. //使用金額校驗(yàn)規(guī)則  
  49. console.log(checkType('18.36','money')); 

這樣運(yùn)行能正常,也有擴(kuò)展性性,但是對于代碼潔癖的來說,這樣寫法不優(yōu)雅。因?yàn)?checkType 違反了函數(shù)單一原則。一個(gè)函數(shù)負(fù)責(zé)過多的職責(zé)可能會導(dǎo)致以后不可估量的問題,使用方面也很讓人疑惑。

面對這樣的情況,就個(gè)人而言,了解的做法是:保留 checkType ,不做任何修改,在項(xiàng)目里面增加一個(gè)新的 API ,比如 checkTypOfString ,把重構(gòu)的代碼寫到 checkTypOfString 里面。通過各種方式引導(dǎo)開發(fā)者少舊 checkType ,多用 checkTypOfString 。之后的項(xiàng)目迭代里面,合適的時(shí)候廢棄 checkType 。

5-2.函數(shù)違反單一原則

函數(shù)違反單一原則***一個(gè)后果就是會導(dǎo)致邏輯混亂。如果一個(gè)函數(shù)承擔(dān)了太多的職責(zé),不妨試下:函數(shù)單一原則 -- 一個(gè)函數(shù)只做一件事。

如下例子

 

  1. //現(xiàn)有一批的錄入學(xué)生信息,但是數(shù)據(jù)有重復(fù),需要把數(shù)據(jù)進(jìn)行去重。然后把為空的信息,改成保密。  
  2. let students=[  
  3.     {  
  4.         id:1,  
  5.         name:'守候' 
  6.         sex:'男' 
  7.         age:'' 
  8.     },  
  9.     {  
  10.         id:2,  
  11.         name:'浪跡天涯' 
  12.         sex:'男' 
  13.         age:''  
  14.     },  
  15.     {  
  16.         id:1,  
  17.         name:'守候' 
  18.         sex:'' 
  19.         age:''  
  20.     },  
  21.     {  
  22.         id:3,  
  23.         name:'鴻雁' 
  24.         sex:'' 
  25.         age:'20'  
  26.     }  
  27. ];  
  28.  
  29. function handle(arr) {  
  30.     //數(shù)組去重  
  31.     let _arr=[],_arrIds=[];  
  32.     for(let i=0;i<arr.length;i++){  
  33.         if(_arrIds.indexOf(arr[i].id)===-1){  
  34.             _arrIds.push(arr[i].id);  
  35.             _arr.push(arr[i]);  
  36.         }  
  37.     }  
  38.     //遍歷替換  
  39.     _arr.map(item=>{  
  40.         for(let key in item){  
  41.             if(item[key]===''){  
  42.                 item[key]='保密' 
  43.             }  
  44.         }  
  45.     });  
  46.     return _arr;  
  47.  
  48. console.log(handle(students)) 

運(yùn)行結(jié)果沒有問題,但是大家想一下,如果以后,如果改了需求,比如,學(xué)生信息不會再有重復(fù)的記錄,要求把去重的函數(shù)去掉。這樣一來,就是整個(gè)函數(shù)都要改了。還影響到下面的操作流程。相當(dāng)于了改了需求,整個(gè)方法全跪。城門失火殃及池魚。

下面使用單一原則構(gòu)造一下

 

  1. let handle={  
  2.     removeRepeat(arr){  
  3.         //數(shù)組去重  
  4.         let _arr=[],_arrIds=[];  
  5.         for(let i=0;i<arr.length;i++){  
  6.             if(_arrIds.indexOf(arr[i].id)===-1){  
  7.                 _arrIds.push(arr[i].id);  
  8.                 _arr.push(arr[i]);  
  9.             }  
  10.         }  
  11.         return _arr;  
  12.     },  
  13.     setInfo(arr){  
  14.         arr.map(item=>{  
  15.             for(let key in item){  
  16.                 if(item[key]===''){  
  17.                     item[key]='保密' 
  18.                 }  
  19.             }  
  20.         });  
  21.         return arr;  
  22.     }  
  23. };  
  24. students=handle.removeRepeat(students);  
  25. students=handle.setInfo(students);  
  26. console.log(students); 

結(jié)果一樣,但是需求改下,比如不需要去重,把代碼注釋或者直接刪除就好。這樣相當(dāng)于把函數(shù)的職責(zé)分離了,而且職責(zé)之前互不影響。中間去除那個(gè)步驟不會影響下一步。

 

  1. //students=handle.removeRepeat(students);  
  2. students=handle.setInfo(students);  
  3. console.log(students); 

5-3.函數(shù)寫法優(yōu)化

這種情況就是,對于以前的函數(shù),在不影響使用的情況下,現(xiàn)在有著更好的實(shí)現(xiàn)方式。就使用更好的解決方案,替換以前的解決方案。

比如下面的需求,需求是群里一個(gè)朋友發(fā)出來的,后來引發(fā)的一些討論。給出一個(gè)20180408000000字符串,formatDate函數(shù)要處理并返回2018-04-08 00:00:00。

以前的解法

 

  1. let _dete='20180408000000'  
  2. function formatStr(str){  
  3.     return str.replace(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/, "$1-$2-$3 $4:$5:$6" 
  4.  
  5. formatStr(_dete);  
  6. //"2018-04-08 00:00:00" 

后來研究了這樣的解法。這個(gè)方式就是根據(jù)x的位置進(jìn)行替換填充數(shù)據(jù),不難理解

 

  1. let _dete='20180408000000'  
  2. function formatStr(str,type){  
  3.     let _type=type||"xxxx-xx-xx xx:xx:xx" 
  4.     for(let i = 0; i < str.length; i++){  
  5.         _type = _type.replace('x', str[i]);  
  6.     }  
  7.     return _type;  
  8.  
  9. formatStr(_dete);  
  10. result:"2018-04-08 00:00:00" 

在之后的幾天,在掘金一篇文章(那些優(yōu)雅靈性的JS代碼片段,感謝提供的寶貴方式)的評論里面發(fā)現(xiàn)更好的實(shí)現(xiàn)方式,下面根據(jù)上面的需求自己進(jìn)行改造。

 

  1. let _dete='20180408000000'  
  2. function formatStr(str,type){  
  3.     let i = 0,_type = type||"xxxx-xx-xx xx:xx:xx" 
  4.     return _type .replace(/x/g, () => str[i++])  
  5.  
  6. formatStr(_dete);  
  7. result:"2018-04-08 00:00:00" 

5-4.代碼復(fù)用

上面幾個(gè)例子都是js的,說下與html沾邊一點(diǎn)的兩個(gè)例子--vue數(shù)據(jù)渲染。

    下面代碼中,payChannelEn2Cn addZero formatDateTime函數(shù)都是在vue的methods里面。大家注意。

以前寫法

 

  1. <span v-if="cashType==='cash'">現(xiàn)金</span>  
  2. <span v-else-if="cashType==='check'">支票</span>  
  3. <span v-else-if="cashType==='draft'">匯票</span>  
  4. <span v-else-if="cashType==='zfb'">支付寶</span>  
  5. <span v-else-if="cashType==='wx_pay'">微信支付</span>  
  6. <span v-else-if="cashType==='bank_trans'">銀行轉(zhuǎn)賬</span>  
  7. <span v-else-if="cashType==='pre_pay'">預(yù)付款</span> 

這樣寫的問題在于,首先是代碼多,第二是如果項(xiàng)目有10個(gè)地方這樣渲染數(shù)據(jù),如果渲染的需求變了。比如銀行轉(zhuǎn)賬的值從 bank_trans 改成 bank ,那么就得在項(xiàng)目里面修改10次。時(shí)間成本太大。

后來就使用了下面的寫法,算是一個(gè)小重構(gòu)吧

 

  1. <span>{{payChannelEn2Cn(cashType)}}</span> 

payChannelEn2Cn 函數(shù),輸出結(jié)果

 

  1. payChannelEn2Cn(tag){  
  2.     let _obj = {  
  3.         'cash''現(xiàn)金' 
  4.         'check''支票' 
  5.         'draft''匯票' 
  6.         'zfb''支付寶' 
  7.         'wx_pay''微信支付' 
  8.         'bank_trans''銀行轉(zhuǎn)賬' 
  9.         'pre_pay''預(yù)付款'  
  10.     };  
  11.     return _obj[tag];  

還有一個(gè)例子就是時(shí)間戳轉(zhuǎn)時(shí)間的寫法。原理一樣,只是代碼不同。下面是原來的代碼。

 

  1. <span>{{new Date(payTime).toLocaleDateString().replace(/\//g, '-')}}   
  2. {{addZero(new Date(payTime).getHours())}}:  
  3. {{addZero(new Date(payTime).getMinutes())}}:  
  4. {{addZero(new Date(payTime).getSeconds())}}</span> 

addZero時(shí)間補(bǔ)零函數(shù)

 

  1. Example:3->03  
  2. addZero(i){  
  3.     if (i < 10) {  
  4.         i = "0" + i;  
  5.     }  
  6.     return i;  

問題也和上面的一樣,這里就不多說了,就寫重構(gòu)后的代碼

 

  1. <span>{{formatDateTime(payTime)}} </span> 

formatDateTime函數(shù),格式化字符串

 

  1. formatDateTime(dateTime){  
  2.     return `${new Date(payTime).toLocaleDateString().replace(/\//g, '-')} ${this.addZero(new Date(payTime).getHours())}:${this.addZero(new Date(payTime).getMinutes())}:${this.addZero(new Date(payTime).getSeconds())}`;  

 

可能很多人看到這里,覺得重構(gòu)很簡單,這樣想是對的,重構(gòu)就是這么簡單。但是重構(gòu)也難,因?yàn)橹貥?gòu)一步登天,需要一個(gè)逐步的過程,甚至可以說重構(gòu)就是一次次的小改動(dòng),逐步形成一個(gè)質(zhì)變的過程。如何保證每一次的改動(dòng)都是有意義的改善代碼;如何保證每一次的改動(dòng)都不會影響到項(xiàng)目的正常使用;如果發(fā)現(xiàn)某次改動(dòng)沒有意義,或者改動(dòng)了反而讓代碼更糟糕的時(shí)候,可以隨時(shí)停止或回滾代碼,這些才是重構(gòu)的難點(diǎn)。

6.小結(jié)

 

關(guān)于重構(gòu)就說到這里了,該文章主要是介紹重構(gòu),例子方面都是很簡單的一些例子。目的是為了好些理解重構(gòu)的一些概念。關(guān)于重構(gòu),可能很復(fù)雜,可能很簡單。怎么重構(gòu)也是具體情況,具體分析,重構(gòu)也沒有標(biāo)準(zhǔn)的答案。以后,如果有好的例子,我會***時(shí)間分享,給大家具體情況,具體分析的講述:為什么重構(gòu),怎么重構(gòu)。 

責(zé)任編輯:龐桂玉 來源: segmentfault
相關(guān)推薦

2010-07-09 14:46:56

2011-12-12 10:06:14

解析網(wǎng)站

2023-08-27 14:48:19

開源辦公套件應(yīng)用程序

2013-05-13 09:36:28

ClouderaImpala 1.0Hadoop

2009-10-16 10:44:17

2014-10-27 09:51:19

Web設(shè)計(jì)HTML

2021-08-03 08:13:48

重構(gòu)API代碼

2018-11-26 08:23:36

物聯(lián)網(wǎng)客戶體驗(yàn)IOT

2019-09-12 08:00:00

Visual Stud軟件開發(fā)

2021-05-31 19:04:50

低代碼平臺低代碼開發(fā)

2024-09-05 10:17:34

2024-12-20 08:00:00

2010-10-20 09:25:49

網(wǎng)絡(luò)成本

2012-02-23 00:22:55

2012-07-27 10:30:12

重構(gòu)

2010-01-23 20:57:48

2011-08-16 09:47:58

編程

2019-02-18 16:21:47

華為代碼重構(gòu)

2025-03-25 09:12:00

LIMAI模型

2022-07-25 15:21:50

Java編程語言開發(fā)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號