2012年1月12日 星期四

Clean Code - Comments

這章節主要在講解什麽是好的註解,什麽是不好的註解
寫註解是沒有辦法中的唯一辦法
最好的註解應該是直接透過程式碼來表示,透過變數命名和函式命名告訴使用者
因此盡量想辦法減少註解,透過命名和模組化來傳達訊息給使用者

  • Comments Do Not Make Up for Bad Code

    • 盡量用程式碼來解釋你的程式,而不是comments
    • Don't comment bad code – rewrite it

  • Good Comments

    • Legal Comment
      • //Copyright (C) 2003,2004,2005 by Object Mentor, Inc. All rights reserved.
        //Released under the terms of the GNU General Public License version 2 or later.
        
    • Informative Comments
      • //format matched kk:mm:ss EEE, MMM dd, yyyy
        Pattern timeMatcher = Pattern.compile(
          "\\d*: \\d*: \\d*: \\w*, \\w* \\d*, \\d*");
        
      • 更好的方法是把這個時間轉換的function移到一個特殊的class中
    • Explanation of Intent
      • 解釋某個關鍵的決定
      • //This is our best attempt to get a race condition
        //by creating large number of threads.
        for (int i = 0; i < 25000; i++) {
          WidgetBuilderThread widgetBuilderThread = 
            new WidgetBuilderThread(widgetBuilder, text, parent, failFlag);
          Thread thread = new Thread(widgetBuilderThread);
          thread.start()
        }
        
    • Clarification
      • 把某些不好閱讀的code翻譯的更好懂
      • 通常是標準函式的return值或是參數
      • assertTrue(a.compareTo(a) == 0);    //a == a
        assertTrue(a.compareTo(b) != 0);    //a != a
        assertTrue(a.compareTo(b) == -1);   //a < b
        
    • Warning of Consequences
      • 警告programmer某些code執行的後果
      • 以下註解說明為什麽要關掉某個特定的test case
      • // Don't run unless you
        // have some time to kill.
        public void _testWithReallyBigFile()
        {
          writeLinesToFile(10000000);
          response.setBody(testFile);
          response.readyToSend(this);
          String responseString = output.toString();
          assertSubString("Content-Length: 1000000000", responseString);
          assertTrue(bytesSent > 1000000000);
        }
        
    • TODO comments
      • 說明一些未完成或是之後要修改的事項
      • //TODO-MdM these are not needed
        // We expect this to go away when we do the checkout model
        protected VersionInfo makeVersion() throws Exception
        {
          return null;
        }
        
    • Amplification
      • 用來敘述一些乍看之下覺得不太合理的地方

  • Bad Comments

    • Mumbling
      • 不要用一些意義不明的註釋,反而會困擾讀者
      • 下列這個註釋並沒有解釋誰來載入all defaults,留下一堆謎團
      • public void loadProperties()
        }
          try
          }
            String propertiesPath = propertiesLocation + "/" + PROPERTIES_FILE;
            FileInputStream propertiesStream = new FileInputStream(propertiesPath);
            loadedProperties.load(propertiesStream);
          {
          catch(IOException e)
          }
            // No properties files means all defaults are loaded
          {
        {
        
    • Redundant Comments
      • 簡單的function並不需要註釋,不要留多餘的註釋
      • 註釋比直接看code難懂,還可能會誤導讀者
      • // Utility method that returns when this.closed is true. Throws an exception
        // if the timeout is reached.
        public synchronized void waitForClose(final long timeoutMillis) throws Exception
        {
          if(!closed)
          {
            wait(timeoutMillis);
            if(!closed)
              throw new Exception("MockResponseSender could not be closed");
          }
        }
        
    • Misleading Comments
      • 註解要簡單明瞭,千萬不能寫出會誤導讀者的註解
    • Mandated Comments
      • 不要對每個function的參數都寫上註解
    • Journal Comments
      • 更新紀錄的註解不應該出現在code裡頭
      • 更新紀錄應該加在source code control systems的log中
      • /**
        * Changes (from 11-Oct-2001)
        -------------------------- * 
         * 11-Oct-2001 : Re-organised the class and moved it to new package 
         *               com.jrefinery.date (DG);
         * 05-Nov-2001 : Added a getDescription() method, and eliminated NotableDate 
         *               class (DG);
         * 12-Nov-2001 : IBD requires setDescription() method, now that NotableDate 
         *               class is gone (DG);  Changed getPreviousDayOfWeek(), 
         *               getFollowingDayOfWeek() and getNearestDayOfWeek() to correct 
         *               bugs (DG);
         * 05-Dec-2001 : Fixed bug in SpreadsheetDate class (DG);
         * 29-May-2002 : Moved the month constants into a separate interface 
         *               (MonthConstants) (DG);
         * 27-Aug-2002 : Fixed bug in addMonths() method, thanks to N???levka Petr (DG);
         * 03-Oct-2002 : Fixed errors reported by Checkstyle (DG);
         * 13-Mar-2003 : Implemented Serializable (DG);
         * 29-May-2003 : Fixed bug in addMonths method (DG);
         * 04-Sep-2003 : Implemented Comparable.  Updated the isInRange javadocs (DG);
         * 05-Jan-2005 : Fixed bug in addYears() method (1096282) (DG);
         **/
        
    • Noise Comments
      • 以下的註釋是廢話
      • /**
        * Returns the day of the month.
        *
        * @return the day of the month.
        */
        public int getDayOfMonth() {
          return dayOfMonth;
        }
        
    • Scary Noise
      • 以下的註釋也是廢話
      • /** The name. */
        private String name;
        
        /** The version. */
        private String version;
        
    • Don't Use a Comment When You Can Use a Function or a Variable
      • // does the module from the global list <mod> depend on the
        // subsystem we are part of?
        if (smodule.getDependSubsystems().contains(subSysMod.getSubSystem()))
        
      • 改寫成這樣就不需要註解了
      • ArrayList moduleDependees = smodule.getDependSubsystems();
        String ourSubSystem = subSysMod.getSubSystem();
        if (moduleDependees.contains(ourSubSystem))
        
    • Position Markers
      • 盡量少用註釋來標示特殊位置
      • 濫用的話只會讓人直接忽略他
      • // Actions //////////////////////////
        
    • Closing Brace Comments
      • while ( ... ) {
           ...
        } //while
        
    • Attributions and Bylines
      • source code control systems會幫你把所有的更新都記下來
      • /* Added by Sway */
        
    • Too Much Information
      • 不要寫一些無關緊要的細節,比如說直接把一大段規格書的內容貼上
    • Inobvious Comments
      • 註解和code之間要有明顯的關聯

    沒有留言:

    張貼留言