web-dev-qa-db-fra.com

Quand devrais-je utiliser Struct vs. OpenStruct?

En général, quels sont les avantages et les inconvénients de l’utilisation d’un OpenStruct par rapport à un Struct? Quel type de cas d'utilisation générale conviendrait à chacun d'entre eux?

177
ehsanul

Avec un OpenStruct, vous pouvez créer des attributs de manière arbitraire. Un Struct, en revanche, doit avoir ses attributs définis lors de sa création. Le choix de l’un par rapport à l’autre doit reposer principalement sur la nécessité d’ajouter des attributs ultérieurement.

La façon de les considérer est le juste milieu du spectre entre les hachages d'un côté et les classes de l'autre. Ils impliquent une relation plus concrète entre les données qu'un Hash, mais ils ne disposent pas des méthodes d'instance comme le ferait une classe. Un ensemble d'options pour une fonction, par exemple, a un sens dans un hachage; ils ne sont que vaguement liés. Un nom, une adresse électronique et un numéro de téléphone requis par une fonction peuvent être regroupés dans un fichier Struct ou OpenStruct. Si ce nom, cet email et ce numéro de téléphone nécessitaient des méthodes pour fournir le nom dans les formats "Premier Dernier" et "Dernier, Premier", vous devez créer une classe pour le gérer.

167
Pesto

Autre repère:

require 'benchmark'
require 'ostruct'

REP = 100000

User = Struct.new(:name, :age)

USER = "User".freeze
AGE = 21
HASH = {:name => USER, :age => AGE}.freeze

Benchmark.bm 20 do |x|
  x.report 'OpenStruct slow' do
    REP.times do |index|
       OpenStruct.new(:name => "User", :age => 21)
    end
  end

  x.report 'OpenStruct fast' do
    REP.times do |index|
       OpenStruct.new(HASH)
    end
  end

  x.report 'Struct slow' do
    REP.times do |index|
       User.new("User", 21)
    end
  end

  x.report 'Struct fast' do
    REP.times do |index|
       User.new(USER, AGE)
    end
  end
end

Pour les impatients qui veulent avoir une idée des résultats du benchmark, sans les exécuter eux-mêmes, voici la sortie du code ci-dessus (sur un MB Pro 2.4GHz i7)

                          user     system      total        real
OpenStruct slow       4.430000   0.250000   4.680000 (  4.683851)
OpenStruct fast       4.380000   0.270000   4.650000 (  4.649809)
Struct slow           0.090000   0.000000   0.090000 (  0.094136)
Struct fast           0.080000   0.000000   0.080000 (  0.078940)
82
Robert Klemme

UPDATE:

A partir de Ruby 2.4.1 OpenStruct et Struct sont beaucoup plus rapides en vitesse. Voir https://stackoverflow.com/a/43987844/128421

PRECEDENT:

Pour être complet: Struct vs. Classe vs. Hachage vs OpenStruct

Exécution d'un code similaire à celui de burtlo, on Ruby 1.9.2, (un des 4 cœurs x86_64, 8 Go de RAM)) [tableau modifié pour aligner les colonnes]:

 création de 1 Mio Structs: 1,43 s, 219 Mo/90MB (virt/res) 
 création de 1 Mio instances de classe: 1,43 s, 219 Mo/90 Mo (virt/res) 
 création de 1 Mio Hashes: 4,46 secondes, 493 Mo/virtuels (virt/res) 
 création de 1 Mio OpenStructs: 415,13 s, 2464 Mo/2,3 Go (virtuels/res) # ~ 100x plus lent que Hash 
 création de 100K OpenStructs: 10,96 s, 369 Mo/242 Mo (virt/res) 

OpenStructs utilise sloooooow et une mémoire intensive , et ne s'adapte pas correctement aux grands ensembles de données

La création de 1 Mio OpenStructs est ~ 100x plus lente que la création de 1 Mio hachages .

start = Time.now

collection = (1..10**6).collect do |i|
  {:name => "User" , :age => 21}
end; 1

stop = Time.now

puts "#{stop - start} seconds elapsed"
57
Tilo

Les cas d'utilisation pour les deux sont assez différents.

Vous pouvez considérer la classe Struct dans Ruby 1.9 comme un équivalent de la déclaration struct en C. Dans Ruby Struct.new prend un ensemble de noms de champs comme arguments et retourne une nouvelle classe. De même, en C, une déclaration struct prend un ensemble de champs et permet au programmeur d'utiliser le nouveau type complexe comme il le ferait pour tout type intégré.

Rubis:

Newtype = Struct.new(:data1, :data2)
n = Newtype.new

C:

typedef struct {
  int data1;
  char data2;
} newtype;

newtype n;

La classe OpenStruct peut être comparée à une déclaration de structure anonyme en C. Elle permet au programmeur de créer une instance d'un type complexe.

Rubis:

o = OpenStruct.new(data1: 0, data2: 0) 
o.data1 = 1
o.data2 = 2

C:

struct {
  int data1;
  char data2;
} o;

o.data1 = 1;
o.data2 = 2;

Voici quelques cas d'utilisation courants.

OpenStructs peut être utilisé pour convertir facilement des hachages en objets uniques répondant à toutes les clés de hachage.

h = { a: 1, b: 2 }
o = OpenStruct.new(h)
o.a = 1
o.b = 2

Les structures peuvent être utiles pour les définitions de classes abrégées.

class MyClass < Struct.new(:a,:b,:c)
end

m = MyClass.new
m.a = 1
33
skryl

OpenStructs utilise beaucoup plus de mémoire et est plus lent que Struct.

require 'ostruct' 

collection = (1..100000).collect do |index|
   OpenStruct.new(:name => "User", :age => 21)
end

Sur mon système, le code suivant s’est exécuté en 14 secondes et a consommé 1,5 Go de mémoire. Votre kilométrage peut varier:

User = Struct.new(:name, :age)

collection = (1..100000).collect do |index|
   User.new("User",21)
end

Cela s'est terminé presque instantanément et a consommé 26,6 Mo de mémoire.

24
burtlo

Struct:

>> s = Struct.new(:a, :b).new(1, 2)
=> #<struct a=1, b=2>
>> s.a
=> 1
>> s.b
=> 2
>> s.c
NoMethodError: undefined method `c` for #<struct a=1, b=2>

OpenStruct:

>> require 'ostruct'
=> true
>> os = OpenStruct.new(a: 1, b: 2)
=> #<OpenStruct a=1, b=2>
>> os.a
=> 1
>> os.b
=> 2
>> os.c
=> nil
6
Dorian

Regardez l'API en ce qui concerne la nouvelle méthode. On y trouve beaucoup de différences.

Personnellement, j'aime bien OpenStruct, car je n'ai pas à définir la structure de l'objet à l'avance, mais à ajouter des éléments à ma guise. Je suppose que ce serait son principal (dis) avantage?

5
Omar Qureshi

À l'aide du code @Robert, j'ai ajouté Hashie :: Mash à l'élément de référence et obtenu le résultat suivant:

                           user     system      total        real
Hashie::Mash slow      3.600000   0.000000   3.600000 (  3.755142)
Hashie::Mash fast      3.000000   0.000000   3.000000 (  3.318067)
OpenStruct slow       11.200000   0.010000  11.210000 ( 12.095004)
OpenStruct fast       10.900000   0.000000  10.900000 ( 12.669553)
Struct slow            0.370000   0.000000   0.370000 (  0.470550)
Struct fast            0.140000   0.000000   0.140000 (  0.145161)
3
Donny Kurnia