5 Patterns

5.1 Value Object

An object has state (the values of its instance variables), and identity. Take for example a simple counter

class Counter
  def initialize
    @counter = 0
  end

  def increment
    @counter += 1
  end
end

counter_a = Counter.new
counter_a.increment
counter_b = Counter.new
counter_b.increment

Here we have created a class that can simply count up in increments of one, and we have instantiated it twice. We have incremented each counter once, so now the value of each counter is 1.

Their values are the same, but they are not the same object. It matters which counter we’re looking at, since later on their values might no longer be the same.

The downside is that we need to keep in mind which variables are pointing to which instances. In a large program we might get bugs that are hard to track down because another part of the program is changing things without us realizing it.

Let’s try something else! What if we make an object that cannot be changed? Problem solved! Need to change things anyway? No problem, just create a new object with the updated value, while leaving the old one intact.

To make sure we don’t violate our own rules, we will freeze all values so they can not be changed, and we only provide read-only versions of any attribute accessors.

class Treasure
  attr_reader :gold_coins, :magical_items

  def initialize(gold_coins = 0, magical_items = [])
    @gold_coins = gold_coins
    @magical_items = magical_items.freeze
  end

  def add_gold_coins(number)
    self.class.new(@gold_coins + number, @magical_items)
  end

  def add_magica_item(item)
    self.class.new(@gold_coins, @magical_items + [item])
  end

  def +(other)
    self.class.new(
      @gold_coins + other.gold_coins,
      @magical_items + other.magical_items
    )
  end
end

treasure = Treasure.new(
  946,
  ["enchanted sword", "potion of hysterical laughter"]
)
 # => #<Treasure:0x760 @gold_coins=946,
 #  @other_items=["enchanted sword", "potion of hysterical laughter"]>

treasure.gold_coins # => 946
treasure.add_gold_coins(100).gold_coins # => 1046
treasure.gold_coins # => 946

loot = Treasure.new(733, ["Pirate dagger"])

new_treasure = treasure + loot
 # => #<Treasure:0x00000002437220 @gold_coins=1679,
 # @magical_items=["enchanted sword", "potion of hysterical laughter",
 #                 "Pirate dagger"]>

The classic case for using a value object is when you have some sort of “number with extra meaning”. This can be monetary amount in a certain currency, a vector point in two or more directions, or the amount of a certain item you have in stock.

An extra benefit of using this pattern is that it will force you to follow the Query-Command separation principle. This principle says that an object method should either merely query an object (like our attr_reader methods), or be a command that performs an action. Since the only way to “alter” a value object is by returning a new instance, it’s not possible to alter the object and returning some other value. This keeps code simple and easy to understand.

Value objects will also force your functions and methods to be referentially transparent. This means that a function, given a certain input, will always return the same output, without causing any side effects, like changing internal state.

So while there aren’t many functions or lambdas involved in this pattern, it is a great first step for making our code more functional.

5.2 Function Object (to be written)

5.3 Functional Core, Imperative Shell (to be written)

5.4 Collections

Persistent Collections (to be written)

Lazy Collections (to be written)