login

NullObjectPattern (Ruby)

HomePage | RecentChanges | Preferences | Wikis | RubyGarden | Feed-icon-16x16

"A recursive structure, like a list or a tree, requires some special marker to define those nodes at the structure's boundary. Also interior and edge nodes should be as similar as possible since one can become the other as the structure grows or shrinks.

"Therefore: Mark the space just beyond the structure's edge with instances of an appropriate null object. Expect this object to participate in calculations by returning zero, null or empty, as appropriate for any particular algorithm." [1]

class Tree
    attr_accessor :left, :right, :value
    
    def initialize( value )
        @left = NullTree.new
        @right = NullTree.new
        @value = value
    end
    
    def size
        1 + left.size + right.size
    end

    def sum_of_values
       value + left.sum_of_values + right.sum_of_values
    end

    def product_of_values
       value * left.product_of_values * right.product_of_values
    end
end


class NullTree
    def size
        0
    end

    def sum_of_values
       0
    end

    def product_of_values
       1
    end
end

tree = Tree.new( 2 )
tree.left = Tree.new( 3 )
tree.left.right = Tree.new( 4 )
tree.left.left = Tree.new( 5 )

p tree.size
p tree.sum_of_values
p tree.product_of_values

Results in the output:

4
14
120

-- NatPryce

As a wee optimization, you could use the SingletonPattern to implement the null class.

 require 'singleton'
 class NullTree
     include Singleton
     # ...
 end

 class Tree
     def initialize( value )
         @left = NullTree.instance
         @right = NullTree.instance
         @value = value
     end 
     # ...
 end

or even...

 class Tree
     @@empty_leaf = NullTree.new
     def initialize
         @left = @right = @@empty_leaf
         ...

In Ruby, the nil value is itself an example of the NullObjectPattern. You can send the message nil? to any object. The nil value returns true and other objects return false.


I would make the NullTree?'s class a subclass of Tree and provide a predicate to check. Then all of the code for a method can be implemented in Tree and people wanting to extend it only need to extend that.

class Tree; end

NullTree = Class.new(Tree) {
  def dup() self; end
  def clone() self; end
  def null_tree?() true; end
  def inspect() 'NullTree'; end
}.new


class Tree

  def initialize(val)
    @val = val
    @left = @right = NullTree
  end

  def null_tree?() false; end

  attr_accessor :val, :left, :right

  def size
    if null_tree?
      0
    else
      1 + @left.size + @right.size
    end
  end

end

-- DevinPapineau?

That misses the whole point of the NullObjectPattern. The size method should not care whether a branch is the null_tree or not. The null_tree should return 0 from it's size method. The implementation if Tree::size would then be much simpler:

  def size
    1 + @left.size + @right.size
  end
--NatPryce.


Also see: ExampleDesignPatternsInRuby

HomePage | RecentChanges | Preferences | Wikis | RubyGarden
Edit text of this page | View other revisions
Rev 17, Last edited at September 14, 2005 05:02 am by rgNoNameGiven / host-64-47-108-26.masergy.com (diff)
Find: