Những điều bạn cần biết về hằng số trong Ruby

Có rất nhiều thứ về hằng số trong Ruby mà bạn có thể nghĩ đến, ví dụ như bạn có thể thay đổi giá trị của 1 hằng số trong Ruby, không giống như các ngôn ngữ khác như C và Java. Chúng ta sẽ khám phá chi tiết hơn trong bài viết này nhé! Định nghĩa hằng số Một hằng số trong Ruby không yêu cầu một ký hiệu hay cú pháp đặc biệt gì để khai báo, bạn đơn giản chỉ cần viết hoa chữ cái đầu của tên hằng số. Dưới đây là 1 vài hằng số hợp lệ:

ABC = 1
Goo = 2
Foo = 3

Lưu ý rằng bạn không thể khai báo một hằng số bên trong một phương thức, nếu không bạn sẽ nhận thông báo lỗi như sau:

def the_method
  ABC = 1
end
 
# "dynamic constant assignment"

Vì vậy, bạn chỉ cần khai báo hằng số ở bên ngoài phương thức, thông thường, chúng ta khai báo hằng số trên cùng của class vì thế chúng có thể được nhìn thấy rõ ràng.

class RubyBlog
  URL    = "blackbytes.info"
  AUTHOR = "Jesus Castello"
end

Sau đó, bạn có thể truy cập các hằng số này bên trong phương thức của class hoặc phương thức của class khác bằng cách sử dụng cú pháp:

p RubyBlog::AUTHOR
 
# "Jesus Castello"

Uninitialized Constant Một lỗi phổ biến mà bạn có thể nhận được là:

puts Foo
# "uninitialized constant Foo (NameError)"

Bạn có thể hiểu nhầm lỗi này thành "constant not found Foo". Một điểm quan trọng để hiểu lỗi này đó là các class trong Ruby đều được coi là các hằng số:

Array
String
Hash

Chúng là các hằng số vì chữ cái đầu của chúng viết hoa. Điều này là quan trọng bởi vì hầu hết lý do bạn nhìn thấy lỗi này là bạn quên khi require một số file hoặc gem cái mà được định nghĩa hằng số. Hoặc có thể bạn chỉ sai lỗi chính tả của tên hằng số. Vì thế hãy để ý các vấn đề này. Hằng số có thể thay đổi Giống như đã đề cập khi giới thiệu, hằng số trong Ruby có thể thay đổi:

ABC = 1
ABC = 2

Nhưng bạn vẫn nhìn thấy message cảnh báo:

2: warning: already initialized constant ABC

Chương trình của bạn vẫn chạy tốt, nhưng bạn nên tránh cách thay đổi này. Không có cách nào để ngăn chặn một hằng số thay đổi bởi vì biến trong Ruby luôn trỏ tới một đối tượng. Cách tốt nhất để làm thế là sử dụng một đối tượng bất biến. Ví dụ:

AUTHOR = "Jesus Castello".freeze
 
AUTHOR << "o"
# RuntimeError: can't modify frozen String

Chú ý rằng trong ví dụ trên, bạn vẫn thay đổi cái mà hằng số AUTHOR trỏ tới, điều duy nhất freeze bảo vệ bạn là việc tự nó thay đổi đối tượng. Constant Methods Có một số phương thức được dùng để làm việc với hằng số:

Phương thức Mô tả
constants Trả về một mảng các ký hiệu đại diện cho các hằng số được định nghĩa trong class
const_get Trả về giá trị của hằng số. Tham số là một ký hiệu hoặc một string
const_set Thiết lập giá trị cho một hằng số. sử dụng 2 tham số: tên hằng số như một ký hiệu và giá trị của hằng số
const_missing Giống phương thức method_missing nhưng sử dụng cho hằng số
const_defined? Trả về true nếu hằng số được gọi đã được định nghĩa
remove_const Loại bỏ một hằng số
private_constant Làm cho một hằng số private vì thế nó không thể được truy cập bên ngoài class với cú pháp Class::ABC

Ví dụ:

module Food
  class Bacon; end
  class Chocolate; end
end
 
puts "Classes defined inside #{Food}:"
 
puts Food.constants

Bạn cũng có thể sử dụng chuỗi như Array và nhận về lớp thực tế:

array_class = Object.const_get("Array")

Trong Rails, có phương thức constantize thực hiện việc const_get cho bạn, nhưng nó không thực hiện bất kỳ một kiểm tra bảo mật nào. Ruby Constant Scope Khi bạn tạo một hằng số bên ngoài một class nào đó, hằng số này sẽ có thể dùng ở bất kỳ nơi nào. Hằng số cũng có thể sử dụng trong các lớp con:

class A
  FOO = 1
end
 
class B < A
  def foo
    puts FOO
  end
end
 
B.constants
 
# [:FOO]

Hằng số được định nghĩa bên ngoài một nested module hoặc lớp cũng có thể dùng bên trong các class lồng nhau.

module Food
  STORE_ADDRESS = "The moon"
 
  class Bacon
    def foo; puts STORE_ADDRESS; end
  end
end
 
fb = Food::Bacon.new
fb.foo
 
# "The moon"

Module Mixing Các hằng số từ các module cũng có thể dùng:

module Mixin
  A = 123
end
 
class Product
  include Mixin
 
  puts A
end
 
# 123

Lưu ý rằng điều này hoạt động khi include các module, nó sẽ không hoạt động nếu bạn extend nó. Ví dụ:

class Product
  extend Mixin
 
  puts A
end
 
#  uninitialized constant Product::A

Ngoài ra, khi bạn sử dụng một phương thức được định nghĩa trong phương thức included, nó sẽ sử dụng hằng số được định nghĩa trong module đó, cho dù nếu có cùng một hằng số được định nghĩa trong class hiện tại.

module Parent
  def print_value
    VALUE
  end
end
 
class Child
  include Parent
 
  VALUE = 1
end
 
# Works
p Child::VALUE
 
# uninitialized constant Parent::VALUE
p Child.new.print_value

Module Nesting Dưới đây là một ví dụ nữa về nested classes (giống như các module).

class A
  FOO = 1
end
 
class A::B
  class C
    puts FOO
  end
end
# NameError: uninitialized constant A::B::C::FOO

Chú ý ký hiệuA :: B ở đây sử dụng như một phím tắt. Nhưng vấn đề là class C sẽ không có quyền truy cập vào trực tiếp FOO. Vì lý do đó, bạn có thể sẽ muốn sử dụng cách sau:

class A
  FOO = 1
end
 
class A
  class B
    class C
      puts FOO
    end
  end
end
# 1

Trong ví dụ đầu tiên bạn vẫn có thể dùng :: :: FOO để truy cập hằng số, nhưng nếu tên lớp thay đổi thì bạn sẽ gặp lỗi.

Cú pháp :: A :: FOO này hoạt động vì nó làm cho Ruby tìm trong top-level của scope, nơi các hằng số được định nghĩa giống như Array & String. Kết luận Như vậy, bạn đã học về hằng số trong Ruby, một loại biến có một số hành vi thú vị. Bạn có thể thay đổi giá trị của một hằng số nhưng nó sẽ tạo ra một cảnh báo. Bạn cũng học được rằng tên lớp là các hằng số và rằng bạn nên tránh sử dụng const_get với các input nhập vào của người dùng. Cảm ơn các bạn đã đọc bài viết!!!


All Rights Reserved