在 ruby 當中,經常會要定義一個新的類別,如果覺得每次都要寫 class xxx
太麻煩,就可以用 struct & OpenStruct 快速的產生出來!
Class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| class People
attr_accessor :name, :phone
def initialize(name, phone)
@name = name
@phone = phone
end
def to_ary
[name, phone]
end
end
a = People.new("foo", 1234)
#=> #<People:0x007fcaabcf5328 @name="foo", @phone=1234>
a.name
#=> "foo"
a.phone
#=> 1234
a.to_ary
#=> ["foo", 1234]
|
Struct
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| #method 要用 block 來傳遞,Attribute 一開始就固定了
People = Struct.new(:name, :phone) do
def to_ary
[name, phone]
end
end
# => People
a = People.new("foo", 1234)
#=> #<struct People name="foo", phone=1234>
a.name
#=> "foo"
a.phone
#=> 1234
a.to_ary
#=> ["foo", 1234]
|
也可以直接用繼承的方式
1
2
| class People < struct.new(:name, :phone)
end
|
其他取 Attribute Value 的方法
Class則無法
1
2
3
4
5
6
| a[:name]
#=> "foo"
a["name"]
#=> "foo"
a[0]
#=> "foo"
|
other
不要去 extend 一個 Struct.new - 它已經是一個新的 class。擴展它會產生一個多餘的 class 層級
並且可能會產生怪異的錯誤如果文件被加載多次。
OpenStruct
主要差異點是在於,比 Struct 更有彈性, 因為它可以任意增加 Attribute , 不像 Struct 要先限制好有哪些 Attribute
但比較可惜的是,無法定義 method
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| #在 console 記得先 require
require 'ostruct'
People = OpenStruct.new
#=> #<OpenStruct>
or
People = OpenStruct.new(name: 'foo', phone: 1234)
#=> #<OpenStruct name="foo", phone=1234>
可以自由新增
People.name = 'foo'
#=> "foo"
People.phone = 1234
#=> 1234
People.age = 18
#=> 18
People
#=> #<OpenStruct name="foo", phone=1234, age=18>
也可以直接用繼承的方式
class Test < OpenStruct
end
|
- Struct: 接受的是按順序排列的初始化參數
- Openstruct: 接受的則是散列表的參數
WHEN TO USE?
- As a temporary data structure 暫時的 data 結構
- As internal class data 內部的 class data
也許另一個 class 還不至於明確到可以獨立成一個 class,因此先暫存在別的 class 裡,直到有明確的行為,足夠讓它獨立出去
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| class Person
Address = Struct.new(:street_1, :street_2, :city)
attr_accessor :name, :address
def initialize(name, opts)
@name = name
@address = Address.new(opts[:street_1], opts[:street_2], opts[:city])
end
end
leigh = Person.new("Leigh Halliday", {
street_1: "123 Road",
city: "Toronto",
})
puts leigh.address.inspect
# <struct Person::Address street_1="123 Road", street_2=nil, city="Toronto", province="Ontario", country="Canada", postal_code="M5E 0A3">
|
1
2
3
4
5
| KCup = Struct.new(:size, :brewing_time, :brewing_temp)
colombian = KCup.new(:small, 60, 85)
brewer = Brewer.new(colombian)
expect(brewer.brew).to eq(true)
|
官方文件:
參考文件: