摘要:高內(nèi)聚不應(yīng)該將沒(méi)有任何聯(lián)系的東西堆到一起。高內(nèi)聚是值得要的,因?yàn)樗馕吨惪梢愿玫貓?zhí)行一項(xiàng)工作。高內(nèi)聚有助于緩解高耦合,高耦合是需要高內(nèi)聚的標(biāo)志。對(duì)于開(kāi)發(fā)者來(lái)說(shuō),高內(nèi)聚通常比低耦合更有幫助,盡管兩者通??梢砸黄鹜瓿?。
作為一個(gè)剛寫代碼不久的小菜鳥(niǎo),工作的半年多讓我越發(fā)意識(shí)到提高代碼質(zhì)量的重要性。從前只會(huì)關(guān)注實(shí)現(xiàn)功能,慢慢的開(kāi)始關(guān)注性能,現(xiàn)階段則發(fā)現(xiàn)其實(shí)還有很多細(xì)節(jié)也是(如可讀性、易用性、可維護(hù)性、一致性)提高代碼質(zhì)量的關(guān)鍵?!皩?shí)現(xiàn)功能”跟“優(yōu)雅地實(shí)現(xiàn)功能”是兩碼事。
注:大部分內(nèi)容歸納自網(wǎng)絡(luò),將多篇文章的觀點(diǎn)匯總加工了一下,也融合了一些個(gè)人的見(jiàn)解。原則
單一職責(zé)原則
易用性原則
可讀性原則
復(fù)雜性守恒原則:無(wú)論你怎么寫代碼,復(fù)雜性都是不會(huì)消失的
注:如果邏輯很復(fù)雜,那么代碼看起來(lái)就應(yīng)該是復(fù)雜的。如果邏輯很簡(jiǎn)單,代碼看起來(lái)就應(yīng)該是簡(jiǎn)單的。單一職責(zé)原則
面向?qū)ο笪宕笤O(shè)計(jì)模式基本原則之一。即一部分代碼只應(yīng)該用于某一個(gè)特定功能,不應(yīng)與其他功能耦合在一起。
假設(shè)你的一個(gè)function同時(shí)實(shí)現(xiàn)了功能a和功能b,之后需求變更,你需要修改功能a,但是因?yàn)檫@兩個(gè)功能都在一個(gè)function里,你就不得不再去確認(rèn)是否會(huì)影響到功能b。這就造成了不必要的成本。
如下我總結(jié)了三個(gè)拆分代碼的原則:
“and”原則當(dāng)你為你的方法命名時(shí)不得不加上“and”時(shí),就該考慮考慮是不是要把這個(gè)方法拆分一下了。
“100”原則當(dāng)你的一個(gè)function超過(guò)一百行時(shí),一定要進(jìn)行拆分了。
注:這里的100可能有點(diǎn)多,只是對(duì)我個(gè)人而言,100算是我的極限,總之就是絕對(duì)不要將一個(gè)函數(shù)寫的太長(zhǎng)。命令、查詢拆分原則
我們開(kāi)發(fā)中大部分操作可以總結(jié)為“命令”和“查詢”,如寫cookie、修改data、發(fā)送post請(qǐng)求都可以叫“命令”,而讀取cookie、ajax獲取數(shù)據(jù)則認(rèn)為是“查詢”操作。
函數(shù)式編程中講究“數(shù)據(jù)不可變”,即:
只有純的沒(méi)有副作用的函數(shù),才是合格的函數(shù)。副作用:指當(dāng)調(diào)用函數(shù)時(shí),除了返回函數(shù)值之外,還對(duì)主調(diào)用函數(shù)產(chǎn)生附加的影響。例如修改全局變量(函數(shù)外的變量)或修改參數(shù)。
好處是使得開(kāi)發(fā)更加簡(jiǎn)單,可回溯,測(cè)試友好,減少了任何可能的副作用。
將“命令”與“查詢”拆分實(shí)際上就是函數(shù)式編程思想的部分體現(xiàn),參考如下代碼:
function getFirstName() { var firstName = document.querySelector("#firstName").value; firstName = firstName.toLowerCase(); setCookie("firstName", firstName); if (firstName === null) { return ""; } return firstName; } var activeFirstName = getFirstName();
通過(guò)名字來(lái)看,該方法是用于獲取first name的,但實(shí)際上它還設(shè)置了cookie,這是我們沒(méi)有預(yù)料的。對(duì)于一個(gè)“查詢”方法,它不應(yīng)該有任何修改方法外變量的行為,即“副作用”。更好的寫法如下:
function getFirstName() { var firstName = document.querySelector("#firstName").value if (firstName === null) { return ""; } return firstName; } setCookie("firstName", getFirstName().toLowerCase());
一目了然,getFirstName只返回firstName,而設(shè)置cookie操作在它之外進(jìn)行。
易用性原則 簡(jiǎn)單這里的簡(jiǎn)單,主要?dú)w結(jié)為function的一些設(shè)計(jì)原則,有如下幾點(diǎn):調(diào)用簡(jiǎn)單、易理解、減少記憶成本、參數(shù)處理。
如下,僅僅想實(shí)現(xiàn)修改dom顏色、寬度等屬性,原生代碼如下:
document.querySelector("#id").style.color = "red" document.querySelector("#id").style.width = "123px" document.querySelector("#id").style.height = "456px"
而封裝過(guò)后:
function a(selector, color, width, height) { document.querySelector(selector).style.color = color document.querySelector(selector).style.width = width document.querySelector(selector).style.height = height } a("#a", "red")
瞬間變得簡(jiǎn)單可用了 ~
但該方法還存在一個(gè)問(wèn)題,那就是命名太抽象了。。。除了開(kāi)發(fā)者自己以外不可能有人在不看源碼的情況下一眼看出這個(gè)方法a是干嘛的。那么咱再把這個(gè)方法名改寫得更易理解一點(diǎn):
function letSomeElementChange(selector, color, width, height) { document.querySelector(selector).style.color = color document.querySelector(selector).style.width = width document.querySelector(selector).style.height = height }
這樣我們就能一目了然該方法的作用 ~ 不過(guò)仍有可優(yōu)化的地方。這么長(zhǎng)的方法名誰(shuí)記得住,要減少記憶成本啊,再改個(gè)名:
function setElement(selector, color, width, height) { document.querySelector(selector).style.color = color document.querySelector(selector).style.width = width document.querySelector(selector).style.height = height }
OK,目前這個(gè)方法已經(jīng)滿足它的職責(zé)并且很好用了,但還覺(jué)得怪怪的。這一坨參數(shù)太礙眼。。。
function setElement(selector, opt) { const { color, width, height } = opt color && document.querySelector(selector).style.color = color width && document.querySelector(selector).style.width = width height && document.querySelector(selector).style.height = height }
把多個(gè)參數(shù)合并一下,并在內(nèi)部做兼容處理,這個(gè)方法便易用多了。即使不傳第二個(gè)參數(shù)也不會(huì)有任何副作用。
一致性假如有這樣一個(gè)方法,獲取歌曲列表,并將其設(shè)置到div的innerText中:
function getSongs() { return $.get("/songs).then((response) { div.innerText = response.songs }) }
這就違背了方法的表里一致性,也違背了上文的單一職責(zé)原則中命令、查詢拆分原則,因?yàn)樗粌H獲取了歌單,同時(shí)還修改了innerText,要讓其更合理:
要么換個(gè)名字
要么拆分為兩個(gè)方法
低耦合耦合是衡量一個(gè)程序單元對(duì)其他程序單元的依賴程度。耦合(或高耦合)是應(yīng)該極力避免的。如果你發(fā)現(xiàn)自己正在復(fù)制和粘貼代碼并進(jìn)行小的更改,或者重寫代碼,因?yàn)槠渌胤桨l(fā)生了更改,這就是高耦合的體現(xiàn)。
耦合會(huì)嚴(yán)重影響代碼的復(fù)用性及可擴(kuò)展性,讓后人維護(hù)時(shí)不得不修改甚至重寫這部分代碼,不僅浪費(fèi)時(shí)間還會(huì)導(dǎo)致倉(cāng)儲(chǔ)中又多出一塊類似的代碼,很容易讓人迷惑。
同時(shí),修改耦合度高的代碼時(shí)經(jīng)常會(huì)牽一發(fā)而動(dòng)全身,如果修改時(shí)沒(méi)有理清這些耦合關(guān)系,那么帶來(lái)的后果可能會(huì)是災(zāi)難性的,特別是對(duì)于需求變化較多以及多人協(xié)作開(kāi)發(fā)維護(hù)的項(xiàng)目,修改一個(gè)地方會(huì)引起本來(lái)已經(jīng)運(yùn)行穩(wěn)定的模塊錯(cuò)誤,嚴(yán)重時(shí)會(huì)導(dǎo)致惡性循環(huán),問(wèn)題永遠(yuǎn)改不完,開(kāi)發(fā)和測(cè)試都在各種問(wèn)題之間奔波勞累,最后導(dǎo)致項(xiàng)目延期,用戶滿意度降低,成本也增加了,這對(duì)用戶和開(kāi)發(fā)商影響都是很惡劣的,各種風(fēng)險(xiǎn)也就不言而喻了。
高內(nèi)聚不應(yīng)該將沒(méi)有任何聯(lián)系的東西堆到一起。
內(nèi)聚是一個(gè)類中變量與方法連接強(qiáng)度的尺度。高內(nèi)聚是值得要的,因?yàn)樗馕吨惪梢愿玫貓?zhí)行一項(xiàng)工作。低內(nèi)聚是不好的,因?yàn)樗砻黝愔械脑刂g很少相關(guān)。每個(gè)方法也應(yīng)該高內(nèi)聚,大多數(shù)的方法只執(zhí)行一個(gè)功能,不要在方法中添加‘額外’的指令,這樣會(huì)導(dǎo)致方法執(zhí)行更多的函數(shù),同時(shí)也違反了上文的單一職責(zé)原則。
低內(nèi)聚的體現(xiàn):如果屬性沒(méi)有被類中的多個(gè)方法使用,這可能是低內(nèi)聚的標(biāo)志。同樣,如果方法在幾種不同的情況下不能被重用,或者如果一個(gè)方法根本不被使用,這也可能是低內(nèi)聚的一個(gè)標(biāo)志。
高內(nèi)聚有助于緩解高耦合,高耦合是需要高內(nèi)聚的標(biāo)志。但是,如果兩個(gè)問(wèn)題同時(shí)存在,應(yīng)當(dāng)選擇內(nèi)聚的方式。對(duì)于開(kāi)發(fā)者來(lái)說(shuō),高內(nèi)聚通常比低耦合更有幫助,盡管兩者通??梢砸黄鹜瓿?。
錯(cuò)誤處理可預(yù)見(jiàn)的錯(cuò)誤:諸如ajax回調(diào)、函數(shù)參數(shù),這類問(wèn)題很好解決,只需在開(kāi)發(fā)時(shí)多考慮一步,對(duì)各種極端情況做好兼容即可。
不可預(yù)見(jiàn)的錯(cuò)誤:類似兼容性問(wèn)題,這類問(wèn)題無(wú)法在開(kāi)發(fā)時(shí)準(zhǔn)確預(yù)見(jiàn)的錯(cuò)誤,可以準(zhǔn)備好拋錯(cuò),console.error/log/warn,最后你還可以為自己的程序留些后路: try...catch。
可讀性原則 命名命名應(yīng)該保證別人通過(guò)名稱一眼就能知道這個(gè)變量保存的是什么,或者這個(gè)方法是用來(lái)做什么的。
普通變量、屬性用名詞如下:
var person = { name: "Frank" } var student = { grade: 3, class: 2 }
bool變量、屬性用(形容詞)或者(be動(dòng)詞)或者(情態(tài)動(dòng)詞)或者(hasX),如下:
var person = { dead: false, // 如果是形容詞,前面就沒(méi)必要加 is,比如isDead 就很廢話 canSpeak: true, //情態(tài)動(dòng)詞有 can、should、will、need 等,情態(tài)動(dòng)詞后面接動(dòng)詞 isVip: true, // be 動(dòng)詞有 is、was 等,后面一般接名詞 hasChildren: true, // has 加名詞 }
普通函數(shù)、方法用(動(dòng)詞)開(kāi)頭:
var person = { run(){}, // 不及物動(dòng)詞 drinkWater(){}, // 及物動(dòng)詞 eat(foo){}, // 及物動(dòng)詞加參數(shù)(參數(shù)是名詞) }
回調(diào)、鉤子函數(shù):
var person = { beforeDie(){}, afterDie(){}, // 或者 willDie(){} dead(){} // 這里跟 bool 沖突,你只要不同時(shí)暴露 bool dead 和函數(shù) dead 就行,怕沖突就用上面的 afterDie } button.addEventListener("click", onButtonClick) var component = { beforeCreate(){}, created(){}, beforeMount(){} }
命名一致性
順序一致性:比如 updateContainerWidth 和 updateHeightOfContainer 的順序就令人很別扭
時(shí)間一致性:有可能隨著代碼的變遷,一個(gè)變量的含義已經(jīng)不同于它一開(kāi)始的含義了,這個(gè)時(shí)候你需要及時(shí)改掉這個(gè)變量的名字。
這一條是最難做到的,因?yàn)閷懘a容易,改代碼難。如果這個(gè)代碼組織得不好,很可能會(huì)出現(xiàn)牽一發(fā)而動(dòng)全身的情況(如全局變量就很難改)
注釋不需要多花哨,只要把作用、用法描述清楚即可。方法的標(biāo)準(zhǔn)注釋應(yīng)該如下:
/** * [function_name description] * @param {[type]} argument [description] * @return {[type]} [description] */ function function_name(argument) { // body... }
將方法的參數(shù)與返回值都寫清楚,我目前用的IDE是sublime,使用Docblockr插件可以自動(dòng)生成格式化注釋,很方便。
Bad Smell項(xiàng)目中我們經(jīng)常能夠遇這類代碼,它們?nèi)钥捎?,但是很“臭”,?guó)外管這類代碼有一個(gè)統(tǒng)稱,即“bad smell”。如下這類代碼可以說(shuō)是很“臭”了:
表里不一的代碼
過(guò)時(shí)的注釋
邏輯很簡(jiǎn)單,但是看起來(lái)很復(fù)雜的代碼
重復(fù)的代碼
相似的代碼
總是一起出現(xiàn)的代碼
未使用的依賴
不同風(fēng)格的代碼
樣式規(guī)范正確命名:class必須用“-”寫法,不要用駝峰和下劃線。
正確嵌套:正常情況下一定要將class嵌套閉合,否則就相當(dāng)于添加到全局,如果有重復(fù)命名的class就會(huì)受影響。
拒絕copy:如果想復(fù)用已有的樣式,直接在原有class上用“,”語(yǔ)法分割,就能應(yīng)用,不要再copy一份樣式,會(huì)讓兩份樣式都被應(yīng)用,就要考慮樣式覆蓋的問(wèn)題,很不友好。
濫用class:沒(méi)有必要加的class不要加,每個(gè)class的添加都應(yīng)該有明確理由。濫用class的話可能會(huì)導(dǎo)致樣式覆蓋,不該應(yīng)用這個(gè)樣式的地方用了這個(gè)樣式。
慎用 !important,會(huì)強(qiáng)行覆蓋所有同屬性樣式,一旦使用后會(huì)讓代碼難以維護(hù),開(kāi)發(fā)過(guò)程中絕對(duì)不要依賴該方法。如下總結(jié)了一些使用 !important的經(jīng)驗(yàn):
一定要優(yōu)化考慮使用樣式規(guī)則的優(yōu)先級(jí)來(lái)解決問(wèn)題而不是 !important
只有在需要覆蓋全站或外部 css(例如引用的 ExtJs 或者 YUI )的特定頁(yè)面中使用 !important
解決緊急線上問(wèn)題可以使用,但之后也要盡快用可維護(hù)的方式將代碼替換回來(lái)
永遠(yuǎn)不要在全站范圍的 css 上使用 !important
永遠(yuǎn)不要在你的插件中使用 !important
說(shuō)得容易,做起來(lái)難 破窗效應(yīng)此理論認(rèn)為環(huán)境中的不良現(xiàn)象如果被放任存在,會(huì)誘使人們仿效,甚至變本加厲。一幢有少許破窗的建筑為例,如果那些窗不被修理好,可能將會(huì)有破壞者破壞更多的窗戶。最終他們甚至?xí)J入建筑內(nèi),如果發(fā)現(xiàn)無(wú)人居住,也許就在那里定居或者縱火。一面墻,如果出現(xiàn)一些涂鴉沒(méi)有被清洗掉,很快的,墻上就布滿了亂七八糟、不堪入目的東西;一條人行道有些許紙屑,不久后就會(huì)有更多垃圾,最終人們會(huì)視若理所當(dāng)然地將垃圾順手丟棄在地上。這個(gè)現(xiàn)象,就是犯罪心理學(xué)中的破窗效應(yīng),在編程領(lǐng)域同樣存在。
要做到:只要是經(jīng)過(guò)你手的代碼,都會(huì)比之前好一點(diǎn)。
參考文章:
javascript的api設(shè)計(jì)原則
Coding with Clarity
Don"t leave broken windows
從JS對(duì)象開(kāi)始,談一談“不可變數(shù)據(jù)”和函數(shù)式編程
重構(gòu) - 代碼優(yōu)化技巧
如何和何時(shí)使用CSS的!important
css優(yōu)先級(jí)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/115717.html
摘要:高內(nèi)聚不應(yīng)該將沒(méi)有任何聯(lián)系的東西堆到一起。高內(nèi)聚是值得要的,因?yàn)樗馕吨惪梢愿玫貓?zhí)行一項(xiàng)工作。高內(nèi)聚有助于緩解高耦合,高耦合是需要高內(nèi)聚的標(biāo)志。對(duì)于開(kāi)發(fā)者來(lái)說(shuō),高內(nèi)聚通常比低耦合更有幫助,盡管兩者通??梢砸黄鹜瓿伞? 作為一個(gè)剛寫代碼不久的小菜鳥(niǎo),工作的半年多讓我越發(fā)意識(shí)到提高代碼質(zhì)量的重要性。從前只會(huì)關(guān)注實(shí)現(xiàn)功能,慢慢的開(kāi)始關(guān)注性能,現(xiàn)階段則發(fā)現(xiàn)其實(shí)還有很多細(xì)節(jié)也是(如可讀性、易用...
摘要:高內(nèi)聚低耦合高內(nèi)聚低耦合一直是軟件設(shè)計(jì)領(lǐng)域里亙古不變的話題,重構(gòu)的目標(biāo)是提高代碼的內(nèi)聚性,降低各功能間的耦合程度,降低后期維護(hù)成本,特別是寫業(yè)務(wù)代碼,這一點(diǎn)相當(dāng)重要。0x00 前言 我是一名來(lái)自螞蟻金服-保險(xiǎn)事業(yè)群的前端工程師,在一線大廠的業(yè)務(wù)部門寫代碼,非常辛苦但也非常充實(shí)。業(yè)務(wù)代碼不同于框架代碼、個(gè)人項(xiàng)目或者開(kāi)源項(xiàng)目,它的特點(diǎn)在于邏輯復(fù)雜、前后依賴多、可復(fù)用性差、迭代周期短,今天辛辛苦苦...
摘要:前言同樣一個(gè)功能,可以用很多方法來(lái)實(shí)現(xiàn),有時(shí)候由于項(xiàng)目時(shí)間緊張,導(dǎo)致很多時(shí)候只是實(shí)現(xiàn)了功能,往往忽視了代碼質(zhì)量。下面幾種代碼重構(gòu)方法,以便大家可以寫出更優(yōu)雅的代碼。 showImg(https://segmentfault.com/img/bVUPmT?w=275&h=183); 前言 同樣一個(gè)功能,可以用很多方法來(lái)實(shí)現(xiàn),有時(shí)候由于項(xiàng)目時(shí)間緊張,導(dǎo)致很多時(shí)候只是實(shí)現(xiàn)了功能,往往忽視了代...
摘要:導(dǎo)讀要從容器化開(kāi)始,而容器又需要從開(kāi)始,本文將介紹如何寫出一個(gè)優(yōu)雅的文件。只要記住以上三點(diǎn)就能寫出不錯(cuò)的。執(zhí)行完成項(xiàng)目的構(gòu)建。 導(dǎo)讀 Kubernetes要從容器化開(kāi)始,而容器又需要從Dockerfile開(kāi)始,本文將介紹如何寫出一個(gè)優(yōu)雅的Dockerfile文件。 文章主要內(nèi)容包括: Docker容器 Dockerfile 使用多階構(gòu)建 感謝公司提供大量機(jī)器資源及時(shí)間讓我們可以實(shí)踐...
摘要:在里,通過(guò)的服務(wù)和依賴注入可以很輕松的實(shí)現(xiàn),這里是我集中功能的服務(wù)文件文件功能類集合獲取短信驗(yàn)證碼這些功能需要用到的方法需要的地方只要注入這個(gè)服務(wù)就可以獲取想要的功能。寫成組件會(huì)有樣式的限制,而這樣寫沒(méi)有。 最近開(kāi)始維護(hù)項(xiàng)目,然后我發(fā)現(xiàn)每天的時(shí)間常常是花在修改幾個(gè)小功能上,改了問(wèn)題有出了另一個(gè)問(wèn)題,思考哪里不對(duì)?,然后這么一天就過(guò)去了。。然后我就開(kāi)始思考標(biāo)題好讓我的時(shí)間不總是花在修改上...
閱讀 2137·2019-08-30 15:53
閱讀 3126·2019-08-30 15:44
閱讀 2994·2019-08-30 14:11
閱讀 2984·2019-08-30 14:01
閱讀 2784·2019-08-29 15:16
閱讀 3913·2019-08-29 13:10
閱讀 1321·2019-08-29 10:56
閱讀 2598·2019-08-26 13:58