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