Examples of Patterns in Ruby
- Adapter
- Builder
- Command
- Composite
- Decorator
- Facade
- Factory
- Interpreter
- Iterator
- Observer
- Proxy
- Singleton
- State
- Strategy
- Template
- Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.[link]
class Quest
  attr_accessor :difficulty, :hero
  def initialize(difficulty)
    @difficulty = difficulty
    @hero = nil
  end
  def finish
    @hero.exp += calculate_experience
  end
  def calculate_experience
    @difficulty * 50 / @hero.level
  end
end
class Hero
  attr_accessor :level, :exp, :quests
  def initialize
    @level = 1
    @exp = 0
    @quests = []
  end
  def take_quest(quest)
    @quests << (quest.hero = self)
  end
  def finish_quest(quest)
    quest.finish
    @quests.delete quest
  end
end
class OldQuest
  attr_accessor :hero, :difficulty, :experience
  def initialize
    @difficulty = 3
    @experience = 10
  end
  def done
    difficulty * experience
  end
end
class QuestAdapter
  attr_accessor :hero
  def initialize(old_quest, difficulty)
    @old_quest = old_quest
    @old_quest.difficulty = difficulty
    @hero = nil
  end
  def finish
    @hero.exp += @old_quest.done
  end
end
# Usage
hero = Hero.new
quest = Quest.new 5
hero.take_quest quest
hero.finish_quest quest
puts hero.exp
# => 250
some_old_quest = OldQuest.new
old_quest_adapted = QuestAdapter.new(some_old_quest, 5)
hero.take_quest old_quest_adapted
hero.finish_quest old_quest_adapted
puts hero.exp
# => 300- Separate the construction of a complex object from its representation so that the same construction process can create different representations.[link]
class BoardBuilder
  def initialize(width, height)
    @board = Board.new
    @board.width = width
    @board.height = height
    @board.tiles = []
    @board.monsters = []
  end
  def add_tiles(n)
    n.times{ @board.tiles << Tile.new }
  end
  def add_monsters(n)
    n.times{ @board.monsters << Monster.new }
  end
  def board
    @board
  end
end
class Board
  attr_accessor :width, :height, :tiles, :monsters
  def initialize
  end
end
class Tile; end
class Monster; end
# Usage
builder = BoardBuilder.new 2, 3
puts builder.board
# => Board Object
board = builder.board
puts board.width
# => 2
builder.add_tiles(3)
builder.add_monsters(2)
puts board.tiles.size
# => 3
puts board.monsters.size
# => 2- Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.[link]
class Turn
  def initialize
    @commands = []
  end
  def run_command(command)
    command.execute
    @commands << command
  end
  def undo_command
    @commands.pop.unexecute
  end
end
class Hero
  attr_accessor :money, :health
  def initialize
    @money = 0
    @health = 100
  end
end
class GetMoneyCommand
  def initialize(hero)
    @hero = hero
  end
  def execute
    @hero.money += 10
  end
  def unexecute
    @hero.money -= 10
  end
end
class HealCommand
  def initialize(hero)
    @hero = hero
  end
  def execute
    @hero.health += 10
  end
  def unexecute
    @hero.health -= 10
  end
end
# Usage
hero = Hero.new
get_money = GetMoneyCommand.new hero
heal = HealCommand.new hero
turn = Turn.new
turn.run_command(heal)
puts hero.health
# => 110
turn.run_command(get_money)
puts hero.money
# => 10
turn.undo_command
puts hero.money
# => 0- Composition over inheritance. Compose objects into tree structures to represent part-whole hierarchies.[link]
class CompositeQuest
  def initialize
    @tasks = []
  end
  def <<(task)
    @tasks << task
  end
  def reward
    @tasks.inject(0){ |sum, task| sum += task.reward }
  end
end
class MegaQuest < CompositeQuest
end
class Quest < CompositeQuest
end
class MonsterTask
  attr_reader :reward
  def initialize
    @reward = 100
  end
end
class PuzzleTask
  attr_reader :reward
  def initialize
    @reward = 200
  end
end
# Usage
quest1 = Quest.new
quest1 << MonsterTask.new
quest1 << PuzzleTask.new
puts quest1.reward
# => 300
quest2 = Quest.new
quest2 << MonsterTask.new
quest2 << PuzzleTask.new
megaquest = MegaQuest.new
megaquest << quest1
megaquest << quest2
puts megaquest.reward
# => 600- Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.[link]
class ItemDecorator
  def initialize(item)
    @item = item
  end
  # this needs to be delegated with other efective way
  def use
    @item.use
  end
end
class MagicItemDecorator < ItemDecorator
  def price
    @item.price * 3
  end
  def description
    @item.description + "Magic"
  end
end
class MasterpieceItemDecorator < ItemDecorator
  def price
    @item.price * 2
  end
  def description
    @item.description + "Masterpiece"
  end
end
class Item
  attr_reader :price, :description
  def initialize
    @price = 10
    @description = "Item "
  end
  def use
    "do something"
  end
end
# Usage
item = Item.new
magic_item = MagicItemDecorator.new(item)
puts magic_item.price
# => 30
puts magic_item.description
# => Item Magic
masterpiece_item = MasterpieceItemDecorator.new(item)
puts masterpiece_item.price
# => 20
puts masterpiece_item.description
# => Item Masterpiece
# all next lines puts "do something"
item.use
magic_item.use
masterpiece_item.use- The goal of the Facade Pattern is to provide a unified interface to a set of interfaces in a subsystem. This means you'd just have some object that can send back other objects.[link]
class Hero
  attr_reader :name
  def initialize(name)
    @name = name
  end
  def join(level)
    puts "#{self.name} join #{level}\n"
  end
  def attack(enemy)
    puts "#{self.name} kick #{enemy}\n"
  end
end
class Enemy
  attr_reader :name
  def initialize(name)
    @name = name
  end
  def dead(hero)
    puts "#{self.name} killed by #{hero}"
  end
end
class Level
  attr_reader :stage
  def initialize(stage)
    @stage = stage
  end
  def to_s
    stage
  end
end
class GameFacade
  attr_reader :hero, :enemy, :level
  def initialize
    @hero  = Hero.new('Sonic')
    @enemy = Enemy.new('Eggman')
    @level = Level.new('Green Hill')
  end
  def start_game
    hero.join(level)
    hero.attack(enemy.name)
    enemy.dead(hero.name)
  end
end
game = GameFacade.new
game.start_game
# => Sonic join Green Hill
#    Sonic kick Eggman
#    Eggman killed by Sonic- Define an interface for creating an object, but let subclasses decide which class to instantiate.[link]
class Party
  attr_reader :members
  def initialize(factory)
    @members = []
    @factory = factory
  end
  def add_warriors(n)
    n.times{ @members << @factory.create_warrior }
  end
  def add_mages(n)
    n.times{ @members << @factory.create_mage }
  end
end
class HeroFactory
  def create_warrior
    Warrior.new
  end
  def create_mage
    Mage.new
  end
end
class Hero
  def initialize
  end
end
class Warrior < Hero
end
class Mage < Hero
end
# Usage
party = Party.new(HeroFactory.new)
party.add_warriors(3)
party.add_mages(2)
puts party.members.size
# => 5
puts party.members.count{ |member| member.class == "Mage" }
# => 2- This pattern provides an interpreter to deal with an abstract language. Using classes we can understand the inputs for parse them.[link]
class Word
  def initialize(value)
    @value = value
  end
  def execute
    @value
  end
end
class Plus
  def initialize(first, second)
    @first = first
    @second = second
  end
  def execute
    @first.execute + @second.execute
  end
end
class Minus
  def initialize(first, second)
    @first = first
    @second = second
  end
  def execute
    index = @first.execute =~ /#{@second.execute}/
    second_index = index + @second.execute.length
    @first.execute[0,index] + @first.execute[second_index..-1]
  end
end
class Interpreter
  def self.parse(input)
    @waiting_second_word = false
    words = []
    operations = []
    input.split.each_with_index do |value|
      if value =~ /^[^+-].*/ && !@waiting_second_word
        words << Word.new(value)
      else
        if symbol = operations.pop()
          first = words.size > 1 ? Word.new(words.map(&:execute).join(" ")) :
            words.pop
          second = Word.new(value)
          case symbol
          when /\A\+/
            words << Word.new(Plus.new(first, second).execute)
          when /\A\-/
            words << Word.new(Minus.new(first, second).execute)
          end
          @waiting_second_word = false
        else
          @waiting_second_word = true
          operations << value
        end
      end
    end
    words.pop.execute
  end
end
puts Interpreter.parse("NA + NA + NA + BATMAN")
#=> NANANABATMAN
puts Interpreter.parse("you know nothing Jon Snow - nothing")
#=> you know Jon Snow
puts Interpreter.parse("hello + world - llowo")
#=>herld- Iterator helps you to iterate through a complex object using an iterator method.[link]
class Parent
  attr_reader :first_name
  def initialize(first_name, gender)
    @first_name = first_name
    @gender = gender
  end
end
class Child < Parent
end
class Family
  def initialize(surname)
    @surname = surname
    @children = []
  end
  def add_father(first_name)
    @father = Parent.new first_name, "M"
  end
  def add_mother(first_name)
    @mother = Parent.new first_name, "F"
  end
  def add_child(first_name, gender)
    @children << Child.new(first_name, gender)
  end
  def each_member
    [@father, @mother, @children].flatten.each do |member|
      yield member
    end
  end
end
# Usage
family = Family.new "Jackson"
family.add_father("Robert")
family.add_mother("Susan")
family.add_child("Lucas", "M")
family.add_child("James", "M")
family.add_child("Rose", "F")
family.each_member{ |member| puts member.first_name }
# => Robert
#    Susan
#    Lucas
#    James
#    Rose- Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.[link]
module Observable
  attr_reader :observers
  def initialize(attrs = {})
    @observers = []
  end
  def add_observer(observer)
    @observers << observer
  end
  def notify_observers
    @observers.each{ |observer| observer.update }
  end
end
class Tile
  include Observable
  def initialize(attrs = {})
    super
    @cursed = attrs.fetch(:cursed, false)
  end
  def cursed?
    @cursed
  end
  def activate_curse
    notify_observers
  end
end
class Hero
  attr_reader :health
  def initialize
    @cursed = false
    @health = 10
  end
  def damage(hit)
    @health -= hit
  end
  def cursed?
    @cursed
  end
  def discover(tile)
    if tile.cursed?
      @cursed = true
      tile.add_observer(self)
    end
  end
  def update
    damage(4)
  end
end
# Usage
hero = Hero.new
tile = Tile.new cursed: true
hero.discover(tile)
tile.activate_curse
puts hero.health
# => 6
puts hero.cursed?
# => true- Provide a surrogate or placeholder for another object to control access to it.[link]
require 'forwardable'
class Hero
  attr_accessor :keywords
  def initialize
    @keywords = []
  end
end
class ComputerProxy
  # Forwardable allows objects to run methods on behalf
  # of it's members, in this case the Computer object
  extend Forwardable
  # We delegate the ComputerProxy's use of
  # the Computer object's add method
  def_delegators :real_object, :add
  def initialize(hero)
    @hero = hero
  end
  def execute
    check_access
    real_object.execute
  end
  def check_access
    unless @hero.keywords.include?(:computer)
      raise "You have no access"
    end
  end
  def real_object
    @real_object ||= Computer.new
  end
end
class Computer
  def initialize
    @queue = []
  end
  def add(command)
    @queue << command
  end
  def execute
    "executing commands"
  end
end
# Usage
hero = Hero.new
proxy = ComputerProxy.new(hero)
proxy.add("some command")
proxy.execute
# => raise error
hero.keywords << :computer
proxy.execute
# => executing commands- Define a unique instance of an object.[link]
class HeroFactory
  @@instance = nil
  def self.instance
    @@instance ||= HeroFactory.send(:new)
  end
  def create_warrior
    Warrior.new
  end
  def create_mage
    Mage.new
  end
  private_class_method :new
end
# Usage
factory = HeroFactory.instance
another_factory = HeroFactory.instance
puts factory == another_factory
# => true
HeroFactory.new
# => Raise Exception- This pattern tries to simplify complicated control flows changing an object's behavior dynamically.[link]
class Operation
  attr_reader :state
  def initialize
    @state = OperationOpenState.new
  end
  def trigger(state)
    @state = @state.next(state)
  end
end
class OperationOpenState
  def next(state)
    if valid?(state)
      OperationPendingPaymentState.new
    else
      raise IllegalStateJumpError
    end
  end
  def valid?(state)
    state == :pending_payment
  end
end
class OperationPendingPaymentState
  def next(state)
    OperationConfirmState.new if valid?(state)
  end
  def valid?(state)
    state == :confirm
  end
end
class IllegalStateJumpError < StandardError; end
class OperationConfirmState; end
#Usage
operation = Operation.new
puts operation.state.class
#=> OperationOpenState
operation.trigger :pending_payment
puts operation.state.class
#=> OperationPendingPaymentState
operation.trigger :confirm
puts operation.state.class
#=> OperationConfirmState
operation = Operation.new
operation.trigger :confirm
#=> raise IllegalStateJumpError- Define a family of algorithms, encapsulate each one, and make them interchangeable.[link]
class Hero
  attr_reader :damage, :health, :skills
  attr_accessor :printer
  def initialize(printer)
    @damage = 10
    @health = 5
    @printer = printer
    @skills = [:stealth, :driving, :intimidation]
  end
  def print_stats
    if block_given?
      yield(damage, health, skills)
    else
      printer.print(damage, health, skills)
    end
  end
end
class BattleStats
  def print(damage, health, skills)
    "Damage: #{damage}\nHealth: #{health}"
  end
end
class SkillStats
  def print(damage, health, skills)
    skills.inject(""){ |result, skill| result + skill.to_s.capitalize + "\n" }
  end
end
# Usage
Hero.new(BattleStats.new).print_stats
# => Damage: 10
#    Health: 5
Hero.new(SkillStats.new).print_stats
# => Stealth
#    Driving
#    Intimidation
Hero.new(any_printer).print_stats do |damage, health, skills|
  "Looks: I'm printing a customize message about my hero with damage #{damage} and number of skills: #{skills.size}"
end- Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template methods lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.[link]
class Hero
  attr_reader :damage, :abilities
  def initialize
    @damage = damage_rating
    @abilities = occupation_abilities
  end
  def greet
    greeting = ["Hello"]
    greeting << unique_greeting_line
    greeting
  end
  def unique_greeting_line
    raise "You must define unique_greeting_line"
  end
  def damage_rating
    10
  end
  def occupation_abilities
    []
  end
  def attack
    "Atack dealing #{damage} damage"
  end
end
class Warrior < Hero
  def damage_rating
    15
  end
  def occupation_abilities
    [:strike]
  end
  def unique_greeting_line
    "Warrior is ready to fight!"
  end
end
class Mage < Hero
  def damage_rating
    7
  end
  def occupation_abilities
    [:magic_spell]
  end
  def unique_greeting_line
    "Mage is ready to make powerful spells!"
  end
end