摘要:代碼寫得是否整潔是客觀的,是的人或后期維護的人覺得好才是真的好。三代碼設計原則要想寫出優(yōu)雅整潔的代碼,就要遵循特定的設計原則。
歡迎關注我的公眾號睿Talk,獲取我最新的文章:
最近在做一些項目重構的工作,看了不少臟亂差的代碼,身心疲憊。本文將討論如何編寫整潔的代碼,不求高效運行,只求可讀性強,便于維護。
二、為什么要寫簡潔的代碼作為一個合格的程序員,寫出簡潔的代碼是基本的職業(yè)素養(yǎng)。相信絕大部分的程序員都不會故意寫惡心代碼的,無論是對自己或者對別人都沒有任何好處。那么,是什么阻礙我們寫出優(yōu)秀代碼呢?有下面這么幾種可能性:
時間緊任務重,沒那么多時間考慮代碼設計
偷懶圖方便,不過腦子機械式的寫代碼
注意力只集中在功能的實現(xiàn),不考慮后期的維護成本
知識儲備不夠,不知道怎樣寫出優(yōu)雅的代碼
出來混遲早要還的,無論是上述哪種原因,混亂代碼一旦被寫出來,代碼作者肯定是要為其買單的,只是買單的方式會各有不同。可能是后期維護的時候邊改邊抽自己,也可能是別人改代碼的時候邊改邊罵你傻x。
那么,代碼寫好了會有什么好處呢?起碼有以下幾方面:
后期維護更高效,無論是改 bug 還是新增功能,無論是自己改還是別人改
后期更少的加班
思考如何編寫整潔代碼的過程中,技術能力會隨之提高
寫出優(yōu)雅的代碼,會更有成就感,更熱愛自己的工作
別人看到這么優(yōu)雅的代碼,會贊不絕口,個人影響力會放大
既然有這么多好處,那到底怎么評判代碼寫得好不好呢?是自己覺得好就是好嗎?顯然不是。代碼寫得是否整潔是客觀的,是 code review 的人或后期維護的人覺得好才是真的好。所以加強 code review 也是倒逼寫出優(yōu)秀代碼的一種方式。
個人認為代碼的優(yōu)秀程度分以下幾個層次:
程序能正常運行
異常情況有應對方法
簡單明了,易于后期維護
團隊成員間能高效協(xié)作
能高性能運行
層次越高,難度越大,挑戰(zhàn)越大。作為一個有追求的程序員,我們應該不斷突破自己的邊界,追求卓越,更上一層樓。
三、代碼設計原則要想寫出優(yōu)雅整潔的代碼,就要遵循特定的設計原則。透徹理解這些原則后,還要結合具體的項目落地,不斷的練習和重構。下面總結出的一些通用原則供參考。
KISS(keep it simple, stupid)
業(yè)務邏輯要直截了當,不要引入各種依賴,多層次調用。以 React 為例,常見的錯誤是將props在state里存一份,計算的時候再從state中取。這樣帶來的問題是要時刻監(jiān)聽props的變化,然后再同步到state中。這完全是多此一舉,直接用props進行計算即可。
// bad componentWillReceiveProps(nextProps) { this.setState({num: nextProps.num}); } render() { return({this.state.num * 2}); } /***************************/ // good render() { return({this.props.num * 2}); }
DRY (don’t repeat yourself)
不用做機械式的復制粘貼,要鍛煉自己抽象的能力,盡量將通用的邏輯抽象出來,方便日后重用。
Open/Closed (open to extension but closed to modification)
代碼要對擴展開放,對修改封閉。盡量做到在不修改原有代碼的基礎上,增加新的功能。React 的容器組件和展示組件分離用的就是這種思想。當數(shù)據來源不同的時候,只需要修改或新增容器組件,展示組件維持不變。
function Comp() { ... } class ContainerComp extends PureComponent { async componentDidMount() { const data = await fetchData(); this.setState({data}); } render() { return (); } }
從架構層面說,微內核架構也是遵循這一設計原則。它能保證核心模塊不變的情況下,通過插件機制為系統(tǒng)賦予新的能力。我們常用的 Webpack 就是一個很好的例子,它通過引入 loader 和 plugin 的機制,極大的擴展了其文件處理的能力。
Composition > Inheritance
首先說一下類這個概念。本質上來說,定義類就是為了代碼的復用,對于需要同時創(chuàng)建多個對象實例的情況下,這種設計模式是非常有效的。比如說連接池中就需要同時存在多個連接對象,方便資源復用。而對于前端來說,絕大部分的業(yè)務場景都是單例,這種情況下通過定義工具函數(shù),或者直接使用對象字面量會更加高效。工具函數(shù)盡量使用純函數(shù),使代碼更易于理解,不用考慮副作用。這里說的僅限于業(yè)務代碼的范疇,如果是框架型的項目,場景會復雜得多,類會更有用武之地。
既然類都不需要用了,繼承就更無從談起了。繼承的問題是多級繼承之后,定位問題會非常困難,要一級一級往上找才能找到錯誤出處。而組合就沒有這種問題,因為多個功能都是平級的,哪里出問題一眼就能看出來。比較一下下面 2 種風格的代碼:
繼承的寫法:
class Parent extends PureComponent { componentDidMount() { this.fetchData(this.url); } fetchData(url) { ... } render() { const data = this.calcData(); return ({data} ); } } class Child extends Parent { constructor(props) { super(props); this.url = "http://api"; } calcData() { ... } }組合的寫法:
class Parent extends PureComponent { componentDidMount() { this.fetchData(this.props.url); } fetchData(url) { ... } render() { const data = this.props.calcData(this.state); return ({data} ); } } class Child extends PureComponent { calcData(state) { ... } render() {} } 哪種更易于理解呢?
Single Responsibility
遵循單一職責的代碼如果設計得好,組合起來的代碼就會非常的清爽。舉一個注冊的場景,可以劃分為下面幾個職責:
UI 展示
輸入合法性判斷
網絡請求
聚合層
偽代碼如下:
// UI.js export default function UI() { ... } // api.js export function regist(name, email) { ... } // validate.js export function validateName(name) { ... } export function validateEmail(email) { ... } // Regist.js export default class Regist extends PureComponent { ... onSubmit = async () => { const {name, email} = this.state; if (validateName(name) && validateEmail(email)) { const resp = await regist(name, email); ... } } render() {} } 可以看到聚合層的代碼非常簡潔,哪里出問題了就到相應的地方改就好了,即使是多人協(xié)作,也不容易出問題。
Separation of Concerns
關注點分離原則跟單一職責原則有點類似,但更強調的是系統(tǒng)架構層面的設計。典型的例子就是 MVC 模式,Model、View、Control 三層之間都有明確的職責劃分,做到了高內聚低耦合。
React 的源碼設計也是基于這一原則,分為ReactElement, ReactCompositeComponent 和 ReactDomComponent 三層。ReactElement 負責描述頁面的 DOM 結構,也就是著名的 Virtual DOM;ReactCompositeComponent 處理的是組件生命周期、組件 Diff 和更新等邏輯;而ReactDomComponent是真正進行 DOM 操作的類。三層之間分工明確,緊密協(xié)作,共同組成了一個強大的前端框架。
Clean Code > Clever Code
提倡簡潔易懂的代碼,而不是晦澀難懂的“聰明”代碼,如下面這種:
let a, b=3, t = (a=2, b<1) ? (console.log("Y"),"yes") : (console.log("N"),"no");單一代碼文件不超過 200 行
文件一旦超過 200 行,說明邏輯已經有點復雜了,要想辦法抽離出一些純函數(shù)工具方法,讓主線邏輯更加清晰。工具方法可以放在另外的文件里面,減少讀代碼的心理壓力。有一點需要說明一下,并不是所有的文件都不能超過 200 行,像工具方法這種,都是各自獨立的邏輯,寫多少行都無所謂。需要控制的是緊密關聯(lián)的業(yè)務代碼的行數(shù)。
四、前端代碼如何拆分上面提到要合理的拆分代碼,那到底怎么拆呢?對于前端組件代碼,有下面一些拆分點以供參考:
UI
展現(xiàn)邏輯
事件處理
業(yè)務邏輯
網絡請求
配置類純JSON對象
工具類純函數(shù)
需要說明的是展現(xiàn)邏輯和業(yè)務邏輯是兩回事,最好不要混在一起寫。比如組件的顯示隱藏是展現(xiàn)邏輯,而數(shù)據的校驗就是業(yè)務邏輯。
五、總結本文討論了書寫整潔代碼的必要性和重要性,結合實例列出了一些設計原則,還給出了組件代碼拆分的方式。程序員的職業(yè)生涯是一個自我修煉的過程,時刻關注代碼質量,是提高技術水平的重要一環(huán)。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/105330.html
相關文章
代碼整潔之道
摘要:在代碼整潔之道,提出一種軟件質量,可持續(xù)開發(fā)不僅在于項目架構設計,還與代碼質量密切相關,代碼的整潔度和質量成正比,一份整潔的代碼在質量上是可靠的,為團隊開發(fā),后期維護,重構奠定了良好的基礎。 現(xiàn)在的軟件系統(tǒng)開發(fā)難度主要在于其復雜度和規(guī)模,客戶需求也不再像Winston Royce瀑布模型期望那樣在系統(tǒng)編碼前完成所有的設計滿足用戶軟件需求。在這個信息爆炸技術日新月異的時代,需求總是在不停...
代碼整潔之道 - 有意義的命名
摘要:我們這里再介紹一下,朱重八家族的名字,都很有特點。取這樣的名字不是因為朱家是搞數(shù)學的,而是因為在元朝,老百姓如果不能上學和當官就沒有名字,只能以父母年齡相加或者出生的日期命名。所以說命名不僅僅是一種科學,更是一種藝術。 在小朱元璋出生一個月后,父母為他取了一個名字(元時慣例):朱重八,這個名字也可以叫做朱八八。我們這里再介紹一下,朱重八家族的名字,都很有特點。朱重八高祖名字:朱百六;朱...
代碼整潔之道 - 有意義的命名
摘要:我們這里再介紹一下,朱重八家族的名字,都很有特點。取這樣的名字不是因為朱家是搞數(shù)學的,而是因為在元朝,老百姓如果不能上學和當官就沒有名字,只能以父母年齡相加或者出生的日期命名。所以說命名不僅僅是一種科學,更是一種藝術。 在小朱元璋出生一個月后,父母為他取了一個名字(元時慣例):朱重八,這個名字也可以叫做朱八八。我們這里再介紹一下,朱重八家族的名字,都很有特點。朱重八高祖名字:朱百六;朱...
架構整潔之道(二)——兩個價值的故事
摘要:每個軟件系統(tǒng)都提供兩個價值給利益相關者表現(xiàn)和結構。來自利益相關者的觀點,開發(fā)者僅僅只提供了一些形態(tài)上的粗略改變,來自開發(fā)者的觀點,老板的需求越來越難。記住,作為一個開發(fā)者,你就是利益相關者,你需要維護的軟件里有你的利益。 每個軟件系統(tǒng)都提供兩個價值給利益相關者:表現(xiàn)和結構。軟件開發(fā)者應的確保這兩個價值盡量高負責。然而很不幸,程序員很多只關心其中一個而忽略另一個,甚至更不幸,他們可能關注...
發(fā)表評論
0條評論
閱讀 1833·2021-11-25 09:43
閱讀 1334·2021-11-22 15:08
閱讀 3734·2021-11-22 09:34
閱讀 3225·2021-09-04 16:40
閱讀 3000·2021-09-04 16:40
閱讀 542·2019-08-30 15:54
閱讀 1334·2019-08-29 17:19
閱讀 1751·2019-08-28 18:13