簡體   English   中英

Ruby Class 與結構

[英]Ruby Class vs Struct

我見過代碼庫使用結構來包裝 class 內的屬性和行為。Ruby Class 和結構有什么區別? 什么時候應該用一個代替另一個。?

結構文檔

Struct 是一種使用訪問器方法將多個屬性捆綁在一起的便捷方法,而無需編寫顯式類。

Struct 類生成包含一組成員及其值的新子類。 為每個成員創建類似於 Module#attr_accessor 的讀取器和寫入器方法。

因此,如果我想要一個可以訪問名稱屬性(讀取和寫入)的Person類,我要么通過聲明一個類來實現:

class Person
  attr_accessor :name

  def initalize(name)
    @name = name
  end
end

或使用結構:

Person = Struct.new(:name)

在這兩種情況下,我都可以運行以下代碼:

 person = Person.new
 person.name = "Name"
 #or Person.new("Name")
 puts person.name

什么時候用?

正如描述所述,當我們需要一組可訪問的屬性而不必編寫顯式類時,我們會使用 Structs。

例如,我想要一個點變量來保存 X 和 Y 值:

point = Struct.new(:x, :y).new(20,30)
point.x #=> 20

還有一些例子:

要添加到其他答案中,有些事情你不能用 Struct 做,有些事情你可以。

例如,您不能創建沒有參數的 Struct

Bar = Struct.new
=> ArgumentError: wrong number of arguments (given 0, expected 1+)

Bar = Struct.new(:bar)
bar = Bar.new(nil)
bar.class
=> Bar

但是,一個類可以讓你這樣做:

class Foo; end
foo = Foo.new
foo.class
=> Foo

您不能為 Struct 參數設置默認值

Bar = Struct.new(bar: 'default')
=> ArgumentError: unknown keyword: bar

Bar = Struct.new(bar = 'default')
=> NameError: identifier default needs to be constant

但是你可以用一個類來做,要么傳遞一個散列,要么參數可以按任何順序排列,甚至丟失:

class Bar
  attr_reader :bar, :rab
  def initialize(bar: 'default', rab:)
    @bar = bar
    @rab = rab
  end
end

bar = Bar.new(rab: 'mandatory')
bar.rab
=> 'mandatory'
bar.bar
=> 'default'

bar = Bar.new(rab: 'mandatory', bar: 'custom_value')
bar.rab
=> 'mandatory'
bar.bar
=> 'custom_value'

或者直接傳遞值,如果參數應該以相同的順序給出,默認的總是在最后:

class Bar
  attr_reader :rab, :bar
  def initialize(rab, bar = 'default')
    @rab = rab
    @bar = bar
  end
end

bar = Bar.new('mandatory')
bar.rab
=> 'mandatory'
bar.bar
=> 'default'

bar = Bar.new('mandatory', 'custom_value')
bar.rab
=> 'mandatory'
bar.bar
=> 'custom_value'

你不能用 Structs 做任何事情,除非你以這種超級冗長的方式為你的參數設置默認值:

A = Struct.new(:a, :b, :c) do
  def initialize(a:, b: 2, c: 3)
    super(a, b, c)
  end
end

(示例取自這個答案

您可以在 Struct 中定義方法

Foo = Struct.new(:foo) do
  def method(argument)
    # do something with argument
    end
  end
end

結構對於創建數據對象很有用,就像其中一個答案中提到的點示例一樣。

我有時會使用它們以簡單的方式在測試中創建假貨和模擬。 有時 RSpec allow(foo).to receive(:blah)等會變得有點冗長,使用 Struct 非常簡單。

Struct 是用於創建類的 Ruby 簡寫。 在適用的情況下使用 Struct 可以簡化您的代碼。 https://www.rubytapas.com/2012/11/07/episode-020-struct/ 上有一個很好的討論

我想@sam_forgot 建議的基准。 這種比較不是很公平。 如今,類和結構都支持關鍵字參數。 在每個上使用關鍵字參數會產生相反的效果,正如您從我的示例結構中看到的,帶有關鍵字參數的性能與類沒有太大的不同。

require 'benchmark'

REP=1000000

SUser = Struct.new(:name, :age)
SUserK = Struct.new(:name, :age, keyword_init: true)

DATA = { name: "Harry", age: 75 }
DATA2 = DATA.values

class CUser
  attr_accessor :name, :age
  def initialize(name, age)
    @name = name
    @age = age
  end
end

class CUserK
  attr_accessor :name, :age
  def initialize(name:, age:)
    @name = name
    @age = age
  end
end

Benchmark.bmbm do |x|
  x.report 'Struct create and access, without keyword arguments' do
    REP.times do
      user = SUser.new(DATA)
      "#{user.name} - #{user.age}"
    end
  end

  x.report 'Struct create and access, with keyword arguments' do
    REP.times do
      user = SUserK.new(**DATA)
      "#{user.name} - #{user.age}"
    end
  end

  x.report 'Class create and access, without keyword arguments' do
    REP.times do
      user = CUser.new(*DATA2)
      "#{user.name} - #{user.age}"
    end
  end 

  x.report 'Class create and access, with keyword arguments' do
    REP.times do
      user = CUserK.new(DATA)
      "#{user.name} - #{user.age}"
    end
  end
end

Rehearsal ---------------------------------------------------------------------------------------
Struct create and access, without keyword arguments   3.484609   0.011974   3.496583 (  3.564523)
Struct create and access, with keyword arguments      0.965959   0.005543   0.971502 (  1.007738)
Class create and access, without keyword arguments    0.624603   0.003999   0.628602 (  0.660931)
Class create and access, with keyword arguments       0.901494   0.004926   0.906420 (  0.952149)
------------------------------------------------------------------------------ total: 6.003107sec

                                                          user     system      total        real
Struct create and access, without keyword arguments   3.300488   0.010372   3.310860 (  3.339511)
Struct create and access, with keyword arguments      0.876742   0.004354   0.881096 (  0.903551)
Class create and access, without keyword arguments    0.553393   0.003962   0.557355 (  0.568985)
Class create and access, with keyword arguments       0.831672   0.004811   0.836483 (  0.850224)

有相當大的實際性能差異,ruby 2.6.3p62 中的示例行為:

                          user       system     total     real
Struct create and access  3.052825   0.005204   3.058029  (3.066316)
Class create and access   0.738605   0.001467   0.740072  (0.743738)

示例代碼:

require 'benchmark'

REP=1000000
SUser = Struct.new(:name, :age)
DATA = { name: "Harry", age: 75 }

class User
  attr_accessor :name, :age
  def initialize(name:, age:)
    @name = name
    @age = age
  end
end

Benchmark.bm 20 do |x|
  x.report 'Struct create and access' do
    REP.times do
      user = SUser.new(DATA)
      "#{user.name} - #{user.age}"
    end
  end
  x.report 'Class create and access' do
    REP.times do
      user = User.new(DATA)
      "#{user.name} - #{user.age}"
    end
  end
end

使用 Struct 工具而不是創建 ruby class 的一些特性如下:

  1. 一個 ruby class 需要 8 行來定義所有必要的屬性訪問器和構造方法,而 Struct 只需要一行代碼

  2. 將 Struct 分配給變量后,我可以使用類似 Hash 的下標語法獲取和設置值,在其中,我可以互換使用符號或字符串作為鍵

  3. 結構有一個相等運算符。 Struct 定義它使得具有相等屬性的實例被認為是相等的

  4. 與使用 attr_accessor 定義的屬性不同,Structs 可以使用 members(將返回實例屬性)、each 或 each_pair(您可以在其中迭代屬性的名稱及其值)等方法自省和迭代它們的屬性

  5. 結構還包括可枚舉,因此我們也有完整的可枚舉方法

資料來源: 優美的開發網站

在系統性能方面, 本文肯定類比結構更快

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM