For those that haven’t heard about Ruby Warrior, it is a fun interactive game designed to teach you the Ruby programming language. You play as a warrior climbing a tall tower to reach the precious Ruby at the top level. On each floor you need to write a Ruby script to instruct the warrior to battle enemies, rescue captives, and reach the stairs.
When it comes to writing code and accomplishing certain tasks, there is certainly always more than one way to get the job done. With that being said, why not structure your code in a clean, concise way, so that even the most novice of programmers can understand what’s going on.
The Command design pattern is intended to separate and decouple an object of invocation from an object that receives the message, all while encapsulating relevant information of a method that can be stored and executed at a later time.
My First Attempt
My first solution to Ruby Warrior contained nothing but
if/else statements. Although it was getting the job done, I started to see how my solution could become very messy and it was getting hard for me to keep track of where I was in the program.
class Player def play_turn(warrior) if warrior.feel.empty? if warrior.health < 15 warrior.rest! else warrior.walk! end else warrior.attack! end end end .......
Using the Command Pattern
To start out, Ruby Warrior gives us this shell code.
class Player def play_turn(warrior) # your code goes here end end
First we start by creating our
InitializeWarrior class. This is the class that all of our new warriors actions will inherit from.
class InitializeWarrior def initialize(warrior) @warrior = warrior end end
From now on whenever a new warrior action is introduced we’ll put it in a class and that class will inherit from our
InitializeWarrior class. In the beginning levels, our warrior is given a handful of new abilities.
Our warrior can now
rest, check his/her
rescue a captive. Once we start creating classes that perform specific tasks, we can start to see a pattern emerging. In keeping with patterns, each of our new classes will only have two methods. A
question? method and an
class Walk < InitializeWarrior def question?(health) @warrior.feel.empty? end def action! @warrior.walk! end end class Attack < InitializeWarrior def question?(health) !@warrior.feel.empty? end def action! @warrior.attack! end end class WarriorRest < InitializeWarrior def question?(health) if @warrior.health < 10 true end end def action! @warrior.rest! end end class WarriorHealth < InitializeWarrior def question?(health) @warrior.health < 20 && @warrior.health >= health end def action! @warrior.rest! end end class RescueCaptive < InitializeWarrior def question?(health) @warrior.feel.captive? end def action! @warrior.rescue! end end
Now that we’ve created separate classes for each warrior action, we need to create new instances of these classes and store them so we can run the commands inside of them. We’ll do this in our
class Player def play_turn(warrior) # your code goes here walk = Walk.new(warrior) attack = Attack.new(warrior) rest = WarriorRest.new(warrior) health = WarriorHealth.new(warrior) captive = RescueCaptive.new(warrior) end end
After creating new instances of all of our classes, we still have to run these commands. For this, I created an array called
actionarray within the
playturn method and I’m going to loop through each of the objects we created.
class Player def play_turn(warrior) # your code goes here walk = Walk.new(warrior) attack = Attack.new(warrior) rest = WarriorRest.new(warrior) health = WarriorHealth.new(warrior) captive = RescueCaptive.new(warrior) # create an array of warrior actions so we can reuse them when needed action_array = [walk, attack, rest health, captive] # looping through our action_array in order to run our commands in each separate class action_array.each do |item| if item.question?(@health) item.action! break end end @health = warrior.health end end
That’s the Command Pattern in action!
The Command Pattern makes certain that our code is cooperative with open closed design principles, which means we can implement a new command relatively easy. All of our objects that were created perform a specific task and then they are executed by our
Player class. Whenever a new warrior action is introduced, all we have to do is create the class and then answer the
question? and perform the
action! that is necessary to get out code to work.