Posted over 5 years ago. Visible to the public.
Value Object
The main purpose of a value object is answer meaningful questions for our application.
Copyclass Temperature include Comparable def initialize(temperature) @temperature = Float(temperature) # in case of strings, hashes, arrays we should explicitly .freeze them end def cold? temperature < COLD_THRESHOLD # Celsius degrees end def <=>(other) temperature <=> other.temperature end def to_s temperature.to_s end # Hashing implementation (transformation of the object_id into an integer) # which used in checking for unique entries. def hash temperature.hash # the value must remain constant during the life span of the process end alias eql? == protected COLD_THRESHOLD = new(15) attr_reader :temperature end Temperature.new(3).cold? # => true Temperature.new(3) > Temperature.new(20) # => false Temperature.new(3) == Temperature.new(3) # => true [Temperature.new(7), Temperature.new(20), Temperature.new(20)].uniq # => [7.0, 20.0]
We use a value @temperature
to define its identity in hashing implementation? That's right! Because value objects define their identity by their value. 20°C is.. 20°C – and it will be always equal to 20°C.
Another implementation using a gem
There is a gem called Values Archive which is a tiny library for creating simple immutable value objects for ruby. Here is an example from above but with values gem.
Copyrequire 'values' class Temperature < Value.new(:temperature) include Comparable def cold? self < COLD_THRESHOLD end def <=>(other) temperature <=> other.temperature end def to_s "#{temperature.to_s} °C" end protected COLD_THRESHOLD = new(15) end Temperature.new(3).cold? # => true Temperature.new(3) > Temperature.new(20) # => false Temperature.new(3) == Temperature.new(3) # => true [Temperature.new(7), Temperature.new(20), Temperature.new(20)].uniq # => [#<Temperature temperature=7>, #<Temperature temperature=20>]