CoderClark
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # C0001 不白話的程式碼:文字加密 ## 別讓程式碼加密了 回顧[B0001 易讀程式碼的特性:白話](/bdQxzTadSP-HIfXlQZHxLg),我們提到了易讀程式碼的特性:**白話**;藉由簡單的例子來比較白話與不白話的差異,也將不白話的程式碼比喻是需要**翻譯機**做對應才能理解。 在這裡我們進一步比喻,這些需要翻譯機的程式碼,就像是很怕人(也包括作者自己)一眼就看懂的樣子,於是把文字給**加密**了。如果不想讓自己的程式碼需要翻譯機,就要學習怎麼別將文字加密。說來有趣,人們寫程式竟然直覺是寫出加密的程式碼,反倒是要學習技巧來讓程式碼不要加密。 下面提供一些參考方法,以避免寫出加密的程式碼。 ## 方法1:避免魔術數字 **魔術數字(Magic Number)**,是指程式碼中難以辨識其物理意義的數字。我們用[B0001 易讀程式碼的特性:白話](/bdQxzTadSP-HIfXlQZHxLg)出現過的例子說明魔術數字的概念。 ```c++ if (counter_green < 4) { ++counter_green; return 1; } ``` 這段程式碼的`4`與`1`是無法直接辨識其物理意義的,他們就是魔術數字。 該例子用了2個手段處理: * 使用額外 Variable 來儲存數值,透過**有意義的 Variable 名稱**來明確表示該數字所代表的意義。 * 使用`enum`的 Symbol 替換返回用的數值,透過**有意義的 Symbol 名稱**來明確表示該數字所代表的意義。 所以可以看到修改後的程式碼如下: ```c++ if (counter_green < duration_green) { ++counter_green; return light_green; } ``` 避免魔術數字的核心概念,在於用**有意義的名稱來取代無意義的數字**[^1];因此,只要能符合這個核心概念的手段,都是可行的。比方說: [^1]: Steve McConnell, _Code Complete: A Practical Handbook of Software Construction, Second Edition_, 2004 書中第11、12章有談到魔術數字與有意義命名等相關細節。 * 使用`#define`來定義 Symbol,透過**有意義的 Symbol 名稱**來明確表示該數字所代表的意義[^2]。 ```c++ #define duration_green 4 #define light_green 1 if (counter_green < duration_green) { ++counter_green; return light_green; } ``` [^2]: 針對這個案例使用 `#define` 是可行的手段,但如果沒其他考量的話,可能是較不好的選擇。這牽涉到`#define`更多的觀念,需另外再探討,就不在此文章發散討論。 * 使用 Function 來返回常數,透過**有意義的 Function 名稱**來明確表示該數字所代表的意義[^3]。 [^3]: 一般而言,Function 會是比`enum`或常數更厲害的工具,但這個案例如果沒其他考量的話,不需要做到這麼複雜。這牽涉到 Function 更多的觀念,需另外再探討,就不在此文章發散討論。 ```c++ static inline int duration_green() { return 4; } static inline int light_green() { return 1; } if (counter_green < duration_green()) { ++counter_green; return light_green(); } ``` ## 方法2:新增 Variable 以代換 if 判斷式 在閱讀程式碼的時候,很容易遇到「不確定`if`條件是要判斷什麼事情」的情況;倒不是判斷式裡出現魔術數字導致不容易判斷,而是這個判斷的意義可能不好解讀。 為了讓人更容易了解`if`做了什麼事情,有的人會利用註解做說明。舉例來說: ```c++ if (p1_left > p2_right || p1_right < p2_left) { // segments are not overlapped ... } ``` 然而,在讓程式碼易讀的手段裡,加註解雖然是可行的,但常常是缺點相對較多的方式[^4];因此,對於要賦予無直接意義的`if`判斷式,建議可以使用 Variable。修改如下: ```c++ const bool notOverlapped = p1_left > p2_right || p1_right < p2_left; if (notOverlapped) { ... } ``` [^4]: Steve McConnell, _Code Complete: A Practical Handbook of Software Construction, Second Edition_, 2004 書中第32章有談到註解的問題。 這裡可以看到`notOverlapped`直接就表達了這個`if`所做的判斷意義,就不必額外寫註解了。藉由這個方法我們能理解到,透過**有意義的名稱來取代不容易直接理解的判斷式**,可以加強白話的特性。 不過這個方法是有好有壞的,建議在使用時可以判斷一下是否適用自己手邊的情境。 ### 優點:Variable 通常不會過時 這個優點是相對於註解而言。註解畢竟不是直接的程式碼,所以當時間久了,判斷式被調整了或意義改變了,註解很可能不會被同時改寫,這樣註解就**過時**了。而 Variable 通常不會有這樣的問題,因為一旦判斷式被調整了,對應的程式碼都可能跟著變動,用來代表意義的 Variable 很難不跟著調整名稱,否則程式碼就不好維護了。所以說,**Variable 通常不會過時**[^5]。 [^5]: 這裡用「通常」,是因為有些人的撰碼習慣不好;即使變數的意義變了,也不去調整變數名稱,甚至對相關程式碼的邏輯也不重新整理過,也就是會出現名不符實的程式碼。這觀念會在未來的文章 _C0003 不直觀的程式碼:你說的黑不是黑_ 討論。 ### 優點:Variable 可以複用 身為 Variable。如果有一個以上的`if`都要做相同判斷的話,就可以直接利用這個 Variable,而不用重複寫判斷式。不重複寫除了容易維護以外,該判斷式不用被重複運算也是另一個好處。 ### 缺點:可能有些微效能問題 如果有2層以上的 Variable 加入,可能會讓判斷式運算無法提早完成。 舉例來說,如果將案例改寫如下: ```c++ const bool p1_at_right = p1_left > p2_right; const bool p2_at_right = p1_right < p2_left; const bool notOverlapped = p1_at_right || p2_at_right; if (notOverlapped) { ... } ``` 從改寫後的程式碼可以看到,2層以上的 Variable 是可以提升白話程度,但這也表示`p1_left > p2_right`與`p1_right < p2_left`都必須運算過之後,才能組合出`notOverlapped`;而原本1層 Variable 的作法,在遇到`p1_left > p2_right`是`True`的時候,就不會再運算`p1_right < p2_left`了。 雖然存在運算效能問題,但一般來說,除非這個判斷式是在許多層的 Loop 裡運作、或者 Variable 的運算是高複雜度的,否則這樣的些微效能損失通常可以接受,以換取更好的程式碼易讀性。 ## 方法3:打包行為 在上面提到如何避免魔術數字的手段中,有利用 Function 返回常數值的方式來讓程式碼更白話。這個觀念其實可以從單一個數值的處理,擴展到一整段的程式碼。具體而言,是透過**有意義的 Function 名稱來明確表示一段程式碼所代表的意義**;而實作上,就是將一段程式碼行為打包到 Function 中,然後呼叫 Function 來執行被打包的程式碼。 我們用這個概念改寫[[B0001 易讀程式碼的特性:白話]]的例子。請參考調整後的程式碼。 ```c++ static inline bool process_light(int& counter, const int duration) { const bool ret = counter < duration; if (ret) { ++counter; } return ret; } static inline void reset_light(int& counter_green, int& counter_yellow, int& counter_red) { counter_green = 1; counter_yellow = 0; counter_red = 0; } static LightId getLight_4() { const int duration_green = 4; const int duration_yellow = 1; const int duration_red = 5; static int counter_green = 0; static int counter_yellow = 0; static int counter_red = 0; if (process_light(counter_green, duration_green)) { return light_green; } else if (process_light(counter_yellow, duration_yellow)) { return light_yellow; } else if (process_light(counter_red, duration_red)) { return light_red; } else { reset_light(counter_green, counter_yellow, counter_red); return light_green; } } ``` 在調整後的程式碼中,我們可以明確看到數個`if-else`區塊都被呼叫 Function 所取代;而 Function 名稱與參數的組合,恰恰明確告知了該區塊正在進行的工作。仔細來說,我們不用再透過細讀程式碼以判斷前3個`if`區塊分別是屬於3個燈號的處理、及最後區塊是屬於重設狀態以進行下一輪的綠燈、黃燈、紅燈循環[^6]。 [^6]: 這個修改僅展示如何將行為打包,而不是完善這段程式碼,所以會看到 Variable 的存取位置不合理、Function 參數設計有權責疑慮等問題。為專注在本篇文章的重點,這些問題就不在此發散討論。 要注意的是,很多時候使用 Function 的目的在於複用,但不要被這個觀念給綁死了。對於不需要複用的程式碼,一樣也可以用 Function 打包的。這裡的核心觀念,是透過**有意義的 Function 名稱來明確表示該段程式碼所代表的意義**,複用與不複用都與這個核心觀念無關。所以在這個例子中,為了可以獲得更白話的程式碼,可以看到`reset_light`即使只被一個地方呼叫,仍然要打包到 Function 中[^7]。 [^7]: 「打包行為」不只是可以讓程式碼更白話,也是抽象化(Abstraction)、封裝(Encapsulation)等技術的重要基礎觀念。請特別注意這個觀念,未來文章探討會很常出現。 ## 見山不是山 所以,是不是看到常數、看到`if`判斷式沒有用 Variable、看到沒打包的程式碼,就要修正呢?答案是否定的,要小心掉入**見山不是山**[^8]的陷阱裡。 [^8]: [A0002 見山是山,見山不是山,見山只是山](/xiinN0T5QQ2a_V_rCFhavg) 請先複習一下本篇討論過的核心觀念: * 用**有意義的名稱**來取代無意義的數字 * 用**有意義的名稱**來取代不容易直接理解的判斷式 * 用**有意義的名稱**來取代一段程式碼所代表的意義 這些觀念是可以換句話說的: * 有意義的數字不用取代 * 能直接理解的判斷式不用取代 * 清楚意義的程式碼不用取代 根據這樣的換句話說,可以了解到像下面這樣的程式碼就**不適合**再做加強白話特性的處置: ```c++ int sum = 0; for(int i=0; i<size; ++i) { sum += value[i]; } if(sum > limit) { sum = limit; } ``` 這個案例中,已經一目了然地看到了3段概念: * 加總數值歸零 * 對所有數值加總 * 加總超過上限則只保留到上限 對這麼清楚概念的程式碼還要做處理,可能就會變這樣: ```c++ const int zero = 0; int sum = zero; //method 1 add_to(value, size, sum); //method 3 const bool hit_limit = sum > limit; //method 2 if(hit_limit) { sum = limit; } ``` 是不是覺得這樣很畫蛇添足呢?雖然讓程式碼更白話是重要的,但在不合適的情況下硬是要調整,反而更不易讀了。見山不是山,不可不慎!

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully