Cấu trúc dữ liệu OpenStruct

OpenStruct là một cấu trúc dữ liệu, tương tự như một Hash, cho phép định nghĩa các thuộc tính tùy ý với các giá trị kèm theo của chúng. Điều này được thực hiện bằng cách sử dụng metaprogramming của Ruby để xác định phương thức trên lớp. Việc sử dụng OpenStruct một cách hợp lý sẽ giải quyết được rất nhiều vấn đề khi phát triển ứng dụng như giảm thiểu số lượng class không cần thiết, lưu nhiều kiểu dữ liệu khác nhau trên cùng một object . . .

Những phương thức cơ bản của OpenStruct

# Khởi tạo một cấu trúc dữ liệu OpenStruct
2.3.0-p0 :016 > p = OpenStruct.new
 => #<OpenStruct> 
 
 # Gán một số attributes với các kiểu dữ liệu khác nhau cho OpenStruct
2.3.0-p0 :017 > p.name = "Thanh Giang"
 => "Thanh Giang" 
2.3.0-p0 :018 > p.age = 26
 => 26 
 
 # Kết quả
2.3.0-p0 :019 > p.name
 => "Thanh Giang" 
2.3.0-p0 :020 > p.age
 => 26 
 
 # Với những attributes chưa được khai báo khi gọi sẽ trả về dữ liệu nil, điều này rất có lợi vì không sợ gặp phải exceptions không cần thiết khi code.
2.3.0-p0 :021 > p.address
 => nil

Có lẽ các bạn sẽ thắc mắc OpenStruct có phải là một object không, câu trả lời là với Ruby on Rails mọi thứ đều được coi là object.

2.3.0-p0 :022 > p.is_a? Object
 => true

Thậm chí OpenStruct sử dụng một Hash nội bộ để lưu trữ các thuộc tính cũng như giá trị của nó.

2.3.0-p0 :023 > h = OpenStruct.new name: "Thanh Giang", age: 26
 => #<OpenStruct name="Thanh Giang", age=26> 
2.3.0-p0 :024 > h
 => #<OpenStruct name="Thanh Giang", age=26> 
2.3.0-p0 :025 > h.name
 => "Thanh Giang" 
2.3.0-p0 :026 > h.age
 => 26

Như vậy các bạn hoàn toàn có thể gán toàn bộ attributes của một object nào đó thành cấu trúc dữ liệu OpenStruct.

Những attributes có chứa những ký tự đặc biệt như ()[]* hoặc dấu cách sẽ không tồn tại sẵn có trên cấu trúc dữ liệu OpenStruct tuy vậy vẫn có thể định nghĩa và sử dụng nhờ method send

2.3.0-p0 :048 >   s = OpenStruct.new("Ten toi la" => "Thanh Giang")
 => #<OpenStruct Ten toi la="Thanh Giang"> 
2.3.0-p0 :049 > s.send("Ten toi la")
 => "Thanh Giang"
 
 
 2.3.0-p0 :084 >   s = OpenStruct.new(:male? => true)
 => #<OpenStruct male?=true> 
2.3.0-p0 :085 > s.male?
 => true 
 
 # Thay đổi giá trị của một attributes có chứa ký tự đặc biệt sử dụng `send` method.
2.3.0-p0 :086 > s.send("male?=", false)
 => false 
2.3.0-p0 :087 > s.male?
 => false 

Xóa hoặc thay đổi giá trị của một attributes của OpenStruct

2.3.0-p0 :104 >   r = OpenStruct.new name: "Thanh Giang", age: 26
 => #<OpenStruct name="Thanh Giang", age=26> 
2.3.0-p0 :105 > r.age
 => 26 
2.3.0-p0 :106 > r.age = nil
 => nil 
2.3.0-p0 :107 > r
 => #<OpenStruct name="Thanh Giang", age=nil> 
2.3.0-p0 :108 > r.delete_field :age
 => nil 
2.3.0-p0 :109 > r
 => #<OpenStruct name="Thanh Giang">

Public Class Methods

new(hash=nil)

OpenStruct sử dụng một Hash nội bộ để định nghĩa atrributes và giá trị của chúng, do vậy khi khơỉ tạo một đối tượng OpenStruct thì mặc định Hash đó sẽ là nil

Nếu định nghĩa bằng một Hash thì key và value của Hash đó sẽ được định nghĩa trở thành attributes và value của OpenStruct.


# Có thể khởi tạo hash bằng cả cách viết của Rails 3 hoặc Rails 4 thì OpenStruct vẫn có thể chấp nhận. 
2.3.0-p0 :110 > hash = {name: "Thanh Giang", "age" => 26}
 => {:name=>"Thanh Giang", "age"=>26} 
2.3.0-p0 :111 > h = OpenStruct.new hash
 => #<OpenStruct name="Thanh Giang", age=26>

Để hiểu hơn về cách thức khởi tạo một object OpenStruct các bạn có thể hình dung thông qua đoạn code sau.

   def initialize(hash=nil)
  @table = {}
  if hash
    hash.each_pair do |k, v|
      k = k.to_sym
      @table[k] = v
    end
  end
end

Public Instance Methods

==(other)

Hai object OpenStruct được coi là giống nhau khi mà cả attributes và value của chúng là giống nhau.

2.3.0-p0 :122 > h = OpenStruct.new name: "h"
 => #<OpenStruct name="h"> 
2.3.0-p0 :123 > h1 = OpenStruct.new name: "h1"
 => #<OpenStruct name="h1"> 
2.3.0-p0 :124 > h2 = OpenStruct.new name: "h2", age: 26
 => #<OpenStruct name="h2", age=26> 
2.3.0-p0 :125 > h == h1
 => false 
2.3.0-p0 :126 > h == h2
 => false 
2.3.0-p0 :127 > h3 = OpenStruct.new name: "h"
 => #<OpenStruct name="h"> 
2.3.0-p0 :128 > h == h3
 => true

Đoạn code sau của cấu trúc dữ liệu OpenStruct sẽ giải thích tất cả.

def ==(other)
  return false unless other.kind_of?(OpenStruct)
  @table == other.table!
end

ostruct[name] → object và ostruct[name] = obj → obj Có hai cách có thể lấy ra value của attrubutes OpenStruct

 => #<OpenStruct name="h"> 
2.3.0-p0 :130 > h.name
 => "h" 
2.3.0-p0 :131 > h[:name]
 => "h" 
2.3.0-p0 :132 > h["name"]
 => "h"

dig(name, ...) → object

Có thể khởi tạo một object OpenStruct với attributes là một object OpenStruct khác. Khi đó attributes được khởi tạo cũng được coi là một object.

2.3.0-p0 :155 > address = OpenStruct.new("city" => "Ha Noi", "zip" => 12345)
 => #<OpenStruct city="Ha Noi", zip=12345> 
2.3.0-p0 :156 > person  = OpenStruct.new("name" => "Thanh Giang", "address" => address)
 => #<OpenStruct name="Thanh Giang", address=#<OpenStruct city="Ha Noi", zip=12345>> 
2.3.0-p0 :157 > person.address.city
 => "Ha Noi" 
2.3.0-p0 :158 > person.dig(:address, "zip")
 => 12345

Một số method khác mà cấu trúc dữ liệu OpenStruct cung cấp

  • each_pair {|name, value| block } → ostruct
  • each_pair → Enumerator
  • eql?(other)
  • freeze()
  • hash()
  • inspect()
  • marshal_dump()
  • marshal_load(x)
  • to_h()
  • to_s()

Sức mạnh của OpenStruct sẽ thể hiện rõ ràng nếu chúng ta sử dụng đúng cách và đúng lúc, để tìm hiểu rõ hơn về cấu trúc dữ liệu này các bạn có thể tham khảo tại đây

Thanks for reading !!!