在 Ruby 2.5 為物件引進了 yield_self
方法, 但是方法名字很長, 因此在 Ruby 2.6 時為他新增了一個別名: then
.
這個方法與 tap
類似, 不同的點在於 tap
回傳自己, 但 then
回傳的是執行結果.
善用這兩個方法可以寫出更清楚易懂的程式碼, 並且減少指派 (減少你需要去煩惱 “這個變數該叫什麼名字” 的困擾).
直接來看看一個來自真實世界的範例.
需求是有一系列的圖片, 希望能模糊化且半透明化, 我們使用 image_processing, 而我在閱讀文件後得知.
我大概需要下 -gaussian-blur 0x16 -alpha set -channel A +level 0,20%
這樣的指令去完成我想做的事情.
注意 level
參數是加號開頭的, 在 image_processing 時需要在該參數後面添加加號, 因此我應該要寫成:
# WRONG: NoMethodError (undefined method `call' for "0,20%":String)
image = ImageProcessing::MiniMagick.source(original)
image.convert('png')
.gaussian_blur('0x16')
.alpha('set')
.channel('A')
.level+('0,20%')
.call
因為 +
的優先權比較低, 因此 ('0,20%').call
被先行計算了, 我沒辦法舒服的 chain 下去, 難道我只能寫成這樣嗎?
# BAD
image = ImageProcessing::MiniMagick.source(original)
(image.convert('png')
.gaussian_blur('0x16')
.alpha('set')
.channel('A')
.level+('0,20%'))
.call
# BAD
image_before_call = image.convert('png')
.gaussian_blur('0x16')
.alpha('set')
.channel('A')
.level+('0,20%')
image_before_call.call
想像一下, 日後如果你需要 “加” 更多東西的時候該怎麼辦呢?
優美的 ruby 是不容許這麼難看的東西出現的! 幸好我們有 then
方法, 可以幫我們把那個 .level+
包起來, 去避免使用大範圍的括號調整優先權, 或者使用一個不知道該怎麼取名的臨時變數.
最後的結果如下:
# GOOD
image = ImageProcessing::MiniMagick.source(original)
image.convert('png')
.gaussian_blur('0x16')
.alpha('set')
.channel('A')
.then { _1.level+('0,20%') }
.call
使用 tap
與 then
你幾乎可以 chain 任何你想 chain 的東西, 請善用它們, 讓程式碼看起來更舒服吧.