W
Woody Peterson
I have a design question inspired by Rails 3 internals. In Design
Patterns in Ruby, the Strategy pattern is cited as a way to vary an
algorithm following the oft-spoken "prefer composition over inheritance"
advice. For a concrete example, let's say we wanted to fight in the
matrix:
class Person
attr_accessor :skills
def initialize(skills)
@skills = skills
end
def fight
@skills.each { |skill| skill.invoke }
end
end
class KungFu
def invoke
puts "5-fisted punch"
end
end
class Dodge
def invoke
puts "bullet-time"
end
end
class SeeTheMatrix
def invoke
puts "woah"
end
end
me = Person.new([])
me.fight # i've got nothing
neo = Person.new([KungFu.new, Dodge.new, SeeTheMatrix.new])
neo.fight # "5-fisted punch"
# "bullet-time"
# "woah"
Rails 3 might fight in the matrix like this:
class Person
def fight
end
end
module KungFu
def fight
puts "5-fisted punch"
super
end
end
module Dodge
def fight
puts "bullet-time"
super
end
end
class Neo < Person
include KungFu
include Dodge
def fight
puts "woah"
super
end
end
Neo.new.fight # "woah"
# "bullet-time"
# "5-fisted punch"
Maybe a class-based inheritance implementation would be:
class Person
def fight
end
end
class PersonKnowsKungFu < Person
def fight
"5-fisted punch"
super
end
end
class PersonMovesLikeThem < PersonKnowsKungFu
def fight
"bullet-time"
super
end
end
class Neo < PersonMovesLikeThem
def fight
"woah"
super
end
end
The goal of all of these would be to easily add new skills and
characters. Obviously the class-based inheritance example is brittle
because it'd take a refactoring to give someone bullet-time without
kung-fu. I'm mostly interested in the differences between the
"leaf-class-mixins" strategy and the strategy pattern. "Prefer
composition over inheritance" would suggest you should use the strategy
pattern, but mixing in modules sure seems like a simple and ruby-ish way
of achieving the same thing. The only difference I can see is that the
strategy pattern allows for run-time skill manipulation, which I don't
find myself needing often. What other advantages would I gain from
preferring composition here?
Patterns in Ruby, the Strategy pattern is cited as a way to vary an
algorithm following the oft-spoken "prefer composition over inheritance"
advice. For a concrete example, let's say we wanted to fight in the
matrix:
class Person
attr_accessor :skills
def initialize(skills)
@skills = skills
end
def fight
@skills.each { |skill| skill.invoke }
end
end
class KungFu
def invoke
puts "5-fisted punch"
end
end
class Dodge
def invoke
puts "bullet-time"
end
end
class SeeTheMatrix
def invoke
puts "woah"
end
end
me = Person.new([])
me.fight # i've got nothing
neo = Person.new([KungFu.new, Dodge.new, SeeTheMatrix.new])
neo.fight # "5-fisted punch"
# "bullet-time"
# "woah"
Rails 3 might fight in the matrix like this:
class Person
def fight
end
end
module KungFu
def fight
puts "5-fisted punch"
super
end
end
module Dodge
def fight
puts "bullet-time"
super
end
end
class Neo < Person
include KungFu
include Dodge
def fight
puts "woah"
super
end
end
Neo.new.fight # "woah"
# "bullet-time"
# "5-fisted punch"
Maybe a class-based inheritance implementation would be:
class Person
def fight
end
end
class PersonKnowsKungFu < Person
def fight
"5-fisted punch"
super
end
end
class PersonMovesLikeThem < PersonKnowsKungFu
def fight
"bullet-time"
super
end
end
class Neo < PersonMovesLikeThem
def fight
"woah"
super
end
end
The goal of all of these would be to easily add new skills and
characters. Obviously the class-based inheritance example is brittle
because it'd take a refactoring to give someone bullet-time without
kung-fu. I'm mostly interested in the differences between the
"leaf-class-mixins" strategy and the strategy pattern. "Prefer
composition over inheritance" would suggest you should use the strategy
pattern, but mixing in modules sure seems like a simple and ruby-ish way
of achieving the same thing. The only difference I can see is that the
strategy pattern allows for run-time skill manipulation, which I don't
find myself needing often. What other advantages would I gain from
preferring composition here?