源代碼排版

2018-02-24 16:10 更新

所有風(fēng)格都又丑又難讀,自己的除外。幾乎人人都這樣想。把“自己的除外”拿掉,他們或許是對的...
——Jerry Coffin(論縮排)

  • 使用?UTF-8?作為源文件的編碼。
  • 每個(gè)縮排層級使用兩個(gè)空格。不要使用硬 tab。

    # 差 - 四個(gè)空格
    def some_method
        do_something
    end
    
    # 好
    def some_method
      do_something
    end
  • 使用 Unix 風(fēng)格的換行符。(BSD/Solaris/Linux/OSX 的用戶不用擔(dān)心,Windows 用戶要格外小心。)

    • 如果你使用 Git ,可用下面這個(gè)配置,來保護(hù)你的項(xiàng)目不被 Windows 的換行符干擾:
    $ git config --global core.autocrlf true
  • 不使用?;?隔開語句和表達(dá)式。推論——一行一條語句。

    # 差
    puts 'foobar'; # 不必要的分號
    
    puts 'foo'; puts 'bar' # 一行里有兩個(gè)表達(dá)式
    
    # 好
    puts 'foobar'
    
    puts 'foo'
    puts 'bar'
    
    puts 'foo', 'bar' # 僅對 puts 適用
  • 對于沒有成員的類,盡可能使用單行類定義。

    # 差
    class FooError < StandardError
    end
    
    # 勉強(qiáng)可以
    class FooError < StandardError; end
    
    # 好
    FooError = Class.new(StandardError)
  • 定義方法時(shí)避免單行寫法。盡管還是有些人喜歡這么用的。但是單行定義很容易出錯(cuò),因?yàn)樗谡Z法上有些古怪。無論如何——一個(gè)單行方法里的表達(dá)式不應(yīng)該多于 1 個(gè)。

    # 差
    def too_much; something; something_else; end
    
    # 勉強(qiáng)可以——注意第一個(gè) ; 是必需的
    def no_braces_method; body end
    
    # 勉強(qiáng)可以——注意第二個(gè) ; 是可選的
    def no_braces_method; body; end
    
    # 勉強(qiáng)可以——語法上正確,但是沒有 ; 讓它有些難讀
    def some_method() body end
    
    # 好
    def some_method
      body
    end

    這個(gè)規(guī)則的一個(gè)例外是空方法。

    # 好
    def no_op; end
  • 操作符前后的空格。在逗號?,?、冒號?:?及分號?;?之后,在?{?前后,在?}?之前。 Ruby 解釋器(大部分情況下)忽略空格。但要寫出可讀性高的代碼,正確使用空格是關(guān)鍵。

      sum = 1 + 2
      a, b = 1, 2
      1 > 2 ? true : false; puts 'Hi'
      [1, 2, 3].each { |e| puts e }

    (針對操作符)唯一的例外是當(dāng)使用指數(shù)操作符時(shí):

      # 差
      e = M * c ** 2
    
      # 好
      e = M * c**2

    {?和?}?需要額外說明,因?yàn)樗麄兪怯迷趬K(block)、 哈希字面量(hash literals),以及字符串插值中。 對于哈希字面量來說,兩種風(fēng)格都是可接受的。

      # 好——`{` 之后和 `}` 之前有空格
      { one: 1, two: 2 }
    
      # 好——`{` 之后和 `}` 之前沒有空格
      {one: 1, two: 2}

    第一個(gè)種風(fēng)格稍微更具可讀性(而且有爭議的是,一般在 Ruby 社區(qū)里更受歡迎)。 第二種風(fēng)格具有可為塊和哈希字面量添加可視化的差別的優(yōu)點(diǎn)。 無論你選哪一種都行——但是最好保持一致。

  • (?、?[?之后,?]?、?)?之前,不要有空格。

    some(arg).other
    [1, 2, 3].length
  • !?后不要有空格。

    # 差
    ! something
    
    # 好
    !something
  • 范圍表達(dá)式中間不要有空格。

    # 差
    1 .. 3
    'a' ... 'z'
    
    # 好
    1..3
    'a'...'z'
  • 把?when?跟?case?縮排在同一層。我知道很多人不同意這一點(diǎn),但這是《The Ruby Programming Language》及《Programming Ruby》所使用的風(fēng)格。

    # 差
    case
      when song.name == 'Misty'
        puts 'Not again!'
      when song.duration > 120
        puts 'Too long!'
      when Time.now.hour > 21
        puts "It's too late"
      else
        song.play
    end
    
    # 好
    case
    when song.name == 'Misty'
      puts 'Not again!'
    when song.duration > 120
      puts 'Too long!'
    when Time.now.hour > 21
      puts "It's too late"
    else
      song.play
    end
  • 當(dāng)賦值一個(gè)條件表達(dá)式的結(jié)果給一個(gè)變量時(shí),保持分支的縮排在同一層。

    # 差 - 非常復(fù)雜
    kind = case year
    when 1850..1889 then 'Blues'
    when 1890..1909 then 'Ragtime'
    when 1910..1929 then 'New Orleans Jazz'
    when 1930..1939 then 'Swing'
    when 1940..1950 then 'Bebop'
    else 'Jazz'
    end
    
    result = if some_cond
      calc_something
    else
      calc_something_else
    end
    
    # 好 - 結(jié)構(gòu)很清晰
    kind = case year
           when 1850..1889 then 'Blues'
           when 1890..1909 then 'Ragtime'
           when 1910..1929 then 'New Orleans Jazz'
           when 1930..1939 then 'Swing'
           when 1940..1950 then 'Bebop'
           else 'Jazz'
           end
    
    result = if some_cond
               calc_something
             else
               calc_something_else
             end
    
    # 好 ( 避免代碼讓行寬過長 )
    kind =
      case year
      when 1850..1889 then 'Blues'
      when 1890..1909 then 'Ragtime'
      when 1910..1929 then 'New Orleans Jazz'
      when 1930..1939 then 'Swing'
      when 1940..1950 then 'Bebop'
      else 'Jazz'
      end
    
    result =
      if some_cond
        calc_something
      else
        calc_something_else
      end
  • 在?def?之間使用空行,并且用空行把方法分成合乎邏輯的段落。

    def some_method
      data = initialize(options)
    
      data.manipulate!
    
      data.result
    end
    
    def some_method
      result
    end
  • 函數(shù)最后一個(gè)參數(shù)后面不要加逗號,特別是每個(gè)參數(shù)單獨(dú)一樣的時(shí)候

    # 差 - 雖然移動和增刪參數(shù)的時(shí)候會很簡單,但仍不推薦
    some_method(
                 size,
                 count,
                 color,
               )
    
    # 差
    some_method(size, count, color, )
    
    # 好
    some_method(size, count, color)
  • 當(dāng)給方法的參數(shù)賦默認(rèn)值時(shí),在?=?兩邊使用空格:

    # 差
    def some_method(arg1=:default, arg2=nil, arg3=[])
      # 做一些任務(wù)...
    end
    
    # 好
    def some_method(arg1 = :default, arg2 = nil, arg3 = [])
      # 做一些任務(wù)...
    end

    雖然幾本 Ruby 書建議用第一個(gè)風(fēng)格,不過第二個(gè)風(fēng)格在實(shí)踐中更為常見(并可爭議地可讀性更高一點(diǎn))。

  • 避免在不需要的時(shí)候使用行繼續(xù)符?\?。實(shí)際編碼時(shí),除非用于連接字符串, 否則避免在任何情況下使用行繼續(xù)符。

    # 差
    result = 1 - \
             2
    
    # 好 (但是仍然丑到爆)
    result = 1 \
             - 2
    
    long_string = 'First part of the long string' \
                  ' and second part of the long string'
  • 使用鏈?zhǔn)椒椒〞r(shí)風(fēng)格統(tǒng)一。社區(qū)認(rèn)為前引點(diǎn)號和末端點(diǎn)號都是好的風(fēng)格。

    • (可選 A)和當(dāng)一個(gè)鏈?zhǔn)椒椒ㄕ{(diào)用需要在另一行繼續(xù)時(shí),將?.?放在第二行。
    # 差 - 為了理解第二行需要去查閱第一行
    one.two.three.
      four
    
    # 好 - 第二行在做什么立刻變得很清晰
    one.two.three
      .four
    • (可選 B)末尾用點(diǎn)號表示表達(dá)式?jīng)]有結(jié)束
    # 差 - 需要讀到第二行才能確定表達(dá)式?jīng)]有結(jié)束
    one.two.three
      .four
    
    # 好 - 從第一行就可以立即明白表達(dá)式?jīng)]有結(jié)束
    one.two.three.
      four

兩種方法各自優(yōu)點(diǎn)參閱這里。

  • 方法參數(shù)過長時(shí),將它對齊排列在多行。當(dāng)對齊的參數(shù)由于線寬不適合對齊時(shí), 簡單的在第一行之后縮進(jìn)也是可以接受的。

    # 初始(行太長了)
    def send_mail(source)
      Mailer.deliver(to: 'bob@example.com', from: 'us@example.com', subject: 'Important message', body: source.text)
    end
    
    # 差(兩倍縮排)
    def send_mail(source)
      Mailer.deliver(
          to: 'bob@example.com',
          from: 'us@example.com',
          subject: 'Important message',
          body: source.text)
    end
    
    # 好
    def send_mail(source)
      Mailer.deliver(to: 'bob@example.com',
                     from: 'us@example.com',
                     subject: 'Important message',
                     body: source.text)
    end
    
    # 好(普通縮排)
    def send_mail(source)
      Mailer.deliver(
        to: 'bob@example.com',
        from: 'us@example.com',
        subject: 'Important message',
        body: source.text)
    end
  • 用字面量構(gòu)建數(shù)組時(shí),如果跨行,應(yīng)對齊。

    # 差 - 未對齊
    menu_item = ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam',
      'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam']
    
    # 好
    menu_item = [
      'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam',
      'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam'
    ]
    
    # 好
    menu_item =
      ['Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam',
       'Baked beans', 'Spam', 'Spam', 'Spam', 'Spam', 'Spam']
  • 大數(shù)字添加下劃線來改善可讀性。

    # 差 - 有幾個(gè)零?
    num = 1000000
    
    # 好 - 更容易被人腦解析。
    num = 1_000_000
  • 使用 RDoc 以及它的慣例來撰寫 API 文檔。注解區(qū)塊及?def?不要用空行隔開。

  • 每一行限制在 80 個(gè)字符內(nèi)。
  • 避免行尾空格。
  • 文件以空白行結(jié)尾。
  • 不要使用區(qū)塊注釋。它們不能由空白引導(dǎo)(=begin?必須頂頭開始),并且不如普通注釋容易辨認(rèn)。

    # 差
    = begin
    一行注釋
    另一行注釋
    = end
    
    # 好
    # 一行注釋
    # 另一行注釋
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號