Khi nào sử dụng freeze trong Ruby
Bài đăng này đã không được cập nhật trong 7 năm
Gần đây việc sử dụng #freeze
trong Ruby khá phổ biến, nhưng taị sao lại thế thì điều này chưa được giải thích rõ. Bài viết này sẽ xem xét những lý do phổ biến nhất mà những developer dùng biến freeze
.
Tạo hằng số
Trong Ruby, hằng số có thể thay đổi. Khá lạ đúng không, nhưng ta có thể kiếm chứng với đoạn code sau.
MY_CONSTANT = "foo"
MY_CONSTANT << "bar"
puts MY_CONSTANT.inspect # => "foobar"
Nhưng bằng cách sử dụng freeze
, ta sẽ tạo được một hằng số thực sự, đó là hằng số không thể thay đổi, và khi cố gắng thay đổi nó ta sẽ nhận được thông báo lỗi như thế này.
MY_CONSTANT = "foo".freeze
MY_CONSTANT << "bar" # => RuntimeError: can't modify frozen string
Giảm việc cấp phát đối tượng
Một trong những cách tốt nhất để tăng tốc ứng dụng Ruby đó là giảm số lượng các object được tạo ra. Một trong những nguồn gây ra việc cấp phát đối tượng này đến từ các string
được rải khắp trong ứng dụng của bạn.
Mỗi lần bạn sử dụng method log("foobar")
, bạn sẽ tạo một đối tượng string
. Nếu code của bạn gọi nó hàng nghìn lần 1 giây, có nghĩa là bạn đang tạo hàng nghìn string
1 giây.
May mắn thay, nếu chúng ta dùng freeze
với các string
này, Ruby sẽ chỉ tạo ra một đối tượng string
và cache chúng lại cho lần sử dụng sau. Chỉ số benchmark cho thấy hiệu suất khi dùng freeze
tăng khoảng 50% so với không dùng.
require 'benchmark/ips'
def noop(arg)
end
Benchmark.ips do |x|
x.report("normal") { noop("foo") }
x.report("frozen") { noop("foo".freeze) }
end
# Results with MRI 2.2.2:
# Calculating -------------------------------------
# normal 152.123k i/100ms
# frozen 167.474k i/100ms
# -------------------------------------------------
# normal 6.158M (± 3.3%) i/s - 30.881M
# frozen 9.312M (± 3.5%) i/s - 46.558M
Sự tối ưu hóa trong Ruby >= 2.2
Ruby 2.2 hoặc hơn sẽ tự động freeze
string khi sử dụng string
làm key của hash
user = {"name" => "george"}
# In Ruby >= 2.2
user["name"]
# ...is equivalent to this, in Ruby <= 2.1
user["name".freeze]
Và trong Ruby 3 thì rất có thể tất cả string sẽ đều được tự động freeze
. https://bugs.ruby-lang.org/issues/11473
Value objects & functional programming
Ruby không phải ngôn ngữ lập trình hàm, nhưng nhiều Rubyists bắt đầu viết theo phong cách của lập trình hàm. Nguyên lý chính của phong cách này là nên tránh các tác động bên ngoài, tức là đối tượng sẽ không thay đổi sau khi được khởi tạo.
Bằng cách sử dụng freeze
bên trong constructor
, ta có thể đảm bảo đối tượng sẽ không thể thay đổi, và mọi tác động bên ngoài sẽ đều báo lỗi.
class Point
attr_accessor :x, :y
def initialize(x, y)
@x = x
@y = y
freeze
end
def change
@x = 3
end
end
point = Point.new(1,2)
point.change # RuntimeError: can't modify frozen Point
Tham khảo http://blog.honeybadger.io/when-to-use-freeze-and-frozen-in-ruby/
All rights reserved