#0013 Design Pattern in Ruby
This log covers 14 design pattern (out of 21 original GoF patterns) in Ruby.1 These patterns are Template Method, Strategy, Observer, Composite, Iterator, Command, Adapter, Proxy, Decorator, Singleton, Factory Method, Abstract Factory, Builder and Interpreter.
Meta-design patterns,2
- Separate out the things that change from those that stay the same
- Program to an interface over inheritance
- Prefer composition over inheritance
- Delegate
Bonus Point,
- You ain’t gonna need it
Template Method
Template Method builds an abstract base class with a skeletal methods (Template Methods). The abstract class controls the higher-level processing through the template method; the subclass fill in the details.
Before using Template Method pattern,
class Report
def initialize
@title = 'Report Title'
@text = ['lorem...', 'lorem...']
end
def output_report(format)
if format == :plain
# Output plain content
elsif format == :html
# Output HTML header
else
raise "Unknown format: #{format}"
end
@text.each do |line|
if format == :plain
# Output plain
else
# Output HTML body
end
end
if format == :html
# Output HTML footer
end
end
end
Code above mixes up code that is changing with code that is not changing.
Code below implements class above using Template Method pattern,
class Report
def initialize
@title = 'Report Title'
@text = ['lorem...', 'lorem...']
end
def output_report
output_start
output_head
output_body_start
output_body
output_body_end
output_end
end
def output_body
@text.each do |line|
output_line(line)
end
end
def output_start
raise 'Called abstract method: output_start'
end
def output_head
raise 'Called abstract method: output_head'
end
def output_body_start
raise 'Called abstract method: output_body_start'
end
def output_line(line)
raise 'Called abstract method: output_line'
end
def output_body_end
raise 'Called abstract method: output_body_end'
end
def output_end
raise 'Called abstract method: output_end'
end
end
class PlainTextReport < Report
def output_start
end
def output_head
puts("**** #{@title} ****")
puts
end
def output_body_start
end
def output_line(line)
puts(line)
end
def output_body_end
end
def output_end
end
end
report = PlainTextReport.new
report.output_report
Hook Methods are non-abstract methods that can be overridden in the concrete classes. It allow concrete class to override the basic implementation or accept the default.
This “I am what I am” approach to typing has been called duck typing. If it looks like a duck and quacks like a duck, then it is a duck.
Duck Typing is a trade-off between compile-time safety and (code clarity and programming flexibility).
Statically typed (e.g. Java) languages are like aristocracy (/ˌarɪˈstɒkrəsi/, 贵族), family tree matters. Dynamically typed (e.g. Ruby) languages are like meritocracies (/ˌmɛrɪˈtɒkrəsi/, 精英管理体制), which only care what a object can do.
Using and Abusing
Using
Start with one variation and refactor the method that will become the template method so that it calls other methods for the variable parts of the algorithm. Create subclass for the first case (Superclass) and move the specific implementation into the subclass.
Abusing
Overdo things in an effort to cover every possibility. The Template Pattern is at its best when it is at its leanest (Everything methods are there for a reason). Also Use hook methods with caution.
Strategy
…