你可以從你們現在項目裡面隨便找幾處註釋,看看寫註釋的代碼是不是存在如下兩種毛病之一:1. 命名不准確;2. 方法太長(超過50行)。如果你找到的代碼沒有出現上面兩種毛病而註釋依然存在,那你再看看這個註釋是否有實際意義,是不是這個註釋不要也無所謂呢。註釋是惡魔這個觀點可能你第一次看到,你可能很難接受,...
你可以從你們現在項目裡面隨便找幾處註釋,看看寫註釋的代碼是不是存在如下兩種毛病之一:
1. 命名不准確;
2. 方法太長(超過50行)。
如果你找到的代碼沒有出現上面兩種毛病而註釋依然存在,那你再看看這個註釋是否有實際意義,是不是這個註釋不要也無所謂呢。
註釋是惡魔
這個觀點可能你第一次看到,你可能很難接受,因為寫了這麼多年的註釋,你從未想過註釋居然是惡魔,所以,你看到這個觀點的時候可能就會本能的找出1000種理由反對(絕對不可能實現啊什麼的),但是,這個觀點並不是今天才出現,相信很多年前就有人提出,現在已被越來越多的人認可。
我第一次接受到這個觀點還是從一個美國客戶(十幾年編程經驗的技術大牛)那裡,2011年,他讓我們不要寫註釋。他當時主要意思是我們寫的中式英語他猜起來太費勁,所以他後面又安慰我們說“好的代碼是不需要註釋的”,而我從此就將他後面那半句話奉為至寶。
註釋是惡魔,它將我們的代碼變得很難理解。就像本文開篇說的,你可以找找你們項目中出現註釋的地方,要麼命名不准確,要麼方法太長。你可以隨機找10處註釋,看看有幾處是惡魔,歡迎貼到評論中。
舉一個以前項目中的例子吧,命名不准確的例子:
/// <summary> /// 管理員是否可以審核該申請 /// </summary> public bool IsAudit { get; set; }
在這個例子中,其實將"is"換成"can"就不需要註釋了。
寫註釋讓代碼更難讀。
首先,如果一個程式員可以隨便寫註釋,那麼他對命名準確性和方法長度的控制就不會那麼在意,寫代碼更隨意,代碼質量比不能寫註釋的程式員更大幾率低下。
其次,代碼註釋只是在寫代碼的時候提供說明,如果讀代碼都依靠註釋的話,那一個類被另一個類引用來引用去的就根本沒法閱讀了。
所以,“寫註釋是為了讓代碼更易讀”本身就是站不住腳的。
不寫一行註釋?根本就做不到!
這句話可能從你閱讀本文開始在心裡面重覆了無數遍,這也是大多數人的心聲。
其實前面說的,寫註釋讓代碼更難讀的觀點很多朋友從內心上是認可的。因為確實沒有辦法啊,有的方法業務邏輯複雜,不知不覺方法已上百行,有的命名還是中西結合的,不寫註釋自己第二天就讀不懂了。所以,真是糾結,內心承受百般折磨。
寫到這裡,突然想起在園子里看到的一個笑話,說一個公司的產品每年都在更新換代,因為每年新招的程式員都要把程式重新寫一遍。
“零註釋”根本做不到?如果你叢刻開始懷疑自己的這個觀點,那你就可能做得到。
如何做到不寫一行註釋
1. 從現在開始,強迫自己不要寫註釋。
2. 控制每個方法不超過50行,用方法定義來描述方法的實現邏輯。
3. 變數命名不要太過隨便。
本文想要告訴大家的是,零註釋一點都不難。我們團隊大約從2012年開始全面執行零註釋,後面經歷2個產品項目,多個外包項目,積累的經驗越來越多,獲得的質量效果越來越好,零註釋越來越深入人心。
零註釋這個編碼規則也是我們團隊近些年質量建設非常重要的里程碑之一,再此分享給大家。如果能夠影響你一點點,那都足夠了。
附2個我們的代碼片段
雖沒有註釋,大家不妨猜猜這兩個方法做什麼用的。
1. 查詢的例子
1 public PageResult<IssueDto> Search(IssueSearchCriteria criteria, PageRequest request) 2 { 3 using (var db = base.NewDB()) 4 { 5 return db.Issues 6 .WhereByAssignee(criteria.AssignedUserId) 7 .WhereBySupervisor(criteria.SupervisorUserId) 8 .WhereByCategory(criteria.CategoryId) 9 .WhereBySearchStatus(criteria.Status) 10 .WhereDateRange(criteria) 11 .WhereNotDeleted() 12 .WhereByKeyword(criteria.Keyword) 13 .ToDtos() 14 .OrderByDescending(x => x.CreatedTime) 15 .ToPageResult(request.PageIndex, request.PageSize); 16 } 17 }
2. 更新的例子
1 public void Submit(Guid userId, string content, string text, double lng, double lat, string address) 2 { 3 using (var db = base.NewDB()) 4 { 5 var issue = new Issue(userId, content, text, lng, lat, address); 6 db.Issues.Add(issue); 7 db.AddIssueLog(IssueLog.CreateOnSubmit(issue.Id, db.Users.GetNickName(userId))); 8 db.SaveChanges(); 9 10 issue.GenerateSerialNumber(); 11 if (!string.IsNullOrEmpty(SettingContext.Instance.AdminOpenIds)) 12 { 13 var name = db.Users.GetName(userId); 14 var totalPendings = db.Issues.Count(x => x.Status == IssueStatus.None && x.IsDeleted == false); 15 var adminOpenIds = SettingContext.Instance.AdminOpenIds.Split(','); 16 foreach (var openId in adminOpenIds) 17 { 18 var message = new PendingProcessTemplateMessage(openId, issue, name, totalPendings); 19 db.WeixinScheduledMessages.Add(message.ToWeixinScheduledMessage()); 20 } 21 } 22 db.SaveChanges(); 23 } 24 }