Skip to content

Instantly share code, notes, and snippets.

@anildigital
Last active December 19, 2015 10:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save anildigital/5939892 to your computer and use it in GitHub Desktop.
Save anildigital/5939892 to your computer and use it in GitHub Desktop.
I gave talk on 'Design Patterns in Ruby' at Pune Ruby Users Group meetup at ThoughtWorks. Here are the slides https://speakerdeck.com/anildigital/design-patterns-in-ruby And below are the code snippets which I used to demo.
# Example from http://www.takadevelop.com/2013/05/03/adapter-pattern-in-ruby/
# Adapter Design Pattern
# Abstract Duck
class Duck
def quack
end
def fly
end
end
# Abstrack Turkey
class Turkey
def gobble
end
def fly
end
end
# Weird Duck
class WeirdDuck < Duck# Adaptee
def quack
"quack"
end
def fly
"I'm flying"
end
end
# Wild Turkey
class WildTurkey < Turkey
def gobble
"Gobble Gobble"
end
def fly
"I'm flying a short distance"
end
end
# Turkey to Duck Adapter
class TurkeyToDuckAdapter # < Duck
attr_reader :turkey
def initialize(turkey)
@turkey = turkey
end
def quack
@turkey.gobble
end
def fly
@turkey.fly
end
end
wildturkey = WildTurkey.new
duck_like_turkey = TurkeyToDuckAdapter.new(wildturkey)
puts duck_like_turkey.quack
puts duck_like_turkey.fly
# Example from ported from example in Design Patterns in Ruby book by Russ Olsen
# Composite Design Pattern
# Task class
class Task
attr_reader :name
def initialize(name)
@name = name
end
def get_time_required
0.0
end
end
# Concrete requirement anaylysis task
class RequirementAnalysis < Task
def initialize
super("Requirement Analysis")
end
def get_time_required
2.0
end
end
# Concrete generate mockups task
class Mockup < Task
def initialize
super("Requirement Analysis")
end
def get_time_required
1.0
end
end
# Concrete coding task
class Code < Task
def initialize
super("Code Task")
end
def get_time_required
1.0
end
end
# Concrete testing task
class Test < Task
def initialize
super("Test Software")
end
def get_time_required
1.0
end
end
# Concrete ship task
class Ship < Task
def initialize
super("Deploy Software! Ship it!")
end
def get_time_required
0.0
end
end
# Composite class
class CompositeTask < Task
def initialize(name)
super(name)
@sub_tasks = []
end
def add_sub_task(task)
@sub_tasks << task
end
def remove_sub_task(task)
@sub_tasks.delete(task)
end
def get_time_required
time=0.0
@sub_tasks.each {|task| time += task.get_time_required}
time
end
end
class ShipSoftware < CompositeTask
def initialize
super('Ship software')
add_sub_task( RequirementAnalysis.new )
add_sub_task( Mockup.new )
add_sub_task( Code.new )
add_sub_task( Test.new )
add_sub_task( Ship.new )
end
end
ship_software = ShipSoftware.new
puts ship_software.get_time_required
# Example ported from Java example on http://ruby019.wordpress.com/2012/11/27/decorator-design-pattern-3/
# Decorator Design Pattern
# Original Kindle class
class Kindle
def description
"Kindle Paperwhite"
end
def weight
213
end
def price
10999
end
end
# Decorator class
class KindleDecorator < Kindle
def initialize(kindle)
@kindle = kindle
end
def description
@kindle.description
end
def weight
@kindle.weight
end
def price
@kindle.price
end
end
# Bag decorator
class Bag < KindleDecorator
def initialize(kindle)
@kindle = kindle
end
def description
@kindle.description + ", bag"
end
def weight
@kindle.weight + 30
end
def price
@kindle.price + 1000
end
end
# Adapter decorator
class Adapter < KindleDecorator
def initialize(kindle)
@kindle = kindle
end
def description
@kindle.description + ", adapter"
end
def weight
@kindle.weight + 50
end
def price
@kindle.price + 1200
end
end
kindle_with_bag_and_adapter = Adapter.new(Bag.new(Kindle.new))
puts "Package: Kindle With Bag And Adapter";
puts "Description: " + kindle_with_bag_and_adapter.description;
puts "Weight: " + kindle_with_bag_and_adapter.weight.to_s + " gm";
puts "Price: ₹" + kindle_with_bag_and_adapter.price.to_s;
# Example from Design Patterns in Ruby book by Russ Olsen
# Proxy Design Pattern
class BankAccount
attr_reader :balance
def initialize(starting_balance=0)
@balance = starting_balance
end
def deposit(amount)
@balance += amount
end
def withdraw(amount)
@balance -= amount
end
end
# class BankAccountProxy
# def initialize(real_object)
# @real_object = real_object
# end
# def balance
# @real_object.balance
# end
# def deposit(amount)
# @real_object.deposit(amount)
# end
# def withdraw(amount)
# @real_object.withdraw(amount)
# end
# end
# account = BankAccount.new(100)
# account.deposit(50)
# account.withdraw(10)
# proxy = BankAccountProxy.new(account)
# proxy.deposit(50)
require 'etc'
class AccountProtectionProxy
def initialize(real_account, owner_name)
@subject = real_account
@owner_name = owner_name
end
def deposit(amount)
check_access
return @subject.deposit(amount)
end
def withdraw(amount)
check_access
return @subject.withdraw(amount)
end
def balance
check_access
return @subject.balance
end
def check_access
if Etc.getlogin != @owner_name
raise "Illegal access: #{Etc.getlogin} cannot access account."
end
end
end
account = BankAccount.new(100)
account.deposit(50)
account.withdraw(10)
proxy = AccountProtectionProxy.new(account, "anil")
proxy.deposit(50)
# Example from Design Patterns in Ruby book by Russ Olsen
# Strategy Design Pattern
class Report # Context
attr_reader :title, :text
attr_accessor :formatter
def initialize(formatter)
@title = 'Monthly Report'
@text = ['Things are going', 'really, really well.']
@formatter = formatter
end
def output_report()
@formatter.output_report(self)
end
end
class HTMLFormatter # Strategy 1
def output_report(context)
puts('<html>')
puts(' <head>')
# Output The rest of the report ...
puts(" <title>#{context.title}</title>")
puts(' </head>')
puts(' <body>')
context.text.each do |line|
puts(" <p>#{line}</p>")
end
puts(' </body>')
puts('</html>')
end
end
class PlainTextFormatter # Strategy 2
def output_report(context)
puts("***** #{context.title} *****")
context.text.each do |line|
puts(line)
end
end
end
# Pass strtegy object to the context
report = Report.new(HTMLFormatter.new)
report.output_report
# Template Method Design pattern
# Game
class Game
# Template method .. YES it is template method!
def play_game
perform_step1
perform_step2
perform_step3
end
def perform_step1
raise "must be implemented by a class"
end
def perform_step2
raise "must be implemented by a class"
end
def perform_step3
raise "must be implemented by a class"
end
end
# Chess
class Chess < Game
def perform_step1
puts "Start Chess game"
end
def perform_step2
puts "Play Chess"
end
def perform_step3
puts "Declare winner or tie"
end
end
# Monopoly
class Monopoly < Game
def perform_step1
puts "Start Monopoly game"
end
def perform_step2
puts "Play Monopoly"
end
def perform_step3
puts "Declare Monopoly winner"
end
end
puts Chess.new.play_game
puts Monopoly.new.play_game
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment