當前位置:首頁>時尚>怎么寫出好的代碼(如何寫出漂亮的代碼)
發布時間:2024-10-13閱讀(20)
這篇文章講述了代碼重構的一些原則和措施,在自己寫代碼的過程中,應該遵守這些原則,寫出漂亮的代碼。持續分享互聯網研發技術干貨,歡迎關注我。
寫代碼的一些原則
- 命名規范,含義明確。不同含義的變量和方法在詞語、大小寫、詞組上要有有效的區分。
- Never repeat yourself。常量、方法塊、處理邏輯盡量少做重復,可以抽象出共用代碼的最好抽象出來
- 代碼塊職責單一,功能原子化。一段代碼、一個方法最好只做一件事情,可以是控制某個流程,組裝某個信息等,混在一起不利于后續的修改。
- 開放擴展,關閉修改。穩定的東西盡量沉到下層,變化暴露到上層。這樣做的目的是后續維護代碼,盡量多添加,少修改。功能原子化也是為了達到這個目的,只有當功能足夠小,耦合性足夠低的時候,在添加新功能的時候,才可以不去修改老的,而主要通過擴展的方式增加業務
- 高內聚,低耦合。達到這個目的不簡單,一個基本思路就是盡量讓代碼處理的事情原子化,然后根據相關性的強弱選擇不同的聚合方式。以個人的體會來說,方法用來聚合同一個數據的操作,類用來聚合相關性比較強的一組數據或者操作,模塊用來聚合概念上有相似性的數據或者操作,層則用來聚合使用方式相同的數據或者操作。
具體的措施
以下是一些具體的措施:
一、命名二、常量
- 常量:所有字母大寫,不同單詞用下劃線隔開,如METHOD_TIME_THREAD;
- 變量名和方法名:使用小駝峰式命名結構,第一個單詞字母小寫,后續單詞的第一個字母大寫,單詞直接拼接在一起,如outNo,orderId;
- 接口名和類名:大駝峰式命名結構,所有單詞的第一個字母都大寫;
- 數據庫字段、網絡請求字段:所有字母小寫,單詞之間用下劃線隔開,如out_no,order_id。
- 對于固定的字符串、整數,盡量使用常量,避免“魔法”字符串和整數(稱之為魔法的意思是,可能稍一不注意,東西取錯了都不知道)
- 常量是無狀態的,所以常量類和常量的命名上,最好別帶太強的業務信息,可以直接使用字面意思命名,否則當別人使用該常量的時候,名字會很奇怪。
- 常量類盡量集中存放,但不要分的太細。集中放是因為常量算是公共資源,理論上所有代碼都要共用,放的越集中就容易讓后續開發者發現,不至于重新定義一模一樣的常量。不要分的太細也是為了讓后續開發者容易識別,如果每個業務都分一套常量,到時候常量類就會迅速膨脹,而且起不到常量的作用
這里舉一個例子:
hashmap<String,String> contentMap = new HashMap<String,String>();//放入一個數據的時候是這樣的:contentMap.put("orderId",orderId);// 取出數據的時候是這樣的:String orderId = contentMap.get("orderId");
這里的“orderId”字符串就稱之為魔法字符串,其實很容易寫錯。而且假如以后這個參數改個名字叫:movieOrderId,那這些字符串就得改很多處,而且不能通過搜索特定字符串一次性替換(因為很多變量名也叫orderId,而且并不見得所有的“orderId”都應該改)。而如果使用常量來代表“orderId”字符串,就在key形成了一致性約束,以后改名字的時候,只需要改常量的內容,put和get操作就自動一致了。這是一個簡單的變量“高內聚”
三、刪掉未使用代碼四、代碼布局
- 當前不用的代碼一定要刪掉,一是不整潔,二是影響后續rd的判斷,不是自己寫的刪也不合適,用也不合適。
- 沒想好的代碼不要寫一半放在原地,應該刪掉代碼,寫個todo提示
五、注釋
- 根據重要程度對變量、方法排個基本次序。人在閱讀理解的時候,會遵從類似金字塔的邏輯,所以要讓讀代碼的人先看到重要的,再看次要的。
- 相關的代碼要集中在一起,不相關的代碼要用空行隔開,這樣便于快速瀏覽代碼的邏輯。
- 變量順序:靜態常量、成員變量,所有變量按照重要性順序排列。這樣做是因為人的大腦有首因效應,對最先看到的事物記憶最深刻。
- 方法順序:構造方法,靜態方法、私有成員方法、共有成員方法,也需要按照重要性順序排列,具體順序可以合理權衡。理論上,靜態方法,多為工具類型的方法,和當前類有強綁定的關系,可以放在靠前的位置,如果沒有強綁定的關系,可以考慮抽離到外部類,避免和當前類含糊不清。私有成員方法和共有成員方法需要根據方法的重要性確定其順序,比如提供重要服務的接口需要放最前,而get、set方法,肯定是要放最后。
- 方法內代碼根據功能分塊,例子(不同的功能要隔開,有關系的功能最好集中在一起,用序號標識出流程)
六、日志
- 重要的系統盡量多加,比如支付、訂單這種,改動風險較高,能說明的最好是說明下。多寫注釋的原因主要在于代碼是英文為主,以中文為母語的人常常會將同一個單詞理解成不同的含義,比如voucher這種多義詞,既可以理解成單據、憑證,也可以理解為代金卷。特別是對于簡寫和隱形約定,很難做到每個人都理解,所以盡量多寫些注釋吧
- 重要流程、算法邏輯、特殊判斷一定要加注釋,
- 接口入參、返回值的限定,可能出現的各種情況最好用注釋說明
七、異常和中斷處理
- 日志要分類,最好一個業務一個日志,混在一起會難以統計和排查問題
- 日志要分級,error,info,debug要分清。error日志就是要認真對待的,出任何問題都要小心謹慎;info日志是打提示信息,比如請求參數、返回內容、重要流程的節點信息等;
- 日志的重點要清晰,一般模式是:先打印日志的目的(比如異常就說什么異常,提示就說什么參數的提示),然后接上重要的參數信息,參數可用你代碼中使用到的參數,這樣看到日志后,可以更快速的對應到具體的代碼上,方便排查問題。
八、模塊劃分
- 盡量統一處理異常和中斷,不要到處都是try-catch模塊或者if-else的判斷返回,一般是在業務接口上統一處理異常,下層邏輯在出現非預期的情況時,盡量拋到上層來統一處理。這里提供一種參考的異常處理和代碼中斷模式。我們先可以繼承RuntimeException,實現一個自己的異常類TransException,由于RuntimeException是可以不捕獲的異常,所以當TransException拋出來的時候,從下到上的方法都不需要throw,也不需要try-catch,這樣我們就可以在最上層catch住,統一處理異常。
- 分析上面兩個類,異常類包括:錯誤枚舉(包括錯誤碼,錯誤信息)、詳細的錯誤信息。當我們遇到可掌握的異常時,可以構造一個特定的TransException對象,將特定的錯誤枚舉,具體的異常信息(比如具體的參數異常信息,錯誤堆棧,可展示的下層業務異常信息等)填到詳細信息里邊,然后在任意地方throw,由上層統一打印錯誤信息,組裝返回內容,而不用一層一層的return false。
- 出了未知異常,盡量掌控所有可以掌控的異常。達到這個目的主要有以下做法:1、操作數據前要檢查,有問題要細化錯誤場景;2、try-catch異常盡量精細,不要try-catch太多代碼。要么是一個流程,要么是一個數據操作,catch住異常要很清楚的知道這是什么類型的異常,將錯誤場景細化
模塊劃分的目的是為了“高內聚”,把具有相同意義的代碼和數據放在一起,一是方便了閱讀和查找,二是可以將穩定的代碼沉到下層,變化抽象到上層。下圖是我們之前商量過的代碼分層框架, 不一定要完全按照這種模式來,但可以有意的按照這種思路去把代碼分離開。
從下到上來說各層代表含義:
- domain:主要是領域對象,包括枚舉、常量、VO對象,服務之間傳遞信息所需要的model等等
- dao:數據庫對應的DO對象,DAO對象等,專門負責處理數據庫的東西
- service:主要負責提供原子服務,包含外部rpc服務的封裝,相對原子的數據處理過程,對dao層的操作等等
- thrift service(server impl):thrift server 端,基于 basic service,實現 thrift service,一般需要將 domain model 封裝為 thrift model。
- thrift service(client repackage):thrift client 端,基于 thrift service,重新封裝一層 service,一般包含: thrift model 與 domain model 之間的轉換、Thrift Exception 異常轉換等。
- biz:主要處理各種業務流程,組合service層的原子服務達到某個目的,處理某個業務
- provider:不直面客戶端的也可以沒有這個模塊,主要負責接收外部請求,做權限管理(比如驗證登陸)、參數校驗,然后調用biz層的接口完成做具體業務處理
- task:用于處理定時任務、外部調度任務的模塊,負責接受任務消息或者調度請求,然后調用biz層的接口完成某件任務
- common:主要是一些工具類(時間、MD5工具等等),以及一些對象轉換的conventor
這樣分模塊的目的是為了減少代碼的耦合性,把相關的數據和代碼抽象的更集中,每當你想用某個常量、枚舉、對象的時候,你大致掃一下domain就知道當下有什么東西,不致于針對同一個東西寫好幾份代碼。而對于service來說,站在原子服務的角度來說,因為service足夠小,所以增加新業務時,service是可以不修改的,而只需要添加服務就可以,biz層也只需要添加邏輯,組合service的服務就可以達到目的,這樣就達到了“開放擴展,關閉修改”的目的,降低修改代碼帶來的風險。
九、使用模型處理外部數據時,盡量使用自己的業務模型,除非特別簡單的http回應,其它的處理都是應該封裝自己的model的。
為什么要把外部數據映射成model呢?
- 原因一:讓看代碼的人,知道你當下的業務在處理的過程中,到底和哪些參數相關,他要重點關注哪些參數。如果沒有model,直接操作外部數據,就會將這些知識散亂的分布在各處的代碼,一旦業務有變化,就不知道加減參數會不會對之前的業務有影響
- 原因二:轉換成model也是為了在一開始就對所需要的參數進行合法性檢查,業務能不能做最好在獲取參數時,就直接判斷,不能走了一些流程后,突然發現參數不可用,然后再中斷流程,這樣會消耗服務的性能。而轉換model也是為了對所有參數進行一次性的合法性檢查。
- 原因三:外部數據映射成model,會在添加或者刪除數據時造成一定的約束,對比操作json,添加和刪除都較為隨意,既不能在類型上進行約束,也不能在對應關系上強制約束,后續開發者為了圖省事,寫著寫著就亂的沒法用了。
模型要有業務含義,在傳遞給服務時,一定要和該服務有很強的業務關系,不要為了簡單使用DO,或者參數差不多、但業務不相關的model圖省事。對于沒有形成強業務綁定的model,經常會因為一個業務的修改,導致當下業務也需要聯動的修改。要降低耦合性
避免超級model,做任何業務,都把參數往一個model中塞。這樣一是讓看代碼的人毫無頭緒,不知道到底哪些參數和當下業務相關,二是讓修改的人很擔心,去掉一個參數會不會影響到多個業務。所以盡量避免使用公共model,如果確實參數重合度比較高的,可以考慮model之間的有意義的繼承。
多用模型,少用hashmap。hashmap當時它相當于一個自由模型,啥都可以塞,啥都可以取,使用上是方便,但是沒有套上任何業務信息,業務越大,修改越多,就會越不可控。每次修改都會造成一些隱患。
轉自:http://ningg.top/develop-series-refactor-best-practice/
歡迎分享轉載→http://m.avcorse.com/read-445702.html
Copyright ? 2024 有趣生活 All Rights Reserve吉ICP備19000289號-5 TXT地圖HTML地圖XML地圖