The document discusses design patterns and their applications in Ruby. It begins with a pop quiz about design patterns, then covers practical examples of strategy, state, composite, adapter, proxy, decorator, mediator, bridge, observer, and prototype patterns in Ruby. It notes some alternative implementations and deprecated patterns in Ruby. The document concludes by recapping that design patterns help with maintainability and serve as a design communication tool.
Design Patterns for Rubyists: Strategies, States, and More
1. +
Design Patterns for Rubyists
Who Said Dynamic Languages Can’t Have Patterns?
Software Design Trilogy – Part II
Andy Maleh / Software Engineer / Groupon
2. +
Outline
Design Patterns Pop Quiz
Practical Applications of Design Patterns in Ruby
Alternative Implementations in Ruby
Deprecated Design Patterns in Ruby
Summary
3. +
Design Patterns Pop Quiz
Who are the Gang of Four?
1. Group of communist party leaders in China
2. Rock band
3. Bunch of computer programmers
4. +
Design Patterns Pop Quiz
What is a Design Pattern?
Reusable solution to a common problem encountered at the
software design level of abstraction.
5. +
Design Patterns Pop Quiz
What does a Design Pattern consist of?
Name
Problem
Solution
Consequences
6. +
Practical Applications of Design
Patterns in Ruby
Strategy
Problem: Need to customize behavior with multiple
variations at run time, and possibly allow clients to
provide their own customization as well
Solution:
Encapsulate each behavior variation in a
Strategy object
7. +
Practical Applications of Design
Patterns in Ruby - Strategy
Example:
Problem: A customer can submit a payment in one of multiple
methods, such as credit card, cashier’s check, and paypal.
The code is littered with conditionals that alter behavior per
payment type, making it quite involved and difficult to add a
new payment type in the future or maintain existing code
Solution: Separate each payment type into its own
PaymentStrategy object and let it handle that payment’s
details
8. +
Practical Applications of Design
Patterns in Ruby - Strategy
Code:
9. +
Practical Applications of Design
Patterns in Ruby
State
Problem: An abstraction transitions through multiple
states and behaves differently under each
Solution:
Encapsulate behavior variations in multiple
State objects
10. +
Practical Applications of Design
Patterns in Ruby - State
Example:
Problem: An order passes through multiple states
before it is processed:
new unverified shipping processed
It can also be made inactive
Developers are tired of all the conditionals littering their
codebase everywhere about the different behaviors
associated with each state
Solution: Encapsulate behavior associated with each
state in its own OrderState object, including the
transitions
11. +
Practical Applications of Design
Patterns in Ruby - State
Code:
12. +
Practical Applications of Design
Patterns in Ruby
Composite
Problem: An object may be composed of other
objects, like chapters consisting of sections in a
book, yet both parent and child objects share
common behavior
Solution:
Define a child object superclass and a
parent object superclass. The parent object
superclass extends the child object superclass to
share common behavior.
13. +
Practical Applications of Design
Patterns in Ruby - Composite
Example:
Problem: An educational website needs to handle
behavior for storing and formatting a hierarchy of
information consistently: chapters, sections, and pages
Solution: Define a Node superclass for all elements and
a ParentNode superclass for all parent elements
(chapters and sections)
14. +
Practical Applications of Design
Patterns in Ruby - Composite
Code:
15. +
Practical Applications of Design
Patterns in Ruby
Adapter
Problem: An existing object (object A) with a
particular interface needs to interact with another
object (object B) that requires a different interface
Solution:
Create an Adapter object that wraps object
A to adapt its interface to what is expected of object
B.
16. +
Practical Applications of Design
Patterns in Ruby - Adapter
Example:
Problem: Authentication library supports outside
strategies with an authenticate method. We have a
CompanyLogin implementation that has a login method
instead.
Solution: Create an CompanyAuthenticationAdapter
that wraps CompanyLogin and provides an
authenticate method instead that calls login
17. +
Practical Applications of Design
Patterns in Ruby
Proxy
Problem: there is a need to alter access to an object
for aspect oriented reasons like caching, security, or
performance, and ensure that this controlled access
is enforced across the app
Solution:Create a Proxy object that wraps the
original object and alters access to it by adding a
caching, security, or performance related layer
18. +
Practical Applications of Design
Patterns in Ruby - Proxy
Example:
Problem: we need to cache data retrieved from a web
service via a client object
Solution: Create a Proxy object that wraps the web
service client and have it cache incoming data
19. +
Practical Applications of Design
Patterns in Ruby
Decorator
Problem:an object capabilities need to be enhanced
without modifying the object
Solution:Create a Decorator object that wraps the
original object and adds the extra capabilities
20. +
Practical Applications of Design
Patterns in Ruby - Decorator
Example:
Problem: the view needs to render some model
attributes as is as well as render some formatted
versions of the model attributes (example a client
description that includes name and address)
Solution: Create a Decorator object that wraps the
model providing its original attribute methods as well as
extra formatted versions
21. +
Practical Applications of Design
Patterns in Ruby - Decorator
Code:
22. +
Practical Applications of Design
Patterns in Ruby - Decorator
Code:
23. +
Practical Applications of Design
Patterns in Ruby
Mediator
Problem: there is a need to decouple multiple
objects from each other while orchestrating a
particular part of work
Solution:introduce a Mediator object that is
responsible for orchestrating the work between
multiple objects
24. +
Practical Applications of Design
Patterns in Ruby - Mediator
Example:
Problem: work associated with processing an order
requires orchestration of many objects, such as
Order, PaymentGateway, AddressService, and has
gone beyond the scope of what an Order object can
handle
Solution: introduce an OrderProcessor mediator object
that is responsible for processing an order
25. +
Practical Applications of Design
Patterns in Ruby
Bridge
Problem: there is a need to implement an
abstraction in different ways or using different
libraries
Solution:decouple abstraction from implementation
by creating an implementation object separate from
the abstraction object, which bridges the abstraction
to the specific implementation
26. +
Practical Applications of Design
Patterns in Ruby - Bridge
Example:
Problem: models are coupled to ActiveRecord as their
storage mechanism. We would like to decouple the
ActiveRecord implementation to allow switching some
models to MongoDB easily without affecting controllers
and higher level models
Solution: decouple models from their storage
implementations by introducing a ModelRepository
object for each Model
27. +
Practical Applications of Design
Patterns in Ruby
Observer
Problem: there is a need to perform work outside an
object’s responsibilities whenever the object state
changes
Solution:
make the object an Observable object that
allows subscribing to state notifications, and
introduce Observer objects that observe the
Observable and react according to changes
28. +
Practical Applications of Design
Patterns in Ruby - Observer
Example:
Problem: there is a need to send an email on every
order state change and we do not want such specific
logic to be tied to the order model directly
Solution: introduce an EmailOrderObserver object that
monitors Order for state transitions. Make Order an
observable by having it provide a
subscribe_to_state_transitions(observer) method to
allow monitoring its state changes
29. +
Practical Applications of Design
Patterns in Ruby - Observer
Code:
30. +
Practical Applications of Design
Patterns in Ruby
Prototype
Problem: there is a need to build a complex object a
certain way, with several variations
Solution:define a Prototype object for each of the
variations, and implement a clone method that
enables you to use them to instantiate objects easily
31. +
Practical Applications of Design
Patterns in Ruby - Prototype
Example:
Problem: need to create different kinds of User objects
when writing tests that conform to different common
roles: User, Admin, Visitor, etc…
Solution: define each Deal type as a prototype using
the FactoryGirl library
32. +
Practical Applications of Design
Patterns in Ruby - Prototype
Code:
33. +
Alternative Implementations in Ruby
Strategy: enumerate blocks per strategy name
State: enumerate blocks per state value
Decorator:
method_missing, Forwardable, Delegator, etc…
Template Method / Factory Method: abstract library
Abstract Factory: abstract library
34. +
Deprecated Design Patterns in
Ruby
Thereare Design Patterns that have alternative
implementations in Ruby that render them
deprecated to an extent
Command Blocks
Iterator Iterator Methods (each, map, inject, etc…)
35. +
Summary
Design Patterns enable developers to honor the open-closed
principle to keep their libraries open for extension, yet closed
for modification
Design Patterns help improve maintainability when behavior
gets complex by adding structure that improves separation of
concerns
Design Patterns serve as a good design communication tool
Design Patterns in Ruby often have alternative
implementations due to language features like blocks and
duck-typing
36. +
Contact Info
Andy Maleh / Software Engineer / Groupon
Blog: http://andymaleh.blogspot.com
Twitter: @AndyMaleh
37. +
References
Design
Patterns by the Gang of Four (ISBN-13:978-
0201633610)
HeadFirst Design Patterns (ISBN-13:978-
0596007126)