no need

Jared Carroll

Ruby’s modules give us opportunities to eliminate unnecessary classes from our designs.

In this example we have a citation, something that could be found in any academic paper, and 2 different ways of formatting it

  1. MLA (Modern Language Association)
  2. CMS (Chicago Manual of Style)

The obvious implementation ported from say Java to Ruby would look like:

class Citation

  class << self

    def mla
      new MLAFormat.new
    end

    def cms
      new CMSFormat.new
    end

  end

  attr_reader :format

  def initialize(format)
    @format = format
  end

  def to_s
    format.format self
  end

end

class Format

  def format(citation)
    raise 'must be implemented with specific citation formatting'
  end

end

class MLAFormat

  def format(citation)
    'MLA format'
  end

end

class CMSFormat

  def format(citation)
    'CMS format'
  end

end

citation = Citation.new MLAFormat.new
puts citation => 'MLA format'

citation = Citation.new CMSFormat.new
puts citation => 'CMS format'

citation = Citation.mla
puts citation => 'MLA format'

citation = Citation.cms
puts citation => 'CMS format'

Here our Citation class delegates the formatting specifics to a Format object. And I also created some class methods on Citation to make creating a Citation with a specific format cleaner and simpler.

Now do the various Format classes need to be classes? I’d say no, they don’t require initialization and they have no state; they’re just behavior.

In Ruby we can use modules instead.

class Citation

  class << self

    def mla
      citation = new
      class << citation
        include MLAFormat
      end
      citation
    end

    def cms
      citation = new
      class << citation
        include CMSFormat
      end
      citation
    end

  end

  def to_s
    format
  end

end

module MLAFormat

  def format
    'MLA format'
  end

end

module CMSFormat

  def format
    'CMS format'
  end

end

citation = Citation.new
class << citation
  include MLAFormat
end
puts citation => 'MLA format'

citation = Citation.new
class << citation
  include CMSFormat
end
puts citation => 'CMS format'

citation = Citation.mla
puts citation => 'MLA format'

citation = Citation.cms
puts citation => 'CMS format'

Here we mixin to a Citation object at runtime a specific format module. The Citation#to_s method is implemented in terms of #format, who’s implementation must be mixed in via a format module. So we’ve eliminated the abstract superclass, Format, changed 2 keywords from class to module and also eliminated the single citation argument from each Format module’s #format method.

Even though there’s not much difference between the 2 methods, I think using modules is more accurate because the classes just aren’t necessary.