Skip to content

Instantly share code, notes, and snippets.

@theotherzach
Last active August 29, 2015 13:57
Show Gist options
  • Save theotherzach/9679096 to your computer and use it in GitHub Desktop.
Save theotherzach/9679096 to your computer and use it in GitHub Desktop.

Bottles Strategy Pattern

Challenge 1: Refactor starting from here to pattern matching/ strategy pattern so that neither Bottles nor Verse would need to be altered in order to replace verse 6 with lyrics regarding 6 packs. (Sample in the comments below. Any language of your choice as always.)

Challenge 2: Start thinking about a world where you can write 6.to_bottle. (Or however you would create a bottle number primitive in your language of choice.) I recommend against attempting to hand-crank your way there.

class Bottles
  def sing
    verses(99, 0)
  end

  def verses(upper_bound, lower_bound)
    upper_bound.downto(lower_bound).map {|n| verse(n) + "\n"}.join
  end

  def verse(number)
    Verse.new(number).to_s
  end
end

class Verse
  attr_reader :number
  def initialize(number)
    @number = number
  end

  def to_s
    "#{current_inventory} #{current_container} #{liquid} #{location}, ".capitalize +
    "#{current_inventory} #{current_container} #{liquid}.\n" +
    "#{action}, " +
    "#{next_inventory} #{next_container} #{liquid} #{location}.\n"
  end

  private

  def liquid
    'of beer'
  end

  def location
    'on the wall'
  end

  def current_inventory
    case number
    when 0
      'no more'
    else
      number
    end
  end

  def next_inventory
    case number
    when 0
      99
    when 1
      'no more'
    else
      number - 1
    end
  end

  def current_container
    case number
    when 1
      'bottle'
    else
      'bottles'
    end
  end

  def next_container
    case number
    when 2
      'bottle'
    else
      'bottles'
    end
  end

  def action
    case number
    when 0
      "Go to the store and buy some more"
    else
      "Take #{pronoun} down and pass it around"
    end
  end

  def pronoun
    case number
    when 1
      'it'
    else
      'one'
    end
  end
end
@theotherzach
Copy link
Author

class Bottles
  def sing
    verses(99, 0)
  end

  def verses(upper_bound, lower_bound)
    upper_bound.downto(lower_bound).map {|n| verse(n) + "\n"}.join
  end

  def verse(number)
    Verse.new(number).to_s
  end

end

class Verse
  attr_reader :verse_number
  def initialize(number)
    @verse_number = begin
                      Kernel.const_get "VerseNumber#{number}"
                    rescue
                      VerseNumber
                    end.new(number)
  end

  def to_s
    verse_number.to_s
  end
end

class VerseNumber
  attr_reader :number
  def initialize(number)
    @number = number
  end

  def to_s
    "#{current_inventory} #{current_container} #{liquid} #{location}, ".capitalize +
      "#{current_inventory} #{current_container} #{liquid}.\n" +
      "#{action}, " +
      "#{next_inventory} #{next_container} #{liquid} #{location}.\n"
  end

  def liquid
    'of beer'
  end

  def location
    'on the wall'
  end

  def next_inventory
    number - 1
  end

  def current_inventory
    number
  end

  def next_container
    'bottles'
  end

  def current_container
    'bottles'
  end

  def action
    "Take #{pronoun} down and pass it around"
  end

  def pronoun
    'one'
  end
end

class VerseNumber2 < VerseNumber
  def next_container
    "bottle"
  end
end

class VerseNumber1 < VerseNumber
  def next_inventory
    "no more"
  end

  def current_container
    "bottle"
  end

  def pronoun
    'it'
  end
end

class VerseNumber0 < VerseNumber
  def action
    "Go to the store and buy some more"
  end

  def next_container
    "bottles"
  end

  def current_inventory
    "no more"
  end

  def next_inventory
    99
  end
end

@DarthStrom
Copy link

Challenge 1:

class Bottles {
    def sing() {
        verses(99, 0)
    }

    def verses(first, last) {
        def song = ""
        (first..last).each { song += verse(it) + "\n" }
        song
    }

    def verse(n) {
        new Verse(n).toString()
    }
}

class Verse {
    private def verseN

    Verse(n) {
        try {
            verseN = this.class.classLoader.loadClass(
                "Verse${n}", true, false).newInstance(number: n)
        }catch(ClassNotFoundException e){
            verseN = new VerseN(number: n)
        }
    }

    String toString() {
        verseN.toString()
    }
}

class VerseN {
    protected def number

    protected def liquid() {
        'of beer'
    }

    protected def location() {
        'on the wall'
    }

    protected def currentAmount() {
        number
    }

    protected def nextAmount() {
        (number + 99) % 100
    }

    protected def currentContainer() {
        'bottles'
    }

    protected def nextContainer() {
        'bottles'
    }

    protected def pronoun() {
        'one'
    }

    protected def action() {
        "Take ${pronoun()} down and pass it around"
    }

    String toString() {
        "${currentAmount()} ${currentContainer()} ".capitalize() +
        "${liquid()} ${location()}, ${currentAmount()} " +
        "${currentContainer()} ${liquid()}.\n${action()}, ${nextAmount()} " +
        "${nextContainer()} ${liquid()} ${location()}.\n"
    }
}

class Verse2 extends VerseN {
    protected def nextContainer() {
        'bottle'
    }
}

class Verse1 extends VerseN {
    protected def nextAmount() {
        'no more'
    }

    protected def currentContainer() {
        'bottle'
    }

    protected def pronoun() {
        'it'
    }
}

class Verse0 extends VerseN {
    protected def currentAmount() {
        'no more'
    }

    protected def action() {
        'Go to the store and buy some more'
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment