---
title: 'Design Patterns in the Wild: Null Object'
teaser:
tags: web,ruby,testing,good code
author: Josh Clayton
published_on: 2011-11-01
---

I knocked out a pretty decent refactoring of some of the internals of Factory
Bot this past weekend. In one of my commits, I used the Null Object pattern
to simplify some conditional logic that was spread across a class.

## What's the Null Object Pattern

The Null Object pattern describes the use of an object to define the concept
of "null" behavior. Typically, a null object will implement a similar
interface to a similar object but not actually *do* anything.

In the instance of Factory Bot, there's a `FactoryBot::Factory` object and a
`FactoryBot::NullFactory` object; both have a common interface by responding
to the instance methods `defined_traits`, `callbacks`, `attributes`,
`compile`, `default_strategy`, and `class_name`. These methods are the core of
a `FactoryBot::Factory` and it's important that the methods exist on the
`NullFactory` (we'll be calling these methods in `FactoryBot::Factory`).

## How to Use the Pattern

In Factory Bot, the `Factory` object deals with taking a handful of declared
attributes and running it, resulting in an instance of a class with values
assigned. Factory Bot supports the concept of parent factories; attributes,
callbacks, and other features get inherited, but a parent isn't required. Here's
the code before the change; as you can see, there's a ton of checking to see if
the parent exists.

```ruby
module FactoryBot
  class Factory
    def default_strategy
      @default_strategy || (parent && parent.default_strategy) || :create
    end

    def compile
      if parent
        parent.defined_traits.each {|trait| define_trait(trait) }
        parent.compile
      end
      attribute_list.ensure_compiled
    end

    protected

    def class_name
      @class_name || (parent && parent.class_name) || name
    end

    def attributes
      compile
      AttributeList.new(@name).tap do |list|
        traits.each do |trait|
          list.apply_attribute_list(trait.attributes)
        end

        list.apply_attribute_list(attribute_list)
        list.apply_attribute_list(parent.attributes) if parent
      end
    end

    def callbacks
      [traits.map(&:callbacks), @definition.callbacks].tap do |result|
        result.unshift(*parent.callbacks) if parent
      end.flatten
    end

    private

    def parent
      return unless @parent
      FactoryBot.factory_by_name(@parent)
    end
  end
end
```

Not only does it add extra lines of code (and every line of code is a liability),
but it also forces other developers reading the code to remember if a parent exists.
This context-switching across five different methods makes it hard to remember what
the actual behavior of each method is doing because certain things may or may not
be executed.

To use the pattern, all I did was create a NullFactory object and implement
the interface I knew I needed to get rid of all the conditionals. For
each method, I returned a "sensible" result; `nil` for `class_name`,
`default_strategy`, and `compile`, and I delegated the remaining few
methods (`defined_traits`, `callbacks`, and `attributes`) to `definition`.

```ruby
module FactoryBot
  class NullFactory
    attr_reader :definition

    def initialize
      @definition = Definition.new
    end

    delegate :defined_traits, :callbacks, :attributes, :to => :definition

    def compile; end
    def default_strategy; end
    def class_name; end
  end
end
```

Testing is pretty straightforward since the behavior is straightforward.

```ruby
describe FactoryBot::NullFactory do
  it { should delegate(:defined_traits).to(:definition) }
  it { should delegate(:callbacks).to(:definition) }
  it { should delegate(:attributes).to(:definition) }

  its(:compile)          { should be_nil }
  its(:default_strategy) { should be_nil }
  its(:class_name)       { should be_nil }
end
```

Now, the private instance method will always return something that behaves
like a `FactoryBot::Factory`. Perfect.

```ruby
def parent
  if @parent # the only conditional to determine if a parent exists
    FactoryBot.factory_by_name(@parent)
  else
    NullFactory.new
  end
end
```

## Results

Here are those methods after introducing the Null Object pattern.

```ruby
module FactoryBot
  class Factory
    def default_strategy
      @default_strategy || parent.default_strategy || :create
    end

    def compile
      parent.defined_traits.each {|trait| define_trait(trait) }
      parent.compile
      attribute_list.ensure_compiled
    end

    protected

    def class_name
      @class_name || parent.class_name || name
    end

    def attributes
      compile
      AttributeList.new(@name).tap do |list|
        traits.each do |trait|
          list.apply_attribute_list(trait.attributes)
        end

        list.apply_attribute_list(attribute_list)
        list.apply_attribute_list(parent.attributes)
      end
    end

    def callbacks
      [parent.callbacks, traits.map(&:callbacks), @definition.callbacks].flatten
    end

    private

    def parent
      if @parent
        FactoryBot.factory_by_name(@parent)
      else
        NullFactory.new
      end
    end
  end
end
```

The commit in Factory Bot can be found [here](https://github.com/thoughtbot/factory_bot/commit/aee300aa90a82970c1fa73df48bbc8305b99532a).
As you can see, the logic is simplified greatly across all the methods because
there's no more conditional checking. The developer reading this code doesn't
have to care if a parent is assigned or not because he can be sure that the
parent, regardless of what it is, will behave in the correct manner when that
method is executed.

A couple of months ago, [Gabe](http://thoughtbot.com/about/#gberkewilliams)
and I implemented the same pattern in
[Kumade](https://github.com/thoughtbot/kumade) by [introducing a `NoopPackager`](https://github.com/thoughtbot/kumade/commit/d854184744a70d1786e65a3a4bbd6429988b7f00#lib/kumade/packagers/noop_packager.rb).

Have you used the Null Object pattern recently? If you haven't, your code is
probably ripe for some Null Object pattern disruption!

* * *

**Disclaimer:**

Looking for FactoryGirl? The library was renamed in 2017.
[Project name history can be found here.](https://github.com/thoughtbot/factory_bot/blob/master/NAME.md)
