顯示具有 Notes 標籤的文章。 顯示所有文章
顯示具有 Notes 標籤的文章。 顯示所有文章

2013年1月4日 星期五

如何命名變數以及函式名稱

  • 命名要精準

    • 命名要精準,不要使用模糊的單字
    • def GetPage(url):
      
    • Get是一個不好得命名,沒有明確指出從什麼地方GetPage
    • 如果是從Internet,應命名為FetchPage()或DownloadPage()
    • class BinaryTree:
          def Size(self):
              ...
      
    • Size是一個不好得命名,沒有明確指出是tree的高度還是有多少個nodes
    • 直接命名為Height()或是NumNodes()會比較明確

  • 找尋更能表達意義的單字

    • 使用字典找尋適合的單字
    • 以下是一些colorful word的範例,=>左手邊的單字可用右手邊的單字取代
    • send => deliver, dispatch, announce, distribute, route
    • find => search, extract, locate, recover
    • start => launch, create, begin, open
    • make => create, set up, build, generate, compose, add, new

  • 避免tmp和retval這類通用的命名

    • 讀者沒辦法從retval中獲得更多資訊,使用更清楚的命名取代retval
    • tmp只適合用在很短生存範圍的程式碼,例如下列swapping範例tmp生存範圍只在if{}內
    • if (right < left) {
          tmp = right;
          right = left;
          left = tmp;
      }
      

  • Loop Iterator

    • iterator的名稱使用i, j, k是OK的,因為這是大家都習慣的用法
    • for (int i = 0; i < clubs.size(); i++)
          for (int j = 0; j < clubs[i].members.size(); j++)
              for (int k = 0; k < users.size(); k++)
                  if (clubs[i].members[k] == users[j])
                      cout << "user[" << j << "] is in club[" << i << "]" << endl;
      }
      
    • 最好在i, j, k前加入prefix的字串,用來辨別iterator所走訪的物件
    • if (clubs[ci].members[mi] == users[ui])
      
    • 如此一來有bug就能即時發現
    • if (clubs[ci].members[ui] == users[mi]) # Bug! First letters don't match up.
      

  • 數值和單位

    • 在命名數值變數時可以加上單位
    • Start(int delay); 
      CreateCache(int size); 
      ThrottleDownload(float limit); 
      Rotate(float angle); 
      
      Start(int delay_secs); 
      CreateCache(int size_mb);
      ThrottleDownload(float max_kbps); 
      Rotate(float degrees_cw); 
      
    • 第二個範例會比第一個範例好

  • 命名要包含資料狀態

    • 加入目前資料的狀態到命名
    • password : plaintext_password
    • comment : unescaped_comment
    • html : html_utf8
    • data : data_urlenc
  • 在短生存範圍內可使用簡短的命名

    • 變數m只出現在if的範圍內,不影響其他人理解這段程式碼
    • if (debug) {
          map<string,int> m;
          LookUpNamesNumbers(&m);
          Print(m);
      }
      

  • 不使用不常見的縮寫

    • 用BEManager取代BackEndManager並不是一個好得命名
    • 團隊新來的成員沒辦法搞懂這些縮寫

  • 刪除多餘的命名

    • ConvertToString() => ToString()
    • DoServeLoop() => ServeLoop()

  • 使用命名格式來區分不同意義

    • const, macro, class, private variable都有不同的命名格式,方便一眼區分
    • static const int kMaxOpenFiles = 100;
      class LogReader {
        public:
          void OpenFile(string local_file);
        private:
          int offset_;
          DISALLOW_COPY_AND_ASSIGN(LogReader);
      };
      

    2013年1月3日 星期四

    撰寫良好程式碼最重要的原則


  • 程式碼應該要容易被理解

    • 程式碼應該要容易被理解,讓別人花最少時間讀懂程式碼
    • 易讀的程式碼同事意味著有良好的架構且較容易測試
    • 大多數時候,攥寫較短的程式碼會比長的程式碼來得好(但是也有例外)



  • 撰寫較少的程式碼通常比較好

    • 2000行程式碼會比5000行程式碼來的容易瞭解
    • 某些情況長的程式碼會比短的程式碼來得容易瞭解
    • assert((!(bucket = FindBucket(key))) || !bucket->IsOccupied());
      
    • 第二個例子雖然比較長,但是比第一個例子容易理解
      bucket = FindBucket(key);
      if (bucket != NULL) assert(!bucket->IsOccupied());
      
    • 加上一些註解會更容易瞭解
      // Fast version of "hash = (65599 * hash) + c"
      hash = (hash << 6) + (hash << 16) - hash + c;
      
    • 永遠都以撰寫好讀的程式碼為目標

    2012年10月18日 星期四

    Debug-Later Programming v.s. Test-Driven Development

  • Debug-Later Programming(DLP)
    • Traditional way of programming
    • 設計架構之後開始寫程式
    • 當程式寫完後開始進行測試以及Debug
      • 測試和Debug的過程會佔據軟體開發超過一半的時間
      • late feedback(bug需要花好幾天、數周甚至超過一個月才會讓程式開發者知道)
      • 軟體開發時間拉長,而且難以估計時間
  • Test-Driven Development
    • 在寫程式前先撰寫unit test
      • Tests are small
      • 自動化測試,自動測試是TDD的關鍵
    • 通過所有測試即完成程式
    • 新增功能的步驟
      • 加入一個小的測試程式
      • 跑一遍所有的測資,檢查是否通過所有測項
      • 修改產品的程式碼到通過測項
    • TDD的好處
      • Bug變少
      • debug時間變少
      • 減少發生bug的side effect
      • 測試程式本身就是軟體文件
      • 睡覺睡得安穩,週末不會被打擾
      • 監控專案進度
      • TDD is fun

    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之間要有明顯的關聯

    2012年1月5日 星期四

    Clean Code - Functions

    這章的內容是在教你怎麼寫出簡潔的function
    以下是一個錯誤示範,你花三分鐘可能還看不懂他在寫什麽
    public static String testableHtml(PageData pageData, boolean includeSuiteSetup) throws Exception {
                WikiPage wikiPage = pageData.getWikiPage();
                StringBuffer buffer = new StringBuffer();
                if (pageData.hasAttribute("Test")) {
                    
                    if (includeSuiteSetup) {
                        WikiPage suiteSetup =
                        PageCrawlerImpl.getInheritedPage(SuiteResponder.SUITE_SETUP_NAME, wikiPage);
    
                        if (suiteSetup != null) {
                            WikiPagePath pagePath = suiteSetup.getPageCrawler().getFullPath(suiteSetup);
                            String pagePathName = PathParser.render(pagePath);
                            buffer.append("!include -setup .")
                                .append(pagePathName)
                                .append("\n");
                        }
                    }
    
                    WikiPage setup = PageCrawlerImpl.getInheritedPage("SetUp", wikiPage);
                    if (setup != null) {
                        WikiPagePath setupPath =
                        wikiPage.getPageCrawler().getFullPath(setup);
                        String setupPathName = PathParser.render(setupPath);
                        buffer.append("!include -setup .")
                            .append(setupPathName)
                            .append("\n");
                    }
                }
            buffer.append(pageData.getContent());
            if (pageData.hasAttribute("Test")) {
                WikiPage teardown =
                PageCrawlerImpl.getInheritedPage("TearDown", wikiPage);
                
                if (teardown != null) {
                    WikiPagePath tearDownPath =
                    wikiPage.getPageCrawler().getFullPath(teardown);
                    String tearDownPathName = PathParser.render(tearDownPath);
                    buffer.append("\n")
                        .append("!include -teardown .")
                        .append(tearDownPathName)
                        .append("\n");
                }
         }   
            if (includeSuiteSetup) {
                WikiPage suiteTeardown = PageCrawlerImpl.getInheritedPage(SuiteResponder.SUITE_TEARDOWN_NAME, wikiPage);
                if (suiteTeardown != null) {
                    WikiPagePath pagePath = suiteTeardown.getPageCrawler().getFullPath (suiteTeardown);
                    String pagePathName = PathParser.render(pagePath);
                    buffer.append("!include -teardown .")
                    .append(pagePathName)
                    .append("\n");
                }
            }
        
            pageData.setContent(buffer.toString());
            return pageData.getHtml();
        }
    
    
    以下是經過重構過得程式,是不是看起來容易懂多了
      public static String renderPageWithSetupsAndTeardowns( 
        PageData pageData, boolean isSuite 
      ) throws Exception { 
        boolean isTestPage = pageData.hasAttribute("Test"); 
        if (isTestPage) { 
          WikiPage testPage = pageData.getWikiPage(); 
          StringBuffer newPageContent = new StringBuffer(); 
          includeSetupPages(testPage, newPageContent, isSuite); 
          newPageContent.append(pageData.getContent()); 
          includeTeardownPages(testPage, newPageContent, isSuite); 
          pageData.setContent(newPageContent.toString()); 
        } 
        return pageData.getHtml(); 
      } 
    
  • Small!

    • Function should be small
    • Function以20行為最佳
      • 之前的例子再進一步重構
      • public static String renderPageWithSetupsAndTeardowns(PageData pageData,
            boolean isSuite) throws Exception {
            if (isTestPage(pageData)) {
                includeSetupAndTeardownPages(pageData, isSuite);
            }
            return pageData.getHtml();
        }
        
      • if, else, while其中的block最好只有一行,而那一行是一個function call

  • Do One Thing

    • 第一個範例會讓人看不懂是因為他做太多事情了
    • Function should do one thing. They should do it well. They should do it only.
    • 當寫完function後,要再檢查看看是否還能把其中內容再拆成另一個function

  • One Level of Abstraction per Function

    • 函式中的statments都要再同一個抽象層級上
    • 第一個範例中的getHtml()就是屬於較高層級的概念,append則是低層級的概念
    • Reading code from top to bottom
      • 就像讀報紙一樣

  • Switch Statements

    • 我們很難讓switch精簡短小
    • 使用abstract factory和多型來處理switch

  • Use Descriptive Name

    • You know you are working on clean code when each routine you read turns out to be pretty much what you expected."
    • 不要害怕使用long name,SetupTeardownIncluder.render會比testableHtml要來的好

  • Function Arguments

    • 最理想的function是沒有參數,其次是一個參數,再來是兩個參數
    • 除非是特殊理由,否則不要使用三個以上的參數
    • 盡量避免使用output arguments,因為大多數人預期function由參數輸入並且由return回傳,output arguments會讓人想比較久
      • boolean fileExists("MyFile") 透過參數來問一個問題
      • InputStream fileOpen("MyFile") 操作一個參數並轉換成InputStream回傳
    • 還有一種形式是event,void passwordAttemptFailedNtimes(int attempts)
      • 此形式沒有return value,通常用於設定系統參數
      • 小心為他命名,務必讓讀者知道這是一個event
    • Flag Arguments
      • Flag arguments are ugly!
      • 違反Do One Thing的原則,盡量拆成兩個functions
    • 兩個參數的function
      • 比一個參數來的難懂
      • assertEquals(expected, actual),很容易搞混前後參數的次序,expected在前只是一種不成文的規定
      • 盡量拆解成一個參數的function,ex: writeField(outputStream, name)可改成outputStream.writeField(name)
    • 三個參數以上的function
      • 三個以上的參數非常難懂
      • 參數有三個以上,通常代表你需要把其中參數封裝成class,ex: x, y封裝成Point
      • 盡量拆解成一個參數的function,ex: writeField(outputStream, name)可改成outputStream.writeField(name)

  • Have No Side Effects

    • 執行以下這個function會有隱藏的side effect
    • public class UserValidator {
        private Cryptographer cryptographer;
        public boolean checkPassword(String username, String password) {
          User user = UserGateway.findByName(username);
          if (user != User.NULL) {
             String codedPhrase = user.getPhraseEncodedByPassword();
             String phrase = cryptographer.decrypt(codedPhrase, password);
             if ("Valid Password".equals(phrase)) {
               Session.initialize();
               return true;
             }
          }
          return false;
        }
      }
      
    • 函式名稱並沒有提到會去call Session.initialize(),因此有一個隱藏的side effect
    • 一定要做的話要重新命名函式為checkPasswordAndInitializeSession (但是這違反Do One Thing)

  • Command Query Separation

    • 一個函式應該專門回答問題或是專心做某件事,而不是兩者都做
    • public boolean set(String attribute, String value),if (set("username", "unclebob"))...
      • set完參數後return是否成功,容易讓讀者搞混
    • 拆成兩個會比較好
    • if (attributeExist("username")) {
        setAttribute("username", "unclebob");
      }
      

  • Prefer Exceptions to Returning Error Codes

    • if (deletePage(page) == E_OK) 使用Exceptions來處理這種returning error codes

  • Don't Repeat Yourself

    • 不要在函式中複製貼上重複的code

  • Structured Programming

    • 在小函式中可以使用break和continue
    • 避免使用goto

    2011年12月30日 星期五

    Clean Code - Meaningful Names

    最近看了一本大家推薦的書
    Clean Code: A Handbook of Agile Software Craftsmanship

    看書名就知道這是一本教你怎麼寫出乾淨又簡潔的code
    書中提的一些概念和方法我覺得蠻有用的,很推薦大家去看這本書
    以下是我整理的一些筆記


    • Use Intention-Revealing Names
    • 選一個好的變數名稱雖然會花時間,但是未來絕對可以幫你省下更多時間
    • 一個好的命名要能傳達以下資訊
    • Why it exists?
    • What it does?
    • How it is used? 
    • 需要額外的註解,不是好的命名
    •  int d; // elapsed time in days
    • 好的命名,清楚的表達變數的用途
    • int elapsedTimeInDays; 
      int daysSinceCreation; 
      int daysSinceModification; 
      int fileAgeInDays; 
      
    • 不好的命名方式,很難看懂這段code在做什麽
    • public List<int[]> getThem() { 
        List<int[]> list1 = new ArrayList<int[]>(); 
        for (int[] x : theList) 
          if (x[0] == 4) 
            list1.add(x); 
        return list1; 
      }
      
    • 同樣一段code經過重新命名後可以看出這是一段踩地雷遊戲程式
    • public List<int[]> getFlaggedCells() { 
        List<int[]> flaggedCells = new ArrayList<int[]>();
        for (int[] cell : gameBoard) 
          if (cell[STATUS_VALUE] == FLAGGED) 
            flaggedCells.add(cell); 
        return flaggedCells; 
      } 
      
    • 更好的作法,用一個Cell的class來取代array of ints
    • public List<int[]> getFlaggedCells() { 
        List<int[]> flaggedCells = new ArrayList<int[]>();
         for (Cell cell : gameBoard) 
           if (cell.isFlagged()) 
             flaggedCells.add(cell); 
         return flaggedCells; 
      } 
      
    • Avoid Disinformation
    • 不要使用一些特殊縮寫的名字
    • ex: hp, aix , sco  (這些縮寫在Unix有特殊意義)
    • 不要使用accountList ,除非他真的是一個List 
    • 就算真的是List也不要把Type encoding到命名裡 
    • accountGroup, bunchOfAccounts, 或是只用accounts都會比accountList來的好
    • 命名不要太過相似
      • XYZMyTodayBrunchBreadXYZMyTodayBreakfastBread 很難被區別
    • 不要使用小寫L和小寫O來做命名,因為很難跟1和0做區別
    • int a = l; 
      if ( O == l ) 
        a = O1; 
      else 
        l = 01; 
      



  • Make Meaningful Distinctions

    • 命名要有意義上的區別
    • 使用Number-series naming(a1, a2, .. aN)不是一種好的命名方式
      • public static void copyChars(char a1[], char a2[]) { 
           for (int i = 0; i < a1.length; i++) { 
             a2[i] = a1[i]; 
           } 
        } 
        
      • 應該命名成source和destination
    • 不要使用類似的單字來命名
      • ex: Product, ProductInfo, ProductData,你無法分辨這三者有什麽不同
    • 不要加上多餘的字
      • ex: NameString, CustomerObject,難道Name會是一個floating pointer嗎?
    • function名稱也要避免使用多餘的字,誰能告訴我以下三個function有什麽差別?
    • getActiveAccount(); 
      getActiveAccounts(); 
      getActiveAccountInfo(); 
      



  • Use Pronounceable Names

    • 使用可以發音的單字來命名,方便Programmer之間溝通
      • genymdhms(generation date, year, month, day, hour, minute, and second)應改成 generationTimestamp



  • Use Searchable Names

    • 使用好被搜尋的名字
    • MAX_CLASSES_PER_STUDENT會比數字7要來的好被找到(簡單說就是不要用magic number)
    • single-letter命名只能被使用在short methods中的local variable


  • Avoid Encodings

    • 不要使用匈牙利命名法
      • 早期因為Compiler不會幫忙檢查type才需要匈牙利命名法,現在的Compiler都很強大
      • 使用匈牙利命名法未來如果要更動型別會很麻煩
        PhoneNumber phoneString; 
        // name not changed when type changed!
        
    • Member Prefixes
      • 不需要prefix member variable with m_
      • 直接用this pointer來區分會比較好,而且在大多數的編輯器中this會有highlight
      • public class Part { 
          private String m_dsc; // The textual description 
          void setName(String name) { 
            m_dsc = name; 
          } 
        } 
        
        public class Part { 
          String description; 
          void setDescription(String description) { 
            this.description = description; 
          } 
        } 
        


  • Avoid Mental Mapping

    • 不要使用單一小寫字母來做命名,因為會需要人腦去做Mapping
    • 如果在很小並且不會和其他名稱衝突的scope,比如說迴圈,使用i, j, k還可以被接受。因為大家都習慣了,容易聯想
    • 如果是使用單一字母a, b, c的話就很糟糕
    • 清楚命名才是王道(clarity is king)


  • Class Names

    • 使用名詞來命名Class和Object,不要使用動詞
    • ex: Customer, WikiPage, Account, AddressParser
    • 避免使用Manager, Processor, Data, Info


  • Method Names

    • 使用動詞來命名,ex: postPayment, deletePage, save
    • Accessors, mutators和predicates要以 get, set和is開頭
      • string name = employee.getName();
        customer.setName("Mike");
        if (paycheck.isPosted())...
        
    • overload constructor的時候,可以使用Factory method pattern來清楚敘述參數
    • Complex fulcrumPoint = Complex.FromRealNumber(23.0);會比Complex fulcrumPoint = new Complex(23.0);來得好


  • Don't be Cute

    • 不要使用俚語或是隱喻的命名方式,因為別人有可能會看不懂
      • (X)HolyHandGrenade => (O)DeleteItems
      • (X)whack() => (O)kill()
      • (X)eatMyShorts() => (O)abort()


  • Pick One Word per Concept

    • 使用一樣的單字來表達同樣一件事情
    • 沒有人分的出來fetch, retrieve, get有什麽差別,使用其中一個為你的method命名就好
    • DeviceManager和ProtocolController,看到這兩個單字你會疑惑Manager和Controller有什麽差別。因此不是一個好的命名
    • 維持code的一致性是很重要的


  • Don't Pun

    • 不要使用同一個單字去表達兩個不同的概念
    • 如果已經有一個叫做add的method用來把兩數相加,就要避免再用add命名"把single parameter加到一個collection的method"。改用insert和append


  • Use Solution Domain Names

    • 命名盡量使用Computer Science領域的專有名詞,因為讀你code的人都是programmer
    • 好的例子
      • AccountVisitor => VISITOR pattern
      • JobQueue => 每個programmer都知道這是什麽


  • Use Problem Domain Names

    • 如果沒有Solution Domain Names可以使用,那就使用和你所解問題有關的字彙。至少可以讓看你code的人去問相關領域的專家


  • Add Meaningful Context

    • firstName, lastName, street, houseNumber, city, state, zipcode這些變數放在一起很明顯可以組成一個address。但是如果你只看到state,你會不知道這代表什麽意思
    • 使用prefix讓他們組合成一個概念,addrFirstName, addrLastName, addrState......


  • Don't Add Gratuitous Context

    • 不要加上多餘的prefix
    • 如果一個application叫做"Gas Satation Deluxe",不需要把所有的class都加上GSD這個prefix
      • 會很難使用IDE做搜尋(所有Class開頭都是GSD)
      • Class名稱會變的很冗長


  • Final Words

    • 不要害怕重新命名,現在有很多好用的tool可以幫助你