你必須很努力

Day29 - Ruby 中的 Struct 與 OpenStruct

2020/10/04
字數統計: 868閱讀時間: 3 min

前言

Ruby 是物件化非常徹底的程式語言,幾乎所有東西都是物件 (除了 block 外),
很常使用 class 來實現物件導向,但每次都要寫 class xxx 建立新的類別挺麻煩的,這時可以使用 StructOpenStruct


OpenStruct

比 Struct 更有彈性, 可以任意增加 Attribute,不像 Struct 需先定義好有哪些 Attribute,無法定義 method,相對效能會較差些

OpenStruct 的祖先分別是

1
2
3
4
5
6
irb

# 在 irb 中,使用 OpenStruct 需 require 'ostruct'
require 'ostruct'
OpenStruct.ancestors
# [OpenStruct, Object, Kernel, BasicObject]

如何使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 在 irb 中,使用 OpenStruct 需 require 'ostruct'
require 'ostruct'

# 方法1
Book = OpenStruct.new
Book.class # OpenStruct
Book.name = 'Ruby on Rails 與它們相關的東西'
Book.url = 'https://riverye.com/'
Book.price = 999
Book.isbn = '987-654-321-0'
Book #<OpenStruct name="Ruby on Rails 與它們相關的東西", url="https://riverye.com/", price=999, isbn="987-654-321-0">

Book.name # Ruby on Rails 與它們相關的東西'
Book[:name] # Ruby on Rails 與它們相關的東西'
Book['name'] # Ruby on Rails 與它們相關的東西'


# 方法2
Book = OpenStruct.new(name: 'Ruby on Rails 與它們相關的東西', isbn: '987-654-321-0')
Book.name # Ruby on Rails 與它們相關的東西'
Book[:name] # Ruby on Rails 與它們相關的東西'
Book['name'] # Ruby on Rails 與它們相關的東西'
Book.name = '小菜'
Book.price = 999
Book.name # 小菜
Book[:name] # 小菜
Book['name'] # 小菜

刪除某一個元素

1
2
3
4
5
Book
#<OpenStruct name="小菜", isbn="987-654-321-0", price=999, hello=13>

Book.delete_field(:price)
#<OpenStruct name="小菜", isbn="987-654-321-0", hello=13>

Struct

使用前,需先定義好 Attribute,不像 OpenStruct 那麼彈性(較嚴謹),但可以定義方法 (method) ,相對效能較好些

Struct 的祖先分別是

1
2
3
Book = Struct.new(:name)
Book.ancestors
[#<Class:0x00007fa7ee9bb048>, Struct, Enumerable, Object, Kernel, BasicObject]

如何使用

1
2
3
# 在 irb 中可直接使用
Book = Struct.new(:name, :price, :url, :isbn)
book_1 = Book.new('Ruby on Rails 與它們相關的東西', 999, 'https://riverye.com/', '987-654-321-0')

需要照順序放 Attribute 喔,若不想照順序放 Attribute 的話,可使用 keyword_init: true

1
2
3
4
5
6
7
8
Book = Struct.new(:name, :price, :url, :isbn, keyword_init: true)
book_hash = { url: 'https://riverye.com/', isbn: '987-654-321-0', name: 'Ruby on Rails 與它們相關的東西', price: 999}
book_1 = Book.new(book_hash)
#<Struct:Book:0x00000168
# isbn = "987-654-321-0",
# name = "Ruby on Rails 與它們相關的東西",
# price = 999,
# url = "https://riverye.com/"

Struct 可以定義方法

1
2
3
4
5
6
7
8
Book = Struct.new('Book', :price) do
def over_thousand?
price >= 1000
end
end

Book.new(1000).over_thousand?
# true

Struct 比較嚴謹

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Book = Struct.new(:name, :url)

# 未帶入的變數會自動變成 nil
book_1 = Book.new('Ruby on Rails 與它們相關的東西')
book_1.name # 'Ruby on Rails 與它們相關的東西'
book_1.url # nil

# 無法隨時新增屬性
Book.tatal = 1
# NoMethodError (undefined method `total=' for Book:Class)

# 宣告時帶入太多參數會產生錯誤,比 OpenStruct 嚴謹許多
book_1 = Book.new('Ruby on Rails 與它們相關的東西', isbn: '987-654-321-0')
# ArgumentError (struct size differs)

OpenStruct VS. Struct 比較

效能上兩者差異蠻大的,可參考 Stack Overflow 這篇,個人也是比較常用 Struct

參考資料

  1. Ruby -- Struct v.s openStruct
  2. 模擬class物件:Ruby當中Struct及OpenStruct的使用
  3. Ruby - Struct vs OpenStruct
  4. OpenStruct in Ruby
  5. 关于ruby:何时应该使用Struct vs. OpenStruct?

小結

個人蠻常在測試中使用,像是要要測試 Sidekiq 的 Sidekiq::RetrySet.new,使用 Struct 來模擬,挺方便的,也會用在不同地方,下一篇會是鐵人賽總結(終於~~


鐵人賽文章連結:https://ithelp.ithome.com.tw/articles/10249081
medium 文章連結:https://link.medium.com/YGEc5Qnqjab
本文同步發布於 小菜的 Blog https://riverye.com/

備註:之後文章修改更新,以個人部落格為主

原文連結:https://riverye.com/2020/10/04/Day29-Ruby-中的-Struct-與-OpenStruct/

發表日期:2020-10-04

更新日期:2022-09-23

CATALOG
  1. 1. 前言
  2. 2. OpenStruct
    1. 2.0.1. OpenStruct 的祖先分別是
  • 3. 如何使用
    1. 3.0.1. 刪除某一個元素
  • 4. Struct
    1. 4.0.1. Struct 的祖先分別是
  • 5. 如何使用
    1. 5.0.1. 需要照順序放 Attribute 喔,若不想照順序放 Attribute 的話,可使用 keyword_init: true
    2. 5.0.2. Struct 可以定義方法
    3. 5.0.3. Struct 比較嚴謹
  • 6. OpenStruct VS. Struct 比較
  • 7. 參考資料
  • 8. 小結