Em geral, quais são as vantagens e desvantagens de usar um OpenStruct em comparação com um Struct? Que tipo de casos de uso geral se encaixaria em cada um deles?
Em geral, quais são as vantagens e desvantagens de usar um OpenStruct em comparação com um Struct? Que tipo de casos de uso geral se encaixaria em cada um deles?
Respostas:
Com um OpenStruct
, você pode criar arbitrariamente atributos. UMAStruct
, por outro lado, deve ter seus atributos definidos quando você o cria. A escolha de um sobre o outro deve basear-se principalmente em se você precisa adicionar atributos posteriormente.
A maneira de pensar sobre eles é como o meio termo do espectro entre os Hashes de um lado e as classes do outro. Elas implicam uma relação mais concreta entre os dados do que a Hash
, mas não possuem os métodos de instância como uma classe. Um monte de opções para uma função, por exemplo, faz sentido em um hash; eles são apenas vagamente relacionados. Um nome, email e número de telefone necessários para uma função podem ser empacotados juntos em um Struct
ou OpenStruct
. Se esse nome, email e número de telefone precisassem de métodos para fornecer o nome nos formatos "Primeiro Último" e "Último, Primeiro", você deverá criar uma classe para lidar com isso.
class Point < Struct.new(:x, :y); methods here; end
Point = Struct.new(:x, :y) { methods here }
. ( fonte ) Obviamente, { ... }
pode ser escrito como um bloco de várias linhas ( do ... end
) e, eu acho, essa é a maneira preferida.
Outra referência:
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
Para o impaciente que deseja ter uma idéia dos resultados do benchmark, sem executá-los, eis a saída do código acima (em um 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)
ATUALIZAR:
A partir do Ruby 2.4.1, OpenStruct e Struct estão muito mais próximos em velocidade. Consulte https://stackoverflow.com/a/43987844/128421
ANTERIORMENTE:
Para completar: Struct vs. Class vs. Hash vs. OpenStruct
Executando código semelhante ao do burtlo, no Ruby 1.9.2, (1 de 4 núcleos x86_64, 8 GB de RAM) [tabela editada para alinhar colunas]:
criando 1 Mio Structs: 1,43 s, 219 MB / 90 MB (virt / res) criando 1 instância da classe Mio: 1,43 s, 219 MB / 90 MB (virt / res) criando 1 Mio Hashes: 4,46 s, 493 MB / 364 MB (virt / res) criando 1 Mio OpenStructs: 415,13 s, 2464 MB / 2,3 GB (virt / res) # ~ 100x mais lento que o Hashes criando 100K OpenStructs: 10,96 s, 369 MB / 242 MB (virt / res)
Os OpenStructs consomem muita memória e muita memória e não se adaptam bem a grandes conjuntos de dados
Criar 1 Mio OpenStructs é ~ 100x mais lento que criar 1 Mio Hashes .
start = Time.now
collection = (1..10**6).collect do |i|
{:name => "User" , :age => 21}
end; 1
stop = Time.now
puts "#{stop - start} seconds elapsed"
Os casos de uso para os dois são bem diferentes.
Você pode pensar na classe Struct no Ruby 1.9 como equivalente à struct
declaração em C. No Ruby, Struct.new
um conjunto de nomes de campos é argumentos e retorna uma nova classe. Da mesma forma, em C, umstruct
declaração utiliza um conjunto de campos e permite que o programador use o novo tipo complexo, como faria com qualquer tipo interno.
Rubi:
Newtype = Struct.new(:data1, :data2)
n = Newtype.new
C:
typedef struct {
int data1;
char data2;
} newtype;
newtype n;
A classe OpenStruct pode ser comparada a uma declaração de estrutura anônima em C. Ele permite que o programador crie uma instância de um tipo complexo.
Rubi:
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;
Aqui estão alguns casos de uso comuns.
O OpenStructs pode ser usado para converter facilmente hashes em objetos únicos que respondem a todas as chaves de hash.
h = { a: 1, b: 2 }
o = OpenStruct.new(h)
o.a = 1
o.b = 2
Estruturas podem ser úteis para definições de classes abreviadas.
class MyClass < Struct.new(:a,:b,:c)
end
m = MyClass.new
m.a = 1
O OpenStructs usa significativamente mais memória e apresenta desempenho mais lento do que o Structs.
require 'ostruct'
collection = (1..100000).collect do |index|
OpenStruct.new(:name => "User", :age => 21)
end
No meu sistema, o código a seguir foi executado em 14 segundos e consumiu 1,5 GB de memória. Sua milhagem pode variar:
User = Struct.new(:name, :age)
collection = (1..100000).collect do |index|
User.new("User",21)
end
Isso terminou quase instantaneamente e consumiu 26,6 MB de memória.
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
Dê uma olhada na API em relação ao novo método. Muitas diferenças podem ser encontradas lá.
Pessoalmente, eu gosto bastante do OpenStruct, pois não preciso definir a estrutura do objeto de antemão e apenas adicionar o que quiser. Eu acho que essa seria sua principal (des) vantagem?
Usando o código @Robert, adiciono Hashie :: Mash ao item de referência e obtive este resultado:
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)
Na verdade, não é uma resposta para a pergunta, mas uma consideração muito importante se você se importa com o desempenho . Observe que toda vez que você cria uma OpenStruct
operação, o cache do método é limpo, o que significa que seu aplicativo terá um desempenho mais lento. A lentidão ou não não OpenStruct
é apenas sobre como ela funciona sozinha, mas as implicações que as utilizam trazem para todo o aplicativo: https://github.com/charliesome/charlie.bz/blob/master/posts/things-that -clear-rubys-method-cache.md # openstructs