Design
Patterns
What is a pattern?
"a regular and intelligible form or sequence discernible in the way in which something happens or is done"
a bit of history...
by Christopher Alexander, Sara Ishikawa and Murray Silverstein (1977)
Patterns describe a problem and then offer a solution.
The
Keystone
Pattern
Problem:
We need an arch to bear weight
Solution:
Use a keystone
Ok, what's this got to do with software?
"The Gang of Four"
(1995)
2 key ideas
Program to an interface, not an implementation
Favour
object composition
over
class inheritance
23 classic patterns
Creational
Abstract Factory
Builder
Factory Method
Prototype
Singleton
Structural
Adapter
Bridge
Composite
Decorator
Facade
Flyweight
Proxy
Behavioural
Chain of responsibility
Command
Interpreter
Iterator
Mediator
Memento
Observer
State
Strategy
Template
Visitor
Why learn about patterns?
Patterns form a common language
Patterns allow you to benefit from the wisdom of many previous designers
Don't reinvent the wheel
The
Adapter
Pattern
Problem:
Classes with incompatible interfaces
Solution:
Wrap and convert the interface
class SquarePeg
attr_reader :width
def initialize(width)
@width = width
end
end
class RoundPeg
attr_reader :radius
def initialize(radius)
@radius = radius
end
end
class RoundHole
attr_reader :radius
def initialize(radius)
@radius = radius
end
def peg_fits?(peg)
peg.radius <= radius
end
end
round_hole = RoundHole.new(10)
round_peg = RoundPeg.new(8)
round_hole.peg_fits?(round_peg)
# => true
square_peg = SquarePeg.new(8)
round_hole.peg_fits?(square_peg)
# => NoMethodError: undefined method `radius' for #<SquarePeg:0x007ff9f108fb68 @width=8>
class SquarePegAdaptor
def initialize(square_peg)
@peg = square_peg
end
def radius
Math.sqrt(((@peg.width / 2) ** 2) * 2)
end
end
round_hole = RoundHole.new(4.0)
4.upto(7) do |i|
peg = SquarePegAdaptor.new(SquarePeg.new(i))
puts "Square peg of size #{i} fits: #{round_hole.peg_fits?(peg)}"
end
# Square peg of size 4 fits: true
# Square peg of size 5 fits: true
# Square peg of size 6 fits: false
# Square peg of size 7 fits: false
The
Facade
Pattern
Problem:
Very complex implementation detail
Solution:
Create a facade with a simplified interface
require 'net/http'
require 'uri'
url = URI.parse('http://www.google.com')
res = Net::HTTP.start(url.host, url.port) do |http|
http.get('/index.html')
end
puts res.body
require "open-uri"
puts open("http://www.google.com").read
DSLs are a great example of facades
When should I use a design pattern?
Design patterns are a tool, not a one size fits all solution
Don't be a pattern abuser!
But, do try to identify when they would be useful
You've been using design patterns without even knowing it!
DSLs (Sinatra, RSpec) - Facade pattern
Ruby symbols - Flyweight pattern
Rack middleware - Decorator pattern
jQuery - Observer pattern
ActiveRecord - Adapter pattern
ActiveRecord - ActiveRecord pattern
You don't need to know all the patterns
Just be aware of them
Some quotes:
Computer Science is a science of abstraction - creating the right model for a problem and devising the appropriate mechanizable techniques to solve it."
A. Aho and J. Ullman, 1992
"Programs must be written for people to read, and only incidentally for machines to execute."
H. Abelson and G. Sussman, 1985
Further reading: Google
Just kidding.
Design Patterns in Ruby
by Russ Olsen
Patterns of Enterprise Architecture
by Martin Fowler
Learning JavaScript Design Patterns
by Addy Osmani
with huge thanks to Henry Garner for open sourcing his original presentation
Design patterns
By Spike L
Design patterns
Hiring week talk
- 1,100