Monkey patching with refinements
Bài đăng này đã không được cập nhật trong 3 năm
Kỹ thuật monkey patching
là kỹ thuật giúp chúng ta mở rộng hoặc sửa đổi một hàm hoặc thuộc tính của một đối tượng đang có bằng một hàm hoặc thuộc tính khác. Tuy nhiên kỹ thuật này rất có thể sẽ gây ra những lỗi rất khó sửa chữa. Chủ đề của bài này là sự kết hợp của monkey patching
và refinement
, sẽ phần nào giúp khắc phục cũng như giảm thiểu những sai sót có thể mắc phải khi sử dụng monkey patching
. Tuy refinement
đã có ở ruby từ version 2.0 nhưng nó không được sử dụng nhiều nên mong rằng qua bài viết này có thể giúp mọi người làm quen với nó cũng như học được một cái gì mới.
Kỹ thuật monkey patching
Thông thường muốn hiểu cái điều gì đó thì chúng ta cần phải giải quyết các vấn đề của nó. Và ở đây chúng ta sẽ tìm hiểu các vấn đề của kỹ thuật monkey patching
(còn gọi là kỹ thuật open class
). Trước hết chúng ta xét ví dụ sử dụng kỹ thuật monkey patching
sau:
class String
def randomize
self.chars.shuffle.join
end
end
Trong ví dụ trên, lớp String được mở lại và thêm vào phương thức randomize
, việc này có ảnh hưởng đến những instance đang tồn tại của String
và các instance mới.
str = 'monkey'
begin
str.scramble
rescue => e
puts e.message
#=> undefined method `scramble' for "monkey":String
end
class String
def scramble
self.chars.shuffle.join
end
end
puts str.scramble
#=> omknye
Vậy, vấn đề của kỹ thuật monkey patching
là gì? Đó là, vì kỹ thuật monkey patching
là toàn cục nên sự thay đổi ở trên có ảnh hưởng đến toàn bộ các instance String
có trong ứng dụng. Có một số điều cần xem xét khi sử dụng kỹ thuật này:
- Nó có xung đột với thư viện của bên thứ ba không?
- Có thể chúng ta vô tình ghi đè lên một phương thức đã có?
- Bản vá liệu có còn tương thích với các phiên bản tương lai của Ruby?
Sử dụng refine
Chúng ta có thể hạn chế phạm vi của monkey patching
bằng cách gọi refine
bên trong module:
module StringExtensions
refine String do
def scramble
self.chars.shuffle.join
end
end
end
Refinement sẽ không hoạt động nếu chỉ định nghĩa nó, để kích hoạt refinement, chúng ta phải khai báo nó bằng cách sử dụng từ khóa using
Sử dụng using
Để kích hoạt refinement chúng ta gọi using
như sau:
using StringExtensions
Chúng ta có thể làm điều này bên trong một module hoặc class để bản vá chỉ hoạt động bên trong module hoặc hoặc class đó.
class Scramble
using StringExtensions
def self.call(word)
word.scramble
end
end
Scramble.call('monkey')
#=> knmeyo
Một refinement được định nghĩa sẽ hoạt động ở 2 chỗ:
- Ở trong chính bản thân block refine đó
- Ở trong class hay module sử dụng
using
để gọi refinement đó
Một số chú ý
- Các method sử dụng trong định nghĩa sẽ không được refine sau khi sử dụng using:
class Calculate
def add1_to(num)
num + one
end
def one
1
end
end
module CalculateExtensions
refine Calculate do
def one
1.0
end
end
end
using CalculateExtensions
Calculate.new.one #=> 1.0
Calculate.new.add1_to(2) #=> 3
- Gọi
using
trực tiếp trong IRB bây giờ đang không hoạt động. Tìm hiểu thêm điều này tại đây.
Kết luận
Refinement là một cách dễ dàng để hạn chế phạm vi của "monkey patching", do đó làm cho nó an toàn hơn khi thực hiện. Chúng ta có thể tránh những kết quả bất ngờ có thể xảy ra khi thực hiện các thay đổi global đối với code, nhưng vẫn có thể tận dụng open class
.
Mong rằng bài viết sẽ giúp ích được cho bạn. ty
References
All rights reserved