1. <ul id="0c1fb"></ul>

      <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
      <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区

      RELATEED CONSULTING
      相關(guān)咨詢
      選擇下列產(chǎn)品馬上在線溝通
      服務(wù)時間:8:30-17:00
      你可能遇到了下面的問題
      關(guān)閉右側(cè)工具欄

      新聞中心

      這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
      web開發(fā)中怎么編寫可讀代碼

      今天小編給大家分享一下web開發(fā)中怎么編寫可讀代碼的相關(guān)知識點,內(nèi)容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

      遂川網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián)公司,遂川網(wǎng)站設(shè)計制作,有大型網(wǎng)站制作公司豐富經(jīng)驗。已為遂川數(shù)千家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\外貿(mào)網(wǎng)站制作要多少錢,請找那個售后服務(wù)好的遂川做網(wǎng)站的公司定做!

      代碼為什么要易于理解

      “Code should be written to minimize the time it would take for someone else to understand it.”

      日常工作的事實是:

      • 寫代碼前的思考和看代碼的時間遠大于真正寫的時間

      • 讀代碼是很平常的事情,不論是別人的,還是自己的,半年前寫的可認為是別人的代碼

      • 代碼可讀性高,很快就可以理解程序的邏輯,進入工作狀態(tài)

      • 行數(shù)少的代碼不一定就容易理解

      • 代碼的可讀性與程序的效率、架構(gòu)、易于測試一點也不沖突

      整本書都圍繞“如何讓代碼的可讀性更高”這個目標來寫。這也是好代碼的重要標準之一。

      如何命名

      變量名中應包含更多信息

      使用含義明確的詞,比如用download而不是get,參考以下替換方案:

       send -> deliver, dispatch, announce, distribute, route find -> search, extract, locate, recoverstart -> lanuch, create, begin, open make -> create,set up, build, generate, compose, add, new
      避免通用的詞

      tmpretval這樣詞,除了說明是臨時變量和返回值之外,沒有任何意義。但是給他加一些有意義的詞,就會很明確:

      tmp_file = tempfile.NamedTemporaryFile() ...SaveData(tmp_file, ...)

      不使用retval而使用變量真正代表的意義:

      sum_squares += v[i]; // Where's the "square" that we're summing? Bug!

      嵌套的for循環(huán)中,ij也有同樣讓人困惑的時候:

      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;

      換一種寫法就會清晰很多:

       if (clubs[ci].members[mi] == users[ui])  # OK. First letters match.

      所以,當使用一些通用的詞,要有充分的理由才可以。

      使用具體的名字

      CanListenOnPort就比ServerCanStart好,can start比較含糊,而listen on port確切的說明了這個方法將要做什么。

      --run_locally就不如--extra_logging來的明確。

      增加重要的細節(jié),比如變量的單位_ms,對原始字符串加_raw

      如果一個變量很重要,那么在名字上多加一些額外的字就會更加易讀,比如將string id; // Example: "af84ef845cd8"換成string hex_id;

                   Start(int delay)  -->  delay → delay_secs        CreateCache(int size)  -->  size → size_mbThrottleDownload(float limit)  -->  limit → max_kbps          Rotate(float angle)  -->  angle → degrees_cw

      更多例子:

      password  ->  plaintext_password comment  ->  unescaped_comment    html  ->  html_utf8    data  ->  data_urlenc
      對于作用域大的變量使用較長的名字

      在比較小的作用域內(nèi),可以使用較短的變量名,在較大的作用域內(nèi)使用的變量,最好用長一點的名字,編輯器的自動補全都可以很好的減少鍵盤輸入。對于一些縮寫前綴,盡量選擇眾所周知的(如str),一個判斷標準是,當新成員加入時,是否可以無需他人幫助而明白前綴代表什么。

      合理使用_-等符號,比如對私有變量加_前綴。
      var x = new DatePicker(); // DatePicker() 是類的"構(gòu)造"函數(shù),大寫開始var y = pageHeight(); // pageHeight() 是一個普通函數(shù) var $all_images = $("img"); // $all_images 是jQuery對象var height = 250; // height不是 //id和class的寫法分開 ...

      命名不能有歧義

      命名的時候可以先想一下,我要用的這個詞是否有別的含義。舉個例子:

      results = Database.all_objects.filter("year <= 2011")

      現(xiàn)在的結(jié)果到底是包含2011年之前的呢還是不包含呢?

      使用minmax代替limit
      CART_TOO_BIG_LIMIT = 10    if shopping_cart.num_items() >= CART_TOO_BIG_LIMIT:        Error("Too many items in cart.") MAX_ITEMS_IN_CART = 10    if shopping_cart.num_items() > MAX_ITEMS_IN_CART:     Error("Too many items in cart.")

      對比上例中CART_TOO_BIG_LIMITMAX_ITEMS_IN_CART,想想哪個更好呢?

      使用firstlast來表示閉區(qū)間
      print integer_range(start=2, stop=4)# Does this print [2,3] or [2,3,4] (or something else)? set.PrintKeys(first="Bart", last="Maggie")

      firstlast含義明確,適宜表示閉區(qū)間。

      使用beiginend表示前閉后開(2,9))區(qū)間
      PrintEventsInRange("OCT 16 12:00am", "OCT 17 12:00am") PrintEventsInRange("OCT 16 12:00am", "OCT 16 11:59:59.9999pm")

      上面一種寫法就比下面的舒服多了。

      Boolean型變量命名
      bool read_password = true;

      這是一個很危險的命名,到底是需要讀取密碼呢,還是密碼已經(jīng)被讀取呢,不知道,所以這個變量可以使用user_is_authenticated代替。通常,給Boolean型變量添加ishascanshould可以讓含義更清晰,比如:

                   SpaceLeft()  -->  hasSpaceLeft()bool disable_ssl = false  -->  bool use_ssl = true
      符合預期
      public class StatisticsCollector {    public void addSample(double x) { ... }    public double getMean() {        // Iterate through all samples and return total / num_samples    }    ...}

      在這個例子中,getMean方法遍歷了所有的樣本,返回總額,所以并不是普通意義上輕量的get方法,所以應該取名computeMean比較合適。

      漂亮的格式

      寫出來漂亮的格式,充滿美感,讀起來自然也會舒服很多,對比下面兩個例子:

      class StatsKeeper {   public:   // A class for keeping track of a series of doubles      void Add(double d);  // and methods for quick statistics about them     private:   int count;        /* how many so    far   */ public:           double Average();   private:   double minimum;   list     past_items         ;double maximum;};

      什么是充滿美感的呢:

      // A class for keeping track of a series of doubles// and methods for quick statistics about them.class StatsKeeper {  public:    void Add(double d);    double Average();  private:    list past_items;    int count;  // how many so far    double minimum;    double maximum;};
      考慮斷行的連續(xù)性和簡潔

      這段代碼需要斷行,來滿足不超過一行80個字符的要求,參數(shù)也需要注釋說明:

      public class PerformanceTester {    public static final TcpConnectionSimulator wifi = new TcpConnectionSimulator(        500, /* Kbps */        80, /* millisecs latency */        200, /* jitter */        1 /* packet loss % */);     public static final TcpConnectionSimulator t3_fiber = new TcpConnectionSimulator(        45000, /* Kbps */        10, /* millisecs latency */        0, /* jitter */        0 /* packet loss % */);     public static final TcpConnectionSimulator cell = new TcpConnectionSimulator(        100, /* Kbps */        400, /* millisecs latency */        250, /* jitter */        5 /* packet loss % */);}

      考慮到代碼的連貫性,先優(yōu)化成這樣:

      public class PerformanceTester {    public static final TcpConnectionSimulator wifi =        new TcpConnectionSimulator(            500, /* Kbps */            80, /* millisecs latency */ 200, /* jitter */            1 /* packet loss % */);     public static final TcpConnectionSimulator t3_fiber =        new TcpConnectionSimulator(            45000, /* Kbps */            10,    /* millisecs latency */            0,     /* jitter */            0      /* packet loss % */);     public static final TcpConnectionSimulator cell =        new TcpConnectionSimulator(            100,   /* Kbps */            400,   /* millisecs latency */            250,   /* jitter */            5      /* packet loss % */);}

      連貫性好一點,但還是太羅嗦,額外占用很多空間:

      public class PerformanceTester {    // TcpConnectionSimulator(throughput, latency, jitter, packet_loss)    //                            [Kbps]   [ms]    [ms]    [percent]    public static final TcpConnectionSimulator wifi =        new TcpConnectionSimulator(500,    80,     200,     1);     public static final TcpConnectionSimulator t3_fiber =        new TcpConnectionSimulator(45000,  10,     0,       0);     public static final TcpConnectionSimulator cell =        new TcpConnectionSimulator(100,    400,    250,     5);}

      用函數(shù)封裝

      // Turn a partial_name like "Doug Adams" into "Mr. Douglas Adams".// If not possible, 'error' is filled with an explanation.string ExpandFullName(DatabaseConnection dc, string partial_name, string* error); DatabaseConnection database_connection;string error;assert(ExpandFullName(database_connection, "Doug Adams", &error)        == "Mr. Douglas Adams");assert(error == "");assert(ExpandFullName(database_connection, " Jake Brown ", &error)        == "Mr. Jacob Brown III");assert(error == "");assert(ExpandFullName(database_connection, "No Such Guy", &error) == "");assert(error == "no match found");assert(ExpandFullName(database_connection, "John", &error) == "");assert(error == "more than one result");

      上面這段代碼看起來很臟亂,很多重復性的東西,可以用函數(shù)封裝:

      CheckFullName("Doug Adams", "Mr. Douglas Adams", "");CheckFullName(" Jake Brown ", "Mr. Jake Brown III", "");CheckFullName("No Such Guy", "", "no match found");CheckFullName("John", "", "more than one result"); void CheckFullName(string partial_name,                   string expected_full_name,                   string expected_error) {    // database_connection is now a class member    string error;    string full_name = ExpandFullName(database_connection, partial_name, &error);    assert(error == expected_error);    assert(full_name == expected_full_name);}

      列對齊

      列對齊可以讓代碼段看起來更舒適:

      CheckFullName("Doug Adams"   , "Mr. Douglas Adams" , "");CheckFullName(" Jake  Brown ", "Mr. Jake Brown III", "");CheckFullName("No Such Guy"  , ""                  , "no match found");CheckFullName("John"         , ""                  , "more than one result"); commands[] = {    ...    { "timeout"      , NULL              , cmd_spec_timeout},    { "timestamping" , &opt.timestamping , cmd_boolean},    { "tries"        , &opt.ntry         , cmd_number_inf},    { "useproxy"     , &opt.use_proxy    , cmd_boolean},    { "useragent"    , NULL              , cmd_spec_useragent},    ...};

      代碼用塊區(qū)分

      class FrontendServer {    public:        FrontendServer();        void ViewProfile(HttpRequest* request);        void OpenDatabase(string location, string user);        void SaveProfile(HttpRequest* request);        string ExtractQueryParam(HttpRequest* request, string param);        void ReplyOK(HttpRequest* request, string html);        void FindFriends(HttpRequest* request);        void ReplyNotFound(HttpRequest* request, string error);        void CloseDatabase(string location);        ~FrontendServer();};

      上面這一段雖然能看,不過還有優(yōu)化空間:

      class FrontendServer {    public:        FrontendServer();        ~FrontendServer();        // Handlers        void ViewProfile(HttpRequest* request);        void SaveProfile(HttpRequest* request);        void FindFriends(HttpRequest* request);         // Request/Reply Utilities        string ExtractQueryParam(HttpRequest* request, string param);        void ReplyOK(HttpRequest* request, string html);        void ReplyNotFound(HttpRequest* request, string error);         // Database Helpers        void OpenDatabase(string location, string user);        void CloseDatabase(string location);};

      再來看一段代碼:

      # Import the user's email contacts, and match them to users in our system.# Then display a list of those users that he/she isn't already friends with.def suggest_new_friends(user, email_password):    friends = user.friends()    friend_emails = set(f.email for f in friends)    contacts = import_contacts(user.email, email_password)    contact_emails = set(c.email for c in contacts)    non_friend_emails = contact_emails - friend_emails    suggested_friends = User.objects.select(email__in=non_friend_emails)    display['user'] = user    display['friends'] = friends    display['suggested_friends'] = suggested_friends    return render("suggested_friends.html", display)

      全都混在一起,視覺壓力相當大,按功能化塊:

      def suggest_new_friends(user, email_password):    # Get the user's friends' email addresses.    friends = user.friends()    friend_emails = set(f.email for f in friends)     # Import all email addresses from this user's email account.    contacts = import_contacts(user.email, email_password)    contact_emails = set(c.email for c in contacts)     # Find matching users that they aren't already friends with.    non_friend_emails = contact_emails - friend_emails    suggested_friends = User.objects.select(email__in=non_friend_emails)     # Display these lists on the page. display['user'] = user    display['friends'] = friends    display['suggested_friends'] = suggested_friends     return render("suggested_friends.html", display)

      讓代碼看起來更舒服,需要在寫的過程中多注意,培養(yǎng)一些好的習慣,尤其當團隊合作的時候,代碼風格比如大括號的位置并沒有對錯,但是不遵循團隊規(guī)范那就是錯的。

      如何寫注釋

      當你寫代碼的時候,你會思考很多,但是最終呈現(xiàn)給讀者的就只剩代碼本身了,額外的信息丟失了,所以注釋的目的就是讓讀者了解更多的信息。

      應該注釋什么

      不應該注釋什么

      這樣的注釋毫無價值:

      // The class definition for Accountclass Account {    public:        // Constructor        Account();        // Set the profit member to a new value        void SetProfit(double profit);        // Return the profit from this Account        double GetProfit();};
      不要像下面這樣為了注釋而注釋:
      // Find a Node with the given 'name' or return NULL.// If depth <= 0, only 'subtree' is inspected.// If depth == N, only 'subtree' and N levels below are inspected.Node* FindNodeInSubtree(Node* subtree, string name, int depth);
      不要給爛取名注釋
      // Enforce limits on the Reply as stated in the Request,// such as the number of items returned, or total byte size, etc. void CleanReply(Request request, Reply reply);

      注釋的大部分都在解釋clean是什么意思,那不如換個正確的名字:

      // Make sure 'reply' meets the count/byte/etc. limits from the 'request' void EnforceLimitsFromRequest(Request request, Reply reply);
      記錄你的想法

      我們討論了不該注釋什么,那么應該注釋什么呢?注釋應該記錄你思考代碼怎么寫的結(jié)果,比如像下面這些:

      // Surprisingly, a binary tree was 40% faster than a hash table for this data.// The cost of computing a hash was more than the left/right comparisons. // This heuristic might miss a few words. That's OK; solving this 100% is hard. // This class is getting messy. Maybe we should create a 'ResourceNode' subclass to// help organize things.

      也可以用來記錄流程和常量:

      // TODO: use a faster algorithm// TODO(dustin): handle other image formats besides JPEG NUM_THREADS = 8 # as long as it's >= 2 * num_processors, that's good enough. // Impose a reasonable limit - no human can read that much anyway.const int MAX_RSS_SUBSCRIPTIONS = 1000;

      可用的詞有:

      TODO  : Stuff I haven't gotten around to yetFIXME : Known-broken code hereHACK  : Adimittedly inelegant solution to a problemXXX   : Danger! Major problem here
      站在讀者的角度去思考

      當別人讀你的代碼時,讓他們產(chǎn)生疑問的部分,就是你應該注釋的地方。

      struct Recorder {    vector data;    ...    void Clear() {        vector().swap(data); // Huh? Why not just data.clear()?     }};

      很多C++的程序員啊看到這里,可能會想為什么不用data.clear()來代替vector.swap,所以那個地方應該加上注釋:

      // Force vector to relinquish its memory (look up "STL swap trick")vector().swap(data);
      說明可能陷阱

      你在寫代碼的過程中,可能用到一些hack,或者有其他需要讀代碼的人知道的陷阱,這時候就應該注釋:

      void SendEmail(string to, string subject, string body);

      而實際上這個發(fā)送郵件的函數(shù)是調(diào)用別的服務(wù),有超時設(shè)置,所以需要注釋:

      // Calls an external service to deliver email.  (Times out after 1 minute.)void SendEmail(string to, string subject, string body);
      全景的注釋

      有時候為了更清楚說明,需要給整個文件加注釋,讓讀者有個總體的概念:

      // This file contains helper functions that provide a more convenient interface to our// file system. It handles file permissions and other nitty-gritty details.
      總結(jié)性的注釋

      即使是在函數(shù)內(nèi)部,也可以有類似文件注釋那樣的說明注釋:

      # Find all the items that customers purchased for themselves.for customer_id in all_customers:    for sale in all_sales[customer_id].sales:        if sale.recipient == customer_id:            ...

      或者按照函數(shù)的步進,寫一些注釋:

      def GenerateUserReport():    # Acquire a lock for this user    ...    # Read user's info from the database    ...    # Write info to a file    ...    # Release the lock for this user

      很多人不愿意寫注釋,確實,要寫好注釋也不是一件簡單的事情,也可以在文件專門的地方,留個寫注釋的區(qū)域,可以寫下你任何想說的東西。

      注釋應簡明準確

      前一個小節(jié)討論了注釋應該寫什么,這一節(jié)來討論應該怎么寫,因為注釋很重要,所以要寫的精確,注釋也占據(jù)屏幕空間,所以要簡潔。

      精簡注釋
      // The int is the CategoryType.// The first float in the inner pair is the 'score',// the second is the 'weight'.typedef hash_map > ScoreMap;

      這樣寫太羅嗦了,盡量精簡壓縮成這樣:

      // CategoryType -> (score, weight)typedef hash_map > ScoreMap;
      避免有歧義的代詞
      // Insert the data into the cache, but check if it's too big first.

      這里的it's有歧義,不知道所指的是data還是cache,改成如下:

      // Insert the data into the cache, but check if the data is too big first.

      還有更好的解決辦法,這里的it就有明確所指:

      // If the data is small enough, insert it into the cache.
      語句要精簡準確
      # Depending on whether we've already crawled this URL before, give it a different priority.

      這句話理解起來太費勁,改成如下就好理解很多:

      # Give higher priority to URLs we've never crawled before.
      精確描述函數(shù)的目的
      // Return the number of lines in this file.int CountLines(string filename) { ... }

      這樣的一個函數(shù),用起來可能會一頭霧水,因為他可以有很多歧義:

      • ”” 一個空文件,是0行還是1行?

      • “hello” 只有一行,那么返回值是0還是1?

      • “hello\n” 這種情況返回1還是2?

      • “hello\n world” 返回1還是2?

      • “hello\n\r cruel\n world\r” 返回2、3、4哪一個呢?

      所以注釋應該這樣寫:

      // Count how many newline bytes ('\n') are in the file.int CountLines(string filename) { ... }
      用實例說明邊界情況
      // Rearrange 'v' so that elements < pivot come before those >= pivot;// Then return the largest 'i' for which v[i] < pivot (or -1 if none are < pivot)int Partition(vector* v, int pivot);

      這個描述很精確,但是如果再加入一個例子,就更好了:

      // ...// Example: Partition([8 5 9 8 2], 8) might result in [5 2 | 8 9 8] and return 1int Partition(vector* v, int pivot);
      說明你的代碼的真正目的
      void DisplayProducts(list products) {    products.sort(CompareProductByPrice);    // Iterate through the list in reverse order    for (list::reverse_iterator it = products.rbegin(); it != products.rend();            ++it)        DisplayPrice(it->price);    ... }

      這里的注釋說明了倒序排列,單還不夠準確,應該改成這樣:

      // Display each price, from highest to lowestfor (list::reverse_iterator it = products.rbegin(); ... )
      函數(shù)調(diào)用時的注釋

      看見這樣的一個函數(shù)調(diào)用,肯定會一頭霧水:

      Connect(10, false);

      如果加上這樣的注釋,讀起來就清楚多了:

      def Connect(timeout, use_encryption):  ... # Call the function using named parametersConnect(timeout = 10, use_encryption = False)
      使用信息含量豐富的詞
      // This class contains a number of members that store the same information as in the// database, but are stored here for speed. When this class is read from later, those// members are checked first to see if they exist, and if so are returned; otherwise the// database is read from and that data stored in those fields for next time.

      上面這一大段注釋,解釋的很清楚,如果換一個詞來代替,也不會有什么疑惑:

      // This class acts as a caching layer to the database.
      簡化循環(huán)和邏輯

      流程控制要簡單

      讓條件語句、循環(huán)以及其他控制流程的代碼盡可能自然,讓讀者在閱讀過程中不需要停頓思考或者在回頭查找,是這一節(jié)的目的。

      條件語句中參數(shù)的位置

      對比下面兩種條件的寫法:

      if (length >= 10)while (bytes_received < bytes_expected) if (10 <= length)while (bytes_expected > bytes_received)

      到底是應該按照大于小于的順序來呢,還是有其他的準則?是的,應該按照參數(shù)的意義來

      • 運算符左邊:通常是需要被檢查的變量,也就是會經(jīng)常變化的

      • 運算符右邊:通常是被比對的樣本,一定程度上的常量

      這就解釋了為什么bytes_received < bytes_expected比反過來更好理解。

      if/else的順序

      通常,if/else的順序你可以自由選擇,下面這兩種都可以:

      if (a == b) {    // Case One ...} else {    // Case Two ...} if (a != b) {    // Case Two ...} else {    // Case One ...}

      或許對此你也沒有仔細斟酌過,但在有些時候,一種順序確實好過另一種:

      • 正向的邏輯在前,比如if(debug)就比if(!debug)

      • 簡單邏輯的在前,這樣ifelse就可以在一個屏幕顯示 - 有趣、清晰的邏輯在前

      舉個例子來看:

      if (!url.HasQueryParameter("expand_all")) {    response.Render(items);    ...} else {    for (int i = 0; i < items.size(); i++) {        items[i].Expand();    }    ... }

      看到if你首先想到的是expand_all,就好像告訴你“不要想大象”,你會忍不住去想它,所以產(chǎn)生了一點點迷惑,最好寫成:

      if (url.HasQueryParameter("expand_all")) {    for (int i = 0; i < items.size(); i++) {        items[i].Expand();    }    ... } else {    response.Render(items);    ... }
      三目運算符(?:)
      time_str += (hour >= 12) ? "pm" : "am"; Avoiding the ternary operator, you might write:    if (hour >= 12) {        time_str += "pm";    } else {        time_str += "am";}

      使用三目運算符可以減少代碼行數(shù),上例就是一個很好的例證,但是我們的真正目的是減少讀代碼的時間,所以下面的情況并不適合用三目運算符:

      return exponent >= 0 ? mantissa * (1 << exponent) : mantissa / (1 << -exponent); if (exponent >= 0) {    return mantissa * (1 << exponent);} else {    return mantissa / (1 << -exponent);}

      所以只在簡單表達式的地方用。

      避免使用do/while表達式
      do {    continue;} while (false);

      這段代碼會執(zhí)行幾遍呢,需要時間思考一下,do/while完全可以用別的方法代替,所以應避免使用。

      盡早return
      public boolean Contains(String str, String substr) {    if (str == null || substr == null) return false;    if (substr.equals("")) return true;    ...}

      函數(shù)里面盡早的return,可以讓邏輯更加清晰。

      減少嵌套
      if (user_result == SUCCESS) {    if (permission_result != SUCCESS) {        reply.WriteErrors("error reading permissions");        reply.Done();        return;    }    reply.WriteErrors("");} else {    reply.WriteErrors(user_result);}reply.Done();

      這樣一段代碼,有一層的嵌套,但是看起來也會稍有迷惑,想想自己的代碼,有沒有類似的情況呢?可以換個思路去考慮這段代碼,并且用盡早return的原則修改,看起來就舒服很多:

      if (user_result != SUCCESS) {    reply.WriteErrors(user_result);    reply.Done();    return;}if (permission_result != SUCCESS) {    reply.WriteErrors(permission_result);    reply.Done();    return;}reply.WriteErrors("");reply.Done();

      同樣的,對于有嵌套的循環(huán),可以采用同樣的辦法:

      for (int i = 0; i < results.size(); i++) {    if (results[i] != NULL) {        non_null_count++;        if (results[i]->name != "") {            cout << "Considering candidate..." << endl;            ...        }    }}

      換一種寫法,盡早return,在循環(huán)中就用continue:

      for (int i = 0; i < results.size(); i++) {    if (results[i] == NULL) continue;    non_null_count++;     if (results[i]->name == "") continue;    cout << "Considering candidate..." << endl;    ... }
      拆分復雜表達式

      很顯然的,越復雜的表達式,讀起來越費勁,所以應該把那些復雜而龐大的表達式,拆分成一個個易于理解的小式子。

      用變量

      將復雜表達式拆分最簡單的辦法,就是增加一個變量:

      if line.split(':')[0].strip() == "root": //用變量替換username = line.split(':')[0].strip() if username == "root":    ...

      或者這個例子:

      if (request.user.id == document.owner_id) {    // user can edit this document...}...if (request.user.id != document.owner_id) {// document is read-only...} //用變量替換final boolean user_owns_document = (request.user.id == document.owner_id);if (user_owns_document) {    // user can edit this document...}...if (!user_owns_document) {    // document is read-only...}
      邏輯替換
      • 1) not (a or b or c) <–> (not a) and (not b) and (not c)

      • 2) not (a and b and c) <–> (not a) or (not b) or (not c)

      所以,就可以這樣寫:

      if (!(file_exists && !is_protected)) Error("Sorry, could not read file."); //替換if (!file_exists || is_protected) Error("Sorry, could not read file.");
      不要濫用邏輯表達式
      assert((!(bucket = FindBucket(key))) || !bucket->IsOccupied());

      這樣的代碼完全可以用下面這個替換,雖然有兩行,但是更易懂:

      bucket = FindBucket(key);if (bucket != NULL) assert(!bucket->IsOccupied());

      像下面這樣的表達式,最好也不要寫,因為在有些語言中,x會被賦予第一個為true的變量的值:

      x = a || b || c
      拆解大表達式
      var update_highlight = function (message_num) {    if ($("#vote_value" + message_num).html() === "Up") {        $("#thumbs_up" + message_num).addClass("highlighted");        $("#thumbs_down" + message_num).removeClass("highlighted");    } else if ($("#vote_value" + message_num).html() === "Down") {        $("#thumbs_up" + message_num).removeClass("highlighted");        $("#thumbs_down" + message_num).addClass("highlighted");    } else {        $("#thumbs_up" + message_num).removeClass("highighted");        $("#thumbs_down" + message_num).removeClass("highlighted");    }};

      這里面有很多重復的語句,我們可以用變量還替換簡化:

      var update_highlight = function (message_num) {    var thumbs_up = $("#thumbs_up" + message_num);    var thumbs_down = $("#thumbs_down" + message_num);    var vote_value = $("#vote_value" + message_num).html();    var hi = "highlighted";     if (vote_value === "Up") {        thumbs_up.addClass(hi);        thumbs_down.removeClass(hi);    } else if (vote_value === "Down") {        thumbs_up.removeClass(hi);        thumbs_down.addClass(hi);    } else {        thumbs_up.removeClass(hi);        thumbs_down.removeClass(hi);    }}
      變量與可讀性
      消除變量

      前一節(jié),講到利用變量來拆解大表達式,這一節(jié)來討論如何消除多余的變量。

      沒用的臨時變量
      now = datetime.datetime.now()root_message.last_view_time = now

      這里的now可以去掉,因為:

      • 并非用來拆分復雜的表達式

      • 也沒有增加可讀性,因為`datetime.datetime.now()`本就清晰

      • 只用了一次

      所以完全可以寫作:

      root_message.last_view_time = datetime.datetime.now()
      消除條件控制變量
      boolean done = false;while (/* condition */ && !done) {    ...    if (...) {        done = true;        continue;     }}

      這里的done可以用別的方式更好的完成:

      while (/* condition */) {    ...    if (...) {        break;    } }

      這個例子非常容易修改,如果是比較復雜的嵌套,break可能并不夠用,這時候就可以把代碼封裝到函數(shù)中。

      減少變量的作用域

      我們都聽過要避免使用全局變量這樣的忠告,是的,當變量的作用域越大,就越難追蹤,所以要保持變量小的作用域。

      class LargeClass {    string str_;    void Method1() {        str_ = ...;        Method2();    }    void Method2() {        // Uses str_    }    // Lots of other methods that don't use str_     ... ;}

      這里的str_的作用域有些大,完全可以換一種方式:

      class LargeClass {    void Method1() {        string str = ...;        Method2(str);     }    void Method2(string str) {        // Uses str    }    // Now other methods can't see str.};

      str通過變量函數(shù)參數(shù)傳遞,減小了作用域,也更易讀。同樣的道理也可以用在定義類的時候,將大類拆分成一個個小類。

      不要使用嵌套的作用域
      # No use of example_value up to this point.if request:    for value in request.values:    if value > 0:        example_value = value         break for logger in debug.loggers:    logger.log("Example:", example_value)

      這個例子在運行時候會報example_value is undefined的錯,修改起來不算難:

      example_value = Noneif request:    for value in request.values:        if value > 0: example_value = value         break if example_value:    for logger in debug.loggers:    logger.log("Example:", example_value)

      但是參考前面的消除中間變量準則,還有更好的辦法:

      def LogExample(value):    for logger in debug.loggers:        logger.log("Example:", value)     if request:        for value in request.values:            if value > 0:                LogExample(value)  # deal with 'value' immediately                break
      用到了再聲明

      在C語言中,要求將所有的變量事先聲明,這樣當用到變量較多時候,讀者處理這些信息就會有難度,所以一開始沒用到的變量,就暫緩聲明:

      def ViewFilteredReplies(original_id):    filtered_replies = []    root_message = Messages.objects.get(original_id)     all_replies = Messages.objects.select(root_id=original_id)    root_message.view_count += 1    root_message.last_view_time = datetime.datetime.now()    root_message.save()     for reply in all_replies:        if reply.spam_votes <= MAX_SPAM_VOTES:            filtered_replies.append(reply)     return filtered_replies

      讀者一次處理變量太多,可以暫緩聲明:

      def ViewFilteredReplies(original_id):    root_message = Messages.objects.get(original_id)    root_message.view_count += 1    root_message.last_view_time = datetime.datetime.now()    root_message.save()     all_replies = Messages.objects.select(root_id=original_id)     filtered_replies = []    for reply in all_replies:        if reply.spam_votes <= MAX_SPAM_VOTES:            filtered_replies.append(reply)     return filtered_replies
      變量最好只寫一次

      前面討論了過多的變量會讓讀者迷惑,同一個變量,不停的被賦值也會讓讀者頭暈,如果變量變化的次數(shù)少一些,代碼可讀性就更強。

      一個例子

      假設(shè)有一個頁面,如下,需要給第一個空的input賦值:

      ...var setFirstEmptyInput = function (new_value) {    var found = false;    var i = 1;    var elem = document.getElementById('input' + i);    while (elem !== null) {        if (elem.value === '') {            found = true;            break;         }        i++;        elem = document.getElementById('input' + i);    }    if (found) elem.value = new_value;    return elem;};

      這段代碼能工作,有三個變量,我們逐一去看如何優(yōu)化,found作為中間變量,完全可以消除:

      var setFirstEmptyInput = function (new_value) {    var i = 1;    var elem = document.getElementById('input' + i);    while (elem !== null) {        if (elem.value === '') {            elem.value = new_value;            return elem;        }        i++;        elem = document.getElementById('input' + i);    }    return null;};

      再來看elem變量,只用來做循環(huán),調(diào)用了很多次,所以很難跟蹤他的值,i也可以用for來修改:

      var setFirstEmptyInput = function (new_value) {    for (var i = 1; true; i++) {        var elem = document.getElementById('input' + i);        if (elem === null)            return null;  // Search Failed. No empty input found.        if (elem.value === '') {            elem.value = new_value;            return elem;        }    }};

      重新組織你的代碼

      分離不相關(guān)的子問題

      工程師就是將大問題分解為一個個小問題,然后逐個解決,這樣也易于保證程序的健壯性、可讀性。如何分解子問題,下面給出一些準則:

      • 看看這個方法或代碼,問問你自己“這段代碼的最終目標是什么?”

      • 對于每一行代碼,要問“它與目標直接相關(guān),或者是不相關(guān)的子問題?”

      • 如果有足夠多行的代碼是處理與目標不直接相關(guān)的問題,那么抽離成子函數(shù)

      來看一個例子:

      ajax_post({    url: 'http://example.com/submit',    data: data,    on_success: function (response_data) {        var str = "{\n";        for (var key in response_data) {            str += "  " + key + " = " + response_data[key] + "\n";        }        alert(str + "}");        // Continue handling 'response_data' ...    }});

      這段代碼的目標是發(fā)送一個ajax請求,所以其中字符串處理的部分就可以抽離出來:

      var format_pretty = function (obj) {    var str = "{\n";    for (var key in obj) {        str += "  " + key + " = " + obj[key] + "\n";    }    return str + "}";};
      意外收獲

      有很多理由將format_pretty抽離出來,這些獨立的函數(shù)可以很容易的添加feature,增強可靠性,處理邊界情況,等等。所以這里,可以將format_pretty增強,就會得到一個更強大的函數(shù):

      var format_pretty = function (obj, indent) {    // Handle null, undefined, strings, and non-objects.    if (obj === null) return "null";    if (obj === undefined) return "undefined";    if (typeof obj === "string") return '"' + obj + '"';    if (typeof obj !== "object") return String(ob            
                  
                                                                  
      網(wǎng)頁題目:web開發(fā)中怎么編寫可讀代碼
      文章出自:http://ef60e0e.cn/article/piojsh.html
      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区
      1. <ul id="0c1fb"></ul>

        <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
        <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

        苍梧县| 弥渡县| 大兴区| 芜湖市| 永清县| 即墨市| 鄂托克旗| 东乌珠穆沁旗| 江安县| 阿克陶县| 绵竹市| 盘锦市| 游戏| 郧西县| 富宁县| 将乐县| 昆明市| 丹寨县| 庆城县| 临沭县| 赞皇县| 临城县| 通渭县| 华阴市| 通辽市| 凌源市| 高安市| 清新县| 镇康县| 漾濞| 郸城县| 江孜县| 丹凤县| 福清市| 洛宁县| 沽源县| 青川县| 西华县| 冀州市| 广安市| 永兴县|