Struct vs. OpenStruct Definition

Mit [Struct][1] and [OpenStruct][2] können Objekte erzeugt werden, deren Struktur zur Laufzeit bestimmt wird. Diese Mächtigkeit wird leider häufig unterschätzt.
Deren Flexibilität ermöglicht es, Daten zu strukturieren, ohne gleich Klassen anzulegen. Zumal die Instantiierung von Struct Objekten sogar performanter ist, als die von Klassen.

Struct

Immer dann, wenn Daten den selben Kontext betreffen, macht es Sinn, sie auch in einer Struktur zu zusammenzufassen.
So zum Beispiel bestehen Geodaten immer aus Breitengrad und Längengrad. Da liegt es nahe, sie miteinander zu verknüpfen:

Struct.new 'Geolocation', :latitude, :longitude
geolocation = Struct::Geolocation.new 53.6, 11.4
# => #<struct Struct::Geolocation latitude=53.6, longitude=11.4>
geolocation.latitude # => 53.6
geolocation.longitude # => 11.4

Structs können auf vielfältige Weise angelegt werden. Weitere Notationen:

# Zuweisung
Geolocation = Struct.new :latitude, :longitude

# Vererbung
class Geolocation < Struct.new(:latitude, :longitude)
end

Der Grund ist, dass Struct#new eine Klasse zurückgibt.
Außerdem können auch Structs angelegt werden, die Logik enthalten:

Struct.new(:latitude, :longitude) do
  def east_of_greenwich?
    longitude.positive?
  end
end

Das unterscheidet Structs zum Beispiel von Hashes.

OpenStruct

OpenStructs sind zwar nicht so performant wie Structs, aber dafür flexibler. Allerdings gehören sie nicht zum Ruby Core:

require 'ostruct'

geolocation = OpenStruct.new latitude: 53.6, longitude: 11.4
geolocation.zoom # => nil
geolocation.zoom = 14
geolocation.zoom # => 14

Zum einen kann eine Instanz ohne vorherige Definition der Struct erstellt werden, zum anderen scheint die Instanz auch auf undefinierte Methoden zu antworten. Der Grund ist, dass OpenStruct auf einem Hash basiert, und mithilfe einer #method_missing fehlende Methodendefinitionen abfängt und zur Laufzeit erstellt.
Das kann beim schnellen Mocken von experimentellen Objekten oder Test Doubles hilfreich sein.

[1] http://ruby-doc.org/core-2.4.0/Struct.html [2] https://ruby-doc.org/stdlib-2.4.0/libdoc/ostruct/rdoc/OpenStruct.html