編程規(guī)范-ruby中我不熟識的

今天review了一次ruby編程規(guī)范,把一些之前我不熟識的在這里記錄下來购公。

  • 對于沒有主體的類萌京,傾向使用單行定義。 [link]
# 差
class FooError < StandardError
end

# 勉強(qiáng)可以
class FooError < StandardError; end

# 好
FooError = Class.new(StandardError)
  • 把 when與 case縮排在同一層級宏浩。這是《Programming Ruby》與《The Ruby Programming Language》中早已確立的風(fēng)格知残。 [link]
# 差
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)將一個條件表達(dá)式的結(jié)果賦值給一個變量時,保持分支縮排在同一層級比庄。 [link]
# 差 - 非常費(fèi)解
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
  • 使用 _ 語法改善大數(shù)的數(shù)值字面量的可讀性求妹。 [link]
# 差 - 有幾個零乏盐?
num = 1000000

# 好 - 方便人腦理解
num = 1_000_000
  • 使用 &&= 預(yù)先檢查變量是否存在,如果存在制恍,則做相應(yīng)動作父能。使用 &&= 語法可以省去 if檢查。 [link]
# 差
if something
  something = something.downcase
end

# 差
something = something ? something.downcase : nil

# 勉強(qiáng)可以
something = something.downcase if something

# 好
something = something && something.downcase

# 更好
something &&= something.downcase
  • 未被使用的區(qū)塊參數(shù)或局部變量净神,添加 _ 前綴或直接使用 _(盡管表意性略差)何吝。這種做法可以抑制 Ruby 解釋器或 RuboCop 等工具發(fā)出“變量尚未使用”的警告。 [link]
# 差
result = hash.map { |k, v| v + 1 }

def something(x)
  unused_var, used_var = something_else(x)
  # ...
end

# 好
result = hash.map { |_k, v| v + 1 }

def something(x)
  _unused_var, used_var = something_else(x)
  # ...
end

# 好
result = hash.map { |_, v| v + 1 }

def something(x)
  _, used_var = something_else(x)
  # ...
end
  • 使用 $stdout/$stderr/$stdin而不是 STDOUT/STDERR/STDIN鹃唯。STDOUT/STDERR/STDIN是常量爱榕,盡管在 Ruby 中允許給常量重新賦值(可能是重定向某些流),但解釋器會發(fā)出警告坡慌。 [link]

  • 傾向使用 sprintf或其別名 format而不是相當(dāng)晦澀的 String#% 方法黔酥。 [link]

# 差
'%d %d' % [20, 10]
# => '20 10'

# 好
sprintf('%d %d', 20, 10)
# => '20 10'

# 好
sprintf('%{first} %{second}', first: 20, second: 10)
# => '20 10'

format('%d %d', 20, 10)
# => '20 10'

# 好
format('%{first} %{second}', first: 20, second: 10)
# => '20 10'
  • 當(dāng)你希望處理的變量類型是數(shù)組,但不太確定其是否真的是數(shù)組時洪橘,通過使用 [*var] 或 Array()來替代顯式的數(shù)組類型檢查與轉(zhuǎn)換跪者。 [link]
# 差
paths = [paths] unless paths.is_a? Array
paths.each { |path| do_something(path) }

# 好
[*paths].each { |path| do_something(path) }

# 好 - 并且更具可讀性
Array(paths).each { |path| do_something(path) }
  • 通過使用范圍或 Comparable#between?來替代復(fù)雜的比較邏輯。 [link]
# 差
do_something if x >= 1000 && x <= 2000

# 好
do_something if (1000..2000).include?(x)

# 好
do_something if x.between?(1000, 2000)
  • 傾向使用 flat_map而不是 map + flatten的組合熄求。此規(guī)則并不適用于深度超過 2 層的數(shù)組坑夯。舉例來說,如果 users.first.songs == ['a', ['b','c']]成立抡四,則使用 map + flatten的組合而不是 flat_map。flat_map只能平坦化一個層級仗谆,而 flatten能夠平坦化任意多個層級指巡。 [link]
# 差
all_songs = users.map(&:songs).flatten.uniq

# 好
all_songs = users.flat_map(&:songs).uniq
  • 傾向使用 reverse_each 而不是 reverse.each隶垮,因?yàn)槟承┗烊?Enumerable 模塊的類可能會提供 reverse_each 的高效版本藻雪。即使這些類沒有提供專門特化的版本,繼承自 Enumerable 的通用版本至少能保證性能與 reverse.each 相當(dāng)威始。 [link]
# 差
array.reverse.each { ... }

# 好
array.reverse_each { ... }

關(guān)于注釋的一些說明

  • 使用 TODO 標(biāo)記應(yīng)當(dāng)加入的特征與功能木西。 [link]

  • 使用 FIXME 標(biāo)記需要修復(fù)的代碼叼丑。 [link]

  • 使用 OPTIMISE 標(biāo)記可能引發(fā)性能問題的低效代碼星立。 [link]

  • 使用 HACK 標(biāo)記代碼異味胧沫,即那些應(yīng)當(dāng)被重構(gòu)的可疑編碼習(xí)慣南蹂。 [link]

  • 使用 REVIEW 標(biāo)記需要確認(rèn)與編碼意圖是否一致的可疑代碼疗疟。比如,REVIEW: Are we sure this is how the client does X currently?霜第。 [link]

類定義

  • 在類定義中刃榨,使用一致的結(jié)構(gòu)枢希。 [link]
class Person
  # 首先是 extend 與 include
  extend SomeModule
  include AnotherModule

  # 內(nèi)部類
  CustomErrorKlass = Class.new(StandardError)

  # 接著是常量
  SOME_CONSTANT = 20

  # 接下來是屬性宏
  attr_reader :name

  # 跟著是其他宏(如果有的話)
  validates :name

  # 公開的類方法接在下一行
  def self.some_method
  end

  # 初始化方法在類方法和實(shí)例方法之間
  def initialize
  end

  # 跟著是公開的實(shí)例方法
  def some_method
  end

  # 受保護(hù)及私有的方法等放在接近結(jié)尾的地方
  protected

  def some_protected_method
  end

  private

  def some_private_method
  end
end
  • 如果嵌套類數(shù)目較多桌吃,進(jìn)而導(dǎo)致外圍類定義較長,則將它們從外圍類中提取出來苞轿,分別放置在單獨(dú)的以嵌套類命名的文件中茅诱,并將文件歸類至以外圍類命名的文件夾下。 [link]
# 差

# foo.rb
class Foo
  class Bar
    # 定義 30 多個方法
  end

  class Car
    # 定義 20 多個方法
  end

  # 定義 30 多個方法
end

# 好

# foo.rb
class Foo
  # 定義 30 多個方法
end

# foo/bar.rb
class Foo
  class Bar
    # 定義 30 多個方法
  end
end

# foo/car.rb
class Foo
  class Car
    # 定義 20 多個方法
  end
end
  • 定義只有類方法的數(shù)據(jù)類型時搬卒,傾向使用模塊而不是類让簿。只有當(dāng)需要實(shí)例化時才使用類。 [link]
# 差
class SomeClass
  def self.some_method
    # 省略主體
  end

  def self.some_other_method
    # 省略主體
  end
end

# 好
module SomeModule
  module_function

  def some_method
    # 省略主體
  end

  def some_other_method
    # 省略主體
  end
end
  • 當(dāng)你想將模塊的實(shí)例方法變成類方法時秀睛,傾向使用 module_function而不是 extend self。 [link]
# 差
module Utilities
  extend self

  def parse_something(string)
    # 做一些事情
  end

  def other_utility_method(number, string)
    # 做一些事情
  end
end

# 好
module Utilities
  module_function

  def parse_something(string)
    # 做一些事情
  end

  def other_utility_method(number, string)
    # 做一些事情
  end
end
  • 優(yōu)先考慮通過工廠方法的方式創(chuàng)建某些具有特定意義的實(shí)例對象莲祸。 [link]
class Person
  def self.create(options_hash)
    # 省略主體
  end
end
  • 盡可能隱式地使用 begin/rescue/ensure/end區(qū)塊蹂安。 [link]
# 差
def foo
  begin
    # 主邏輯
  rescue
    # 異常處理邏輯
  end
end

# 好
def foo
  # 主邏輯
rescue
  # 異常處理邏輯
end
  • 當(dāng)創(chuàng)建一組元素為單詞(沒有空格或特殊字符)的數(shù)組時,傾向使用 %w而不是 []锐帜。此規(guī)則只適用于數(shù)組元素有兩個或以上的時候田盈。 [link]
# 差
STATES = ['draft', 'open', 'closed']

# 好
STATES = %w(draft open closed)
  • 當(dāng)創(chuàng)建一組符號類型的數(shù)組(且不需要保持 Ruby 1.9 兼容性)時,傾向使用 %i 缴阎。此規(guī)則只適用于數(shù)組元素有兩個或以上的時候允瞧。 [link]
# 差
STATES = [:draft, :open, :closed]

# 好
STATES = %i(draft open closed)
  • 當(dāng)訪問集合中的元素時,傾向使用對象所提供的方法進(jìn)行訪問,而不是直接調(diào)用對象屬性上的 [n]方法述暂。這種做法可以防止你在 nil 對象上調(diào)用 []痹升。 [link]
# 差
Regexp.last_match[1]

# 好
Regexp.last_match(1)
  • 當(dāng)你需要構(gòu)造巨大的數(shù)據(jù)塊時,避免使用 String#+畦韭,使用 String#<<來替代疼蛾。String#<<通過修改原對象進(jìn)行拼接工作,其比 String#+ 效率更高艺配,因?yàn)楹笳咝枰a(chǎn)生一堆新的字符串對象察郁。 [link]
# 差
html = ''
html += '<h1>Page title</h1>'

paragraphs.each do |paragraph|
  html += "<p>#{paragraph}</p>"
end

# 好 - 并且效率更高
html = ''
html << '<h1>Page title</h1>'

paragraphs.each do |paragraph|
  html << "<p>#{paragraph}</p>"
end
  • 當(dāng)存在更快速、更專業(yè)的替代方案時转唉,不要使用 String#gsub皮钠。 [link]
url = 'http://example.com'
str = 'lisp-case-rules'

# 差
url.gsub('http://', 'https://')
str.gsub('-', '_')

# 好
url.sub('http://', 'https://')
str.tr('-', '_')
  • 使用 Ruby 2.3 新增的 <<~ 操作符來縮排 heredocs 中的多行文本。 [link]
# 差 - 使用 Powerpack 程序庫的 String#strip_margin
code = <<-END.strip_margin('|')
  |def test
  |  some_method
  |  other_method
  |end
END

# 差
code = <<-END
def test
  some_method
  other_method
end
END

# 好
code = <<~END
  def test
    some_method
    other_method
  end
END
  • 當(dāng)你不需要分組結(jié)果時赠法,使用非捕獲組麦轰。 [link]
# 差
/(first|second)/

# 好
/(?:first|second)/
  • 避免使用 Perl 風(fēng)格的、用以代表最近的捕獲組的特殊變量(比如 $1期虾、$2 等)原朝。使用 Regexp.last_match(n) 來替代。[link]
/(regexp)/ =~ string
...

# 差
process $1

# 好
process Regexp.last_match(1)
  • 小心使用 ^ 與 $ 镶苞,它們匹配的是一行的開始與結(jié)束喳坠,而不是字符串的開始與結(jié)束。如果你想要匹配整個字符串茂蚓,使用 \A 與 \z壕鹉。(注意,\Z 實(shí)為 /\n?\z/) [link]
string = "some injection\nusername"
string[/^username$/]   # 匹配成功
string[/\Ausername\z/] # 匹配失敗
  • 對于復(fù)雜的正則表達(dá)式聋涨,使用 x 修飾符晾浴。這種做法不但可以提高可讀性,而且允許你加入必要的注釋牍白。注意的是脊凰,空白字符會被忽略。 [link]
regexp = /
  start         # some text
  \s            # white space char
  (group)       # first group
  (?:alt1|alt2) # some alternation
  end
/x
  • 只有當(dāng)字符串中同時存在插值與雙引號茂腥,且是單行時狸涌,才使用 %()(%Q 的簡寫形式)。多行字符串最岗,傾向使用 heredocs帕胆。 [link]
# 差 - 不存在插值
%(<div class="text">Some text</div>)
# 應(yīng)當(dāng)使用 '<div class="text">Some text</div>'

# 差 - 不存在雙引號
%(This is #{quality} style)
# 應(yīng)當(dāng)使用 "This is #{quality} style"

# 差 - 多行字符串
%(<div>\n<span class="big">#{exclamation}</span>\n</div>)
# 應(yīng)當(dāng)使用 heredocs

# 好 - 同時存在插值與雙引號,且是單行字符串
%(<tr><td class="name">#{name}</td>)
  • 避免使用 %q般渡,除非字符串同時存在 ' 與 "懒豹。優(yōu)先考慮更具可讀性的常規(guī)字符串芙盘,除非字符串中存在大量需要轉(zhuǎn)義的字符。 [link]
# 差
name = %q(Bruce Wayne)
time = %q(8 o'clock)
question = %q("What did you say?")

# 好
name = 'Bruce Wayne'
time = "8 o'clock"
question = '"What did you say?"'
quote = %q(<p class='quote'>"What did you say?"</p>)
  • 只有當(dāng)正則表達(dá)式中存在一個或以上的 / 字符時脸秽,才使用 %r儒老。 [link]
# 差
%r{\s+}

# 好
%r{^/(.*)$}
%r{^/blog/2011/(.*)$}

避免使用 method_missing。它會使你的調(diào)用棧變得凌亂豹储;其方法不被羅列在 #methods 中贷盲;拼錯的方法可能會默默地工作(nukes.launch_state = false)。優(yōu)先考慮使用委托剥扣、代理巩剖、或是 define_method
來替代。如果你必須使用 method_missing 的話钠怯,務(wù)必做到以下幾點(diǎn): [link]

  • 確保同時定義了 respond_to_missing?佳魔。
  • 僅僅捕獲那些具有良好語義前綴的方法,像是 find_by_*——讓你的代碼愈確定愈好晦炊。
  • 在語句的最后調(diào)用 super鞠鲜。
  • 委托到確定的、非魔術(shù)的方法断国,比如:
# 差
def method_missing?(meth, *params, &block)
  if /^find_by_(?<prop>.*)/ =~ meth
    # ... 一堆處理 find_by 的代碼
  else
    super
  end
end

# 好
def method_missing?(meth, *params, &block)
  if /^find_by_(?<prop>.*)/ =~ meth
    find_by(prop, *params, &block)
  else
    super
  end
end

# 最好的方式可能是在每個需要支持的屬性被聲明時贤姆,使用 define_method 定義對應(yīng)的方法
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市稳衬,隨后出現(xiàn)的幾起案子霞捡,更是在濱河造成了極大的恐慌,老刑警劉巖薄疚,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碧信,死亡現(xiàn)場離奇詭異,居然都是意外死亡街夭,警方通過查閱死者的電腦和手機(jī)砰碴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來板丽,“玉大人呈枉,你說我怎么就攤上這事“<睿” “怎么了碴卧?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長乃正。 經(jīng)常有香客問我,道長婶博,這世上最難降的妖魔是什么瓮具? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上名党,老公的妹妹穿的比我還像新娘叹阔。我一直安慰自己,他們只是感情好传睹,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布耳幢。 她就那樣靜靜地躺著莺褒,像睡著了一般氧吐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上碍侦,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天邢隧,我揣著相機(jī)與錄音店印,去河邊找鬼。 笑死倒慧,一個胖子當(dāng)著我的面吹牛按摘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播纫谅,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼炫贤,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了付秕?” 一聲冷哼從身側(cè)響起兰珍,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎盹牧,沒想到半個月后俩垃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡汰寓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年口柳,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片有滑。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡跃闹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出毛好,到底是詐尸還是另有隱情望艺,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布肌访,位于F島的核電站找默,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏吼驶。R本人自食惡果不足惜惩激,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一店煞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧风钻,春花似錦顷蟀、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至布朦,卻和暖如春囤萤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背喝滞。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工阁将, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人右遭。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓做盅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親窘哈。 傳聞我的和親對象是個殘疾皇子吹榴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)滚婉,斷路器图筹,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • 第5章 引用類型(返回首頁) 本章內(nèi)容 使用對象 創(chuàng)建并操作數(shù)組 理解基本的JavaScript類型 使用基本類型...
    大學(xué)一百閱讀 3,212評論 0 4
  • 在本教程中,將探討使用Ruby開始編程所需的基本語法让腹,以及如何在30分鐘內(nèi)快速入門學(xué)習(xí)并使用Ruby編程語言远剩。 注...
    易百教程閱讀 7,150評論 1 36
  • “有誰的青春不夾雜傷與悲瓜晤,誓死捍衛(wèi)的隨手消散成灰「鼓桑” 看了昨天更新的那一集《最好的我們》痢掠,張平哭了,蔣年年哭了嘲恍,耿...
    貳鷲閱讀 376評論 0 1