前言
Ruby 是物件化非常徹底的程式語言,幾乎所有東西都是物件 (除了 block
外),
很常使用 class
來實現物件導向,但每次都要寫 class xxx
建立新的類別挺麻煩的,這時可以使用 Struct
和 OpenStruct
OpenStruct
比 Struct 更有彈性, 可以任意增加 Attribute,不像 Struct 需先定義好有哪些 Attribute,無法定義 method,相對效能會較差些
OpenStruct 的祖先分別是
1
2
3
4
5
6irb
# 在 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
5Book
#<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
3Book = 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
8Book = 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
8Book = 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
14Book = 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
參考資料
- Ruby -- Struct v.s openStruct
- 模擬class物件:Ruby當中Struct及OpenStruct的使用
- Ruby - Struct vs OpenStruct
- OpenStruct in Ruby
- 关于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/
備註:之後文章修改更新,以個人部落格為主