代碼之丑 例子
1,命名例子
這段代碼做了什么?
public void processChapter(long chapterId) { Chapter chapter = this.repository.findByChapterId(chapterId); if (chapter == null) { throw new IllegalArgumentException("Unknown chapter [" + chapterId + "]"); } chapter.setTranslationState(TranslationState.TRANSLATING); this.repository.save(chapter); }
根據章節id從數據庫中讀取章節,把章節的狀態改為翻譯中,再把章節寫回數據庫。
把一個章節的翻譯狀態改為翻譯中。
問題:需要閱讀這段代碼的細節,才能知道這段代碼是做什么的?
將章節的狀態改為翻譯中,叫處理章節。
將章節的狀態改為翻譯完成,是不是也叫處理章節?
修改章節內容也叫處理章節?
命名過于寬泛,沒有錯,但不精準。
命名首先要能夠描述這段代碼做的事情。changeChapterToTranslating
如果把細節平鋪開來,那本質上和直接閱讀細節差別不大。
一個好的名字應該描述意圖,而非細節。
我們為什么把翻譯狀態改為翻譯中,這一定是有原因的,也就是意圖。具體到這里的業務,我們把翻譯狀態修改為翻譯中,是因為我們在這里開啟了一個翻譯的過程。所以,這段函數應該命名startTranslation。
2,
if (user.isEditor()) { service.editChapter(chapterId, title, content, true); } else { service.editChapter(chapterId, title, content, false); }
if判斷的是參數,而不是動作。
到
boolean approved = user.isEditor(); service.editChapter(chapterId, title, content, approved);
到
boolean approved = isApproved(user); service.editChapter(chapterId, title, content, approved); private boolean isApproved(final User user) { return user.isEditor(); }
3,代碼一次加一點,逐漸變壞
if (code == 400 || code == 401) { // 做一些錯誤處理 }
到
if (code == 400 || code == 401 || code == 402) { // 做一些錯誤處理 }
到
if (code == 400 || code == 401 || code == 402 || ... || code == 500 || ... || ... || code == 10000 || ...) { // 做一些錯誤處理 }
任何代碼都經不起這種無意識的累積,每個人都沒做錯,但最終的結果很糟糕。
讓營地比你來時更干凈。
4, 大類,職責不單一
用戶類
public class User { private long userId; private String name; private String nickname; private String email; private String phoneNumber; private AuthorType authorType; private ReviewStatus authorReviewStatus; private EditorType editorType; ... }
用戶Id, 姓名,昵稱,郵箱,電話號碼
作者類型,表示作者是簽約作者還是普通作者。簽約作者可以設置作品的付費信息,而普通作者不能。
authorReviewStatus作者審核狀態,作者成為簽約作者,需要有一個申請審核的過程,這個狀態就是審核的的狀態。
編輯類型,編輯可以是主編,也可以是小編,他們的權限不一樣。
對普通用戶,普通用戶既不是作者,也不是編輯。作者和編輯相關的字段,對普通用戶來說沒有意義。
對作者,編輯相關的字段也沒有意義,作者是不能成為編輯的。
對編輯,作者相關的字段沒有意義,編輯也不會成為作者。
問題:只有一個用戶類。
public class User { private long userId; private String name; private String nickname; private String email; private String phoneNumber; ... }
作者類,
public class Author { private long userId; private AuthorType authorType; private ReviewStatus authorReviewStatus; ... }
編輯類,
public class Editor { private long userId; private EditorType editorType; ... }
拆出Author和Editor兩個類,把作者和編輯相關的字段分別移到了這兩個類里面。
在這兩個類里分別有一個userId字段,用以識別這個角色是和哪個用戶相關。
5,大類,字段分組
有時候,我們會覺得有一些字段確實都屬于某個類,結果就是,這個類還是很大。
比如拆完的新的User類。
public class User { private long userId; private String name; private String nickname; private String email; private String phoneNumber; ... }
這些字段應該都算用戶信息的一部分。但是,即便是相比于原來的User類小了很多。這個類依然也不算是一個小類。
原因就是,這個類里面的字段并不屬于同一種類型的信息。比如,userId,name,nickname 幾項,算是用戶的基本信息,
而email, phoneNumber這些屬于用戶的聯系方式。
從需求上看,基本信息是那種一旦確定就不怎么會改變的內容,而聯系方式則會根據實際情況調整。比如,綁定各種社交媒體的賬號。
如果我們把這些信息都放到一個類里面,這個類的穩定程度就要差一些。
把User類的字段分組,把不同的信息放到不同的類里面。
public class User { private long userId; private String name; private String nickname; private Contact contact; ... }
聯系方式類
public class Contact { private String email; private String phoneNumber; ... }
把email和PhoneNumber放進去,后面再有任何關于聯系方式的調整就放這個類里。
6,基本類型偏執
public double getEpubPrice(final boolean highQuality, final int chapterSequence) { ... }
根據章節信息獲取EPUB(一種電子書格式)的價格。
問題出在返回值的類型上,也就是價格的類型上。
我們在數據庫中存儲價格的時候,就是用一個浮點數,這里用double可以保證計算的精度,這樣的設計有什么問題?
這是很多人使用基本類型(Primitive)作為變量類型思考的角度。
實際上,這種采用基本類型的設計缺少了一個模型。
雖然價格本身是用浮點數在存儲,但是價格和浮點數本身并不是同一個概念,有著不同的行為需求。
比如,一般情況下,我們要求商品的價格是大于0的,但double類型本身是沒有這種限制的。
用double類型限制。要在使用的地方都保證價格的正確性,價格效應到處都是。
if (price <= 0) { throw new IllegalArgumentException("Price should be positive"); }
補齊這里缺失的模型,我們可以引入Price類型。校驗在初始化時進行:
class Price { private long price; public Price(final double price) { if (price <= 0) { throw new IllegalArgumentException("Price should be positive"); } this.price = price; } }
以對象取代基本類型
如果覺得本文對您有幫助~可以微信支持一下:




浙公網安備 33010602011771號