Refactor code với hai toán tử "cực dị" là "&." và "&.!=" ?!?!
Bài đăng này đã không được cập nhật trong 4 năm
Refactor code là chuyện thường xuyên gặp phải và cũng là kĩ năng mà một developer cần có. Nó giúp cho chúng ta cải thiện performance hệ thống, tăng khả năng đọc hiểu, khả năng mở rộng và bảo trì của dự án. Trong bài viết này mình sẽ xin chia sẻ cách refactor code với hai toán tử "cực dị" là &.
và &.!=
nhé
Toán tử &.
Toán tử &.
được gọi là Safe Navigation Operator được ra mắt ở phiên bản Ruby 2.3.0. Nó khá là giống với toán tử ?.
trong C#, Kotlin, Objective-C, ... Hãy xem xem nó có thể làm những gì nhé.
Giả sử như bạn của bạn nuôi một chú mèo và bạn muốn biết chú mèo này giống gì:
if friend && friend.cat && friend.cat.kind
...
end
Viết như thế này rất rườm rà và khi sửa logic thì cũng cực kì khó chịu. ActiveSupport
có method try
cũng tương tự như toán tử này:
if friend.try(:cat).try(:kind)
...
end
Cả hai đoạn code này làm tròn nhiệm vụ của nó, hoặc sẽ trả về nil
nếu như một method trong chuỗi trả về nil
; hoặc sẽ trả về giống của chú mèo nếu như không có gì bất thường.
Với Safe Navigation Operator - toán tử &.
, ta có thể refactor code lại như sau:
friend&.cat&.kind
Cú pháp nhìn thì có vẻ hơi "si đa" nhưng trông cũng khá là compact đấy chứ.
Ví dụ:
friend = Friend.new cat: nil # Bạn của bạn không nuôi mèo :))
friend.cat.kind
# => NoMethodError: undefined method `kind' for nil:NilClass
friend && friend.cat && friend.cat.kind
# => nil
friend.try(:cat).try(:kind)
# => nil
friend&.cat&.kind
# => nil
Trường hợp này không có gì bất thường, giờ giả sử cat = false
xem sao (hầu như không có trong thực tế nên ví dụ chỉ mang tính chất minh họa thôi nhé )
friend = Friend.new cat: false
friend.cat.kind
# => NoMethodError: undefined method `kind' for false:FalseClass`
friend && friend.cat && friend.cat.kind
# => false
friend.try(:cat).try(:kind)
# => nil
friend&.cat&.kind
# => undefined method `kind' for false:FalseClass`
Trường hợp này ta thấy toán tử &.
đã không pass được giá trị là false
. Giờ nếu trường hợp là Object
thì sao?
friend = Friend.new cat: Object.new
friend.cat.kind
# => NoMethodError: undefined method `kind' for #<Object:0x00559996b5bde8>
friend && friend.cat && friend.cat.kind
# => NoMethodError: undefined method `kind' for #<Object:0x00559996b5bde8>`
friend.try(:cat).try(:kind)
# => nil
friend&.cat&.kind
# => NoMethodError: undefined method `kind' for #<Object:0x00559996b5bde8>`
Ta thấy toán tử &.
sẽ chỉ trả về nil
nếu như method hoặc object trước đó là nil
mà thôi, các trường hợp còn lại vẫn sẽ raise error như thường.
Giờ xét thêm trường hợp của try
, theo định nghĩa:
try(*a, &b)
Invokes the public method whose name goes as first argument just like
public_send
does, except that if the receiver does not respond to it the call returnsnil
rather than raising an exception.
Trong ví dụ trên :cat
không respond với :kind
, chính vì vậy friend.try(:cat).try(:kind)
mới trả về nil
. Để raise exception, chúng ta có thể dùng try!
:
try!(*a, &b)
Same as try, but raises a
NoMethodError
exception if the receiver is notnil
and does not implement the tried method.
friend.try!(:cat).try!(:kind)
# => NoMethodError: undefined method `kind' for #<Object:0x00559996b5bde8>`
Toán tử &.!=
Cái gì?!? Toán tử &.!=
?!?!
Cái quái gì đây?!?!
Đúng rồi đấy, bạn không nhìn nhầm đâu
Thực chất, toán tử &.!=
là toán tử &.
và !=
được kết hợp lại với nhau. Sở dĩ có thể kết hợp được như vậy là bởi vì !=
trông như một toán tử nhưng về bản chất nó lại là một method.
Với cách kết hợp này, chúng ta thậm chí có thể refactor đoạn code dài dòng sau:
if friend.cat && friend.cat != my.cat
...
end
thành:
if friend.cat&.!= my.cat
...
end
Hơi hack não nên mình sẽ giải thích một chút:
Hãy coi toán tử !=
là một method và coi nó giống như method kind
ban nãy nhưng kind
bây giờ lại nhận thêm tham số là my.cat
(đừng quan tâm đến logic, chỉ là ví dụ thôi )
friend.cat&.kind
# sẽ thành
friend.cat&.kind(my.cat)
# giờ thay kind bằng !=
friend.cat&.!= my.cat
Thực sự, mình thấy đây là một cách refactor code cực kì hay. Tuy nhiên, nó khá là hack não nếu như ai chưa biết đến Safe Navigation Operator - Toán tử &.
này. Cá nhân mình khá là thích refactor code, trông cool ngầu hơn, dễ sửa đổi, dễ hiểu lại còn DRY được code nữa. Hy vọng với bài này các bạn sẽ có thêm những cú refactor code thật cool ngầu trong pull request và làm điên đảo reviewer nhé!
Tài liệu tham khảo
All rights reserved