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
    # B0002 易讀程式碼的特性:直觀 ## 直接,不要省略 關於程式碼是否易讀,除了**白話**之外,**「直觀」** 也是很重要的特性。所謂的**直觀**,就是程式碼的字面邏輯要符合直覺,想做的事情都直接表達,不要省略。 ### 忍不住要問為什麼 請嘗試閱讀以下敘述,感受一下文字表達是否足夠直觀。 _敘述組1_: * 方向盤向右轉讓汽車向左轉。 * 按2次向上音量鍵,才讓音量增加。 * 跑道起跑線後退2公尺為起跑點。 * 輪值的第2輪排班從第5個人開始。 相信在看到這些敘述後,心理肯定是忍不住要問 **「為什麼要這樣做」** 吧?這些敘述都很不直覺,閱讀起來很莫名其妙。對於無法理解原因的敘述,是無法看懂的;如果程式碼所呈現的邏輯就像這類的敘述,即使已經寫得很白話了,易讀性還是不佳。 ### 直覺就不用問為什麼 看完 _敘述組1_ ,可以對比一下另一組類似內容的敘述,如下。 _敘述組2_: * 方向盤向右轉讓汽車向右轉。 * 按1次向上音量鍵,讓音量增加。 * 跑道起跑線為起跑點。 * 輪值每一輪的排班都是所有人列入排班。 是不是覺得 _敘述組2_ 既自然又直覺呢?想必一般人看到這樣的敘述,都能輕易理解敘述所代表的邏輯,不會去問為什麼要這樣做。如果程式碼所表達的邏輯都不需要再問為什麼,那麼就能建立很好的易讀性。 ### 填補因果關係 然而,_敘述組1_ 與 _敘述組2_ 畢竟還是代表了不同的程式碼行為,這樣的比較略為不公平。倘若一定要完成 _敘述組1_ 想要達成的行為,要如何描述會比較好呢?答案不難,只要將**因果關係**明確呈現即可。 請再嘗試閱讀下面填補**因果關係**後的新敘述,感受一下文字表達是否足夠直觀。 _敘述組3_: * **由於事故當中判斷右轉有危險**,因此即使方向盤向右轉,也要讓汽車向左轉。 * **由於架構限制,1次按鍵只能對應一個反應,而在選單還未跳出時,按音量鍵會先讓選單跳出**,因此按2次向上音量鍵,才讓音量增加。 * **由於要從排位賽成績調整起跑點,因此排位賽第2名的**跑道起跑線後退2公尺為起跑點。 * **由於職員表前4位為業務較多的主管,每月的輪值只需參與1次,因此**輪值的第2輪排班從第5個人開始。 在 _敘述組3_ 裡可以發現,_敘述組1_ 的主要敘述沒有更動,僅在前面填補原因。當原因有清楚明白地提供出來,讀起來就不會覺得莫名其妙。如果程式碼能明確表達因果關係,讓邏輯脈絡鉅細靡遺地呈現,易讀性就能因直觀而大大地提升。這就是**直觀**的核心概念。 ## 邏輯翻譯機 如果說不夠白話的程式碼需要**字面翻譯機**[^1],那不直觀的程式碼就需要**邏輯翻譯機**。仔細來說,不直觀的程式碼會有一個背後理由,而這個理由沒有在程式碼中明確表達出來。因為不明確表達,導致看到相關的程式碼,都必須在腦中把這個理由補進來,才能讀懂邏輯。這就是**邏輯翻譯機**。 [^1]: [B0001 易讀程式碼的特性:白話](/bdQxzTadSP-HIfXlQZHxLg) 一樣都是進行翻譯,但**邏輯翻譯機**其實比字面翻譯機更難以駕馭。主要的理由有3個面向:**複雜**的對應關係、**難以反推**的對應關係、與**多層次**的對應關係。 ### 複雜的對應關係 一般而言,字面翻譯機通常是簡單的對應關係,像是數字1、2、3分別代表綠燈、黃燈、紅燈,或者數字 4、1、5分別代表綠燈、黃燈、紅燈維持的秒數,對於腦力、記憶力超好的人來說,還是可處理的範圍。然而,**邏輯翻譯機**往往是複雜的對應關係,從「職員表前4位為業務較多的主管,每月的輪值只需參與1次」這樣複雜的理由可以看出,單一個翻譯機對作者或讀者的負擔之重,不是一個字面翻譯機能比擬的。 ### 難以反推的對應關係 由於字面翻譯機是簡單的對應關係,對經驗豐富的程式設計師人員來說,遇到需要字面翻譯機的情況,是有機會不需要原作者說明,就猜到對應關係。相反地,邏輯翻譯機的**對應關係幾乎不可能直接猜到**。回顧前面例子,應該是沒人能透過 _敘述組1_ 直接猜到 _敘述組3_ 的吧?! ### 多層次的對應關係 基本上,字面翻譯機就是一對一的對應關係,像「1代表綠燈」就是一個數字對應一個意義,不太會有另一層理由去決定這個1;而邏輯翻譯機的對應關係可能是一連串因果關係所建立的。一樣拿 _敘述組3_ 的例子來說,「職員表前4位為業務較多的主管」可能是因為「職員表原本就按照員工編號由小到大排列,而且小編號為主管」,甚至員工編號也可能不是真實編號,而是「系統基於某個設計給予每個員工編號,方便做處理」。換句話說,當我們看到「輪值的第2輪排班從第5個人開始」這樣的程式碼,背後所代表的程式碼意義應包括「系統的員工編號規則」、「職員表的排列方式」等一連串的因果關係,最終才是看到的排班程式碼。 如果把每一個細節分拆清楚,可以建立如下圖**多層次的對應關係**。從關係圖可以看到,需要清楚編號、規則、排序、知識、考量等直接或間接的因果關係,才能完成最終實作。如果程式碼不將這些概念清楚明白地表達出來,可見需要多少腦力才能記清楚這麼多細節。 ```mermaid graph TD; ENUM["編號:系統給予員工編號"] RULE["規則:編號先派給主管,再派給基層員工"]; SORT["排序:職員表照編號由小到大排列"]; KNOW["知識:主管有4位"]; CONC["考量:主管業務較多,只輪值1次"]; IMPL["實作:輪值的第2輪排班從第5個人開始"]; ENUM --> SORT; RULE --> SORT; SORT --> IMPL; KNOW --> IMPL; CONC --> IMPL; ``` ## 不直觀的原因 對於有經驗的程式設計人員,在學習到要避免的問題後,像是程式碼壞氣味[^2],通常會設法去避免。然而,即使已經知道**不直觀**的程式碼相當不易讀、會對腦力造成很大的負擔,卻還是不自覺地寫出了這樣的程式碼。人們會很自然地完成工作流程,包括完成程式碼、進行 Debug、通過測試、量產、維護等等,卻沒有發覺自己在處理程式碼的過程中,消耗了大量的腦力在做邏輯翻譯。 [^2]: 提到程式碼壞氣味的資料、文獻有許多,這裡僅提供一篇不錯的整理文章供作參考。未來會另外撰文探討壞氣味。 https://carger.tips/%E9%87%8D%E6%A7%8B-refactoring-%E5%AD%B8%E7%BF%92%E5%BF%83%E5%BE%97%E7%AD%86%E8%A8%98-%E5%A3%9E%E5%91%B3%E9%81%93-bad-smell-code-smell 為什麼會這樣呢?可能要歸咎於**不寫原因的慣性**與**不易檢查的盲點**。 ### 不寫原因的慣性 試想一下,我們是不是都有這樣的慣性:「經過思考並設計好一個方案後,直覺地將方案的結論撰寫出來,而忘了把原因寫清楚」呢?拿輪值的例子來說,在思考過編號、規則、排序、知識、考量等狀況後,想出了「輪值的第2輪排班從第5個人開始」的解決方案;接著,很自然地把解決方案寫完之後,就沒有然後了。如果沒有回頭檢查是否有漏了什麼,一旦程式碼運作正常,應該很難察覺自己沒有把原因寫進去吧! ### 不易檢查的盲點 就算有回頭檢查好了,其實也很容易有**盲點**。透過對比程式碼壞氣味,大概可以看出為什麼會有**盲點**。 程式碼壞氣味,像是參數太多、太多行、霰彈修改、耦合性太高等等,都是直接現象、明確指標,這些就像是線索,可以通過檢查是否存在線索,然後對程式碼進行改進。可是程式碼**不直觀**的關鍵在於沒有將**原因**明確地寫進程式碼裡,而**原因**本身是邏輯推演的過程,不是一種線索,也就很難直接被檢查出來。更何況,作者在撰寫的當下,是帶著邏輯翻譯機在敲程式碼的,會把原因當成理所當然,就不覺得要再補充什麼,這就是**盲點**。 ## 如何往直觀程式碼邁進呢? 在知道不直觀的原因之後,就應該思考怎麼解決。 首先,要破除**不寫原因的慣性**,就需要先養成**回頭檢查**的習慣。所謂回頭檢查,是指別讓自己陷在當下撰寫的一小塊程式碼裡,跳脫到外層看一下程式碼的流程、順一下邏輯,釐清是否有不通順的地方,再設法將邏輯補齊。要知道,程式碼本身就是在處理邏輯,而不通順的地方很可能是邏輯漏洞;也就是說,**回頭檢查**不只可以把邏輯補完整,甚至有機會早期就排除邏輯漏洞有關的 Bug。 接著,要排除**不易檢查的盲點**,一樣也是要透過**回頭檢查**的習慣,特別是**過一段時間後**再回頭檢查。理由是,一般撰寫程式碼時,對於細節的記憶力通常很短,過一段時間回頭看程式碼就像是別人寫的了[^3]。這麼做等於是忘了自己原本已知的**邏輯翻譯機**,就不會有理所當然的**盲點**了。當然,不排除有人記憶力超強,任何程式碼細節都記得清清楚楚,沒法徹底忘掉已知的邏輯翻譯機;這時就只能藉由程式碼審查(Code Review)找人幫忙了。由於審查者不是作者本人,本來就不清楚實作的前因後果,不會有**邏輯翻譯機**有沒有忘記的問題。透過別人的眼睛與腦袋,就更容易排除盲點了! [^3]: Eagleson's Law 是這麼說的:「自己寫的程式碼超過6個月沒看的話,就像是別人寫的一樣」。英文原文為 "Any code of your own that you haven't looked at for six or more months might as well have been written by someone else." 總而言之,程式碼要直觀,就是要不斷問為什麼要這樣寫,只要還能問出為什麼,就把原因補進程式碼裡。如果自己已經覺得沒問題了,可以再找其他人幫忙,確保沒有盲點。假以時日練習,就能把清楚寫出邏輯的動作變成習慣,寫出的程式碼就都能直觀了! ## 延伸閱讀 [[C0002 不直觀的程式碼:反應遲鈍]] (撰寫中) [[C0003 不直觀的程式碼:你說的黑不是黑]] (撰寫中) [[C0004 不直觀的程式碼:逆向行駛]] (撰寫中) [[C0005 不直觀的程式碼:就是要特權]] (撰寫中)

    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