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
- MLA (Modern Language Association)
- 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.