Object-oriented sheep, running around in Ruby Shoes
Introduction
Teaching programming is hard. (Because it's a craft and not a science? Well...) Teaching programming to complete lay-people is even harder. There is hardly a single „starting point“, from which you can unfold things. Two things give an awesome helping hand here: „Learn To Program“ book by Chris Pine and Ruby's succinct syntax, enabling you to explain things in code rather then pseudo-code and not getting lost in public static void meta.
But there's only so much fun you can make with puts 1 + 1 and puts "Hello " * 5. To do something worthwhile, you need variables, arrays, hashes and even classes much sooner than you can actually explain them.
Which brings one big and unfortunate consequence: too much focus on the syntax. Programming is then presumably a knowledge of this abracadabra, this weird language used to „talk to computers“. Which is a pity, because same as being able to write gives you much more power than (only) being able to read, „to fully explore the computer as an artistic material, it's important to understand this «arcane art of computer programming»“, as the Processing book paraphrases John Maeda.
But programming is not about the syntax. Programming is not about „writing code“. The essence of programming is in understanding the world and being able to transform such understanding into a set of descriptions, rules and procedures. When you're facing a task such as „write a customer relationship management (CRM) system“, your problems aren't „which sort of sorting algorithm is faster“ or „how to query a database“. Your problem isn't „how to structure the database“ either, actually. Your problems are questions like „What is an invoice in real word?“ and „How this particular customer understands the ‚invoice‘ concept?“. Programming is about mapping the relationships of dirty and complicated real world into artificial, frictionless world of computer code.
Object Disoriented Programming
Object-oriented programming paradigm is one of the best tools to do such „mapping“. Maybe the best among current ones? There's certainly room for valid disagreement on that. One of the strongest traits of object-oriented programming (OOP) — or more precisely, approach to programming — is that it puts focus on the entities we are working with and their relationships. In the hypothetic CRM scenario above, it forces us to focus on understanding what is an Invoice, how does it relate to a Customer and possibly also to a Project and Account Manager. What properties it has and what actions it can perform.

There's one big problem when explaining object oriented approach to programming.
Basic mental model for computer program is a procedure, a recipe. You begin with a certain input (a stove, a pan, eggs and oil), go through couple of steps, and produce some output (fried eggs). That's quite understandable! Historically, that's the format computer programs were written like. Also, just about everybody wrote their first program as a „procedural recipe“.
One consequence of such „procedural“ approach is that you tend to see data as dumb. As Derek DeVries and Mike Naberezny summarize, „[In PHP] we put data into variables, variables go into functions, and new variables come out. This is the essence of procedural programming.“ (Rails for PHP Developers, p. 73) Data is dumb in this case. What matters is the desired output. „Does it do what it should? Fine, then we're done.“ But we're not. No computer program used by humans „does what it should“. It has bugs, it's not covering all the edge-cases, and most importantly, it needs to evolve. Object oriented programming is (at least some) solution to such „fragile code“. Until you go overboard and end-up with OOP clutter, that is.
In object oriented programming, data is smart. They're not only smart, they're wise in a Socratic sense: they „know about themselves“. They know about their inner state, can perform actions on themselves and interact with other data by sending them messages.
But the OOP approach is rather abstract. What looks very understandable in a picture isn't straightforward translatable into computer code. Even when you understand OOP on a conceptual level, it's very, very hard to use it in your code. There's lots of abracadabra involved. You understand, but you don't see.
The Shoes GUI toolkit
There's one solution for this problem: enabling you to literally see those „objects“. And the best tool for visualizing concepts in a computer class is usually: a game.

So let's build a very simple game. Let's start from simple, procedural code and gradually grow the code towards object oriented practices.
We will build the game in the Shoes GUI toolkit for Ruby, created and maintained by Why The Lucky Stiff. Shoes relies on Cairo graphics library and enables you to build native GUI applications in Ruby programming language for Linux, Mac OS X and Windows. Download Shoes for your platform and the source code for the game from Github. Also, be absolutely sure to check the Nobody Knows Shoes reference, written and drawn by _why. If (?) you have enjoyed Poignant Guide To Ruby, you will enjoy this one even more.
We will be building the game step by step (there are 15) — every step is referenced as a tag in Git repository, so you can very quickly switch between various stages of the code by doing git checkout 1, git checkout 2 ... git checkout 15. (Very handy in front of full class, mind you :)
Or you can download the relevant file marked with git after every step header.
There' rather long ride in code in front of us, so make yourself some comfort. Nevertheless, the code samples are short and every single step will probably fit on your screen entirely.
Step One
When we start coding, we rarely start from nothing. We build on previous code (or have the code built by generators). In this case, we will start with a simple-bounce.rb file from samples bundled with Shoes. Why? It has two things we know we will want: some graphic object moving on a screen and interactivity.
Here's the shortened code with some abracadabra removed:
Shoes.app do
@icon = image "#{DIR}/static/shoes-icon.png", :left => 100, :top => 100 do
alert "You're soooo quick."
end
animate(30) do
# ...
@icon.move x.to_i, y.to_i
end
end
Well, try it in your own Shoes!
Step Two
OK. That's almost nothing. Now let's build the basics of our game: the pasture and the sheep. We will draw them with Shoes's methods like oval, background, gradient, etc. Also, we want the sheep to be moving towards the other end of the pasture, so we will tweak the original animate loop a little bit.
Shoes.app do
# The Pasture
background gradient( "#fff", "#00ff05")
border black, :strokewidth => 6
# The Sheep
fill "#fff"
stroke "#000"
strokewidth 3
@sheep = oval(0, 0, 15, 15)
x, y = self.width / 2, self.height
animate(20) do
y -= 1
@sheep.move x.to_i, y.to_i
end
end
Step Three
So far, so good. We can draw a sheep on canvas and make it move! But one sheep, that's not fun. Let's add much much more sheep:
Shoes.app do
NUM_SHEEPS = 25
# ...
@sheeps = []
# ...
1.upto NUM_SHEEPS do |i|
sheep = oval(0, 0, 15, 15)
sheep.move x + i * 15, y
@sheeps << sheep
end
animate(20) do
y -= 5
@sheeps[ rand NUM_SHEEPS ].move x.to_i, y.to_i
end
end
We use Ruby's upto() method to generate 25 sheep, and presto!, even position them in intervals of 15 points (sheep.move x + i * 15, y). Now we have bunch of sheep running in tight formation towards the other end of pasture, right?
Step Four
Now, this is where the road gets bumpy. This step might even not fit on your display. We have added lots and lots of code. We have wrapped sheep in a class, and made one new class for the pasture:
module SheepInYourShoes
class Pasture
# ...
def initialize(num_sheep=0)
# ...
1.upto(num_sheep) do |i|
@sheep << Sheep.new( { :x => (i * 15), :y => ($app.height - 10)} )
end
end
# ...
end
class Sheep
# ...
def initialize(options={})
@x, @y = options[:x] || 0, options[:y] || 0
# ...
@shape.move @x, @y
end
# ...
end
end
Shoes.app do
$app = self
@pasture = SheepInYourShoes::Pasture.new(25)
animate(30) do
unless @pasture.empty?
@sheep = @pasture.random_sheep
@sheep.run! if @sheep
end
end
end
But, you see? Our application itself, the Shoes.app do ... end part, has shrunken. Not only that: it can just say to the pasture: „create yourself (with this number of sheep) when you know thyself!“ And the pasture is wise enough to create new sheep by calling their initialize method, a.k.a. constructor method. (We have stored the reference to Shoes application in the $app global variable, lifted from another bundled example. This is clearly a violation of OOP principles, waiting to be fixed in future steps.) Also, you can see that sheep on the other hand are smart enough to know about their position in their @x and @y instance variables. We have now separated the logic into different entities within our application. And that's good, because we want (and need) the application to be a bit complex. We need a dog.
Step Five
module SheepInYourShoes
class Pasture
attr_reader :sheep, :dog
def initialize(num_sheep=0)
# ...
@dog = Dog.new
end
# ...
end
# ...
class Dog
def initialize
$app.fill "#FFFC61"; $app.stroke "#000"; $app.strokewidth 4
@shape = $app.oval(($app.width/2-13), ($app.height/2-13), 26, 26) # Draw the dog
end
end
end
Shoes.app do
# ...
@pasture = SheepInYourShoes::Pasture.new(25)
# ...
end
See? That was quite easy! We just used what we have learned when creating sheep and created a dog (@dog = Dog.new) in Pasture's constructor method. But wait! The dog cannot move!
Step Six
We have to add some interactivity in the game, moving the dog shape with our keyboard's arrow keys.
module SheepInYourShoes
# ...
class Dog
# ...
def run!(direction)
case direction
when :left then @x -= 26 unless @x < 26
when :right then @x += 26 unless @x > $app.width-51
when :down then @y += 26 unless @y > $app.height-51
when :up then @y -= 26 unless @y < 0
end
@shape.move @x, @y
end
end
end
Shoes.app do
# ...
keypress do |key|
@pasture.dog.run!(key)
end
end
We just trap every keypress in the application and pass the code to the dog object. He is smart enough to move only if relevant key was pressed and not to run off the pasture.
Step Seven
OK, good so far. But the most important part of the journey is still in front of us. We need the dog to be able to catch the sheep and send'em where they belong! Now, you see, it's a tricky question of responsibility. Who should have the responsibility to shuffle sheep back in our application? Obviously the sheep itself should be smart enough to just „run back“ when hearing loud barking. But who says, „run back!“ The dog? Then the dog would need to know about sheep. And he currently knows only about himself, running around, barking. A „too clever“ object is much worse then „dumb data“. Of course, the pasture knows very well about each sheep from the herd! So let's add a remove_sheep_on(position_x, position_y) method and call this method whenever we press a key (and therefore possibly move the dog). The responsibility split may be counter-intuitive at first here, because obviously it's dog's job to „remove“ sheep. But then he would need to reach deeply into our object structure, have a reference to pasture stored inside itself, and send messages through it to the „sheep“ objects. Have a look on so called Law of Demeter for more info.
module SheepInYourShoes
class Pasture
# ...
def remove_sheep_on(x, y)
found = @sheep.select { |sheep| (x-13..x+13).include?(sheep.x) && (y-13..y+13).include?(sheep.y) }.first
found.back! and @sheep.delete(found) unless found.nil? || found.off?
end
end
class Sheep
# ...
def back!
woof = $app.para "Wooof!", :stroke => '#fff', :fill => '#000'
woof.move(@x, @y)
@y = $app.height - 15
@shape.move @x, @y
@shape.style :fill => '#000'
end
end
# ...
end
Shoes.app do
keypress do |key|
# ...
@pasture.remove_sheep_on(@pasture.dog.x, @pasture.dog.y)
end
end
Now try this version on! You should see a loud „woof“ whenever dog „catches“ a sheep, and said sheep will return to the bottom of the game canvas, painted black. The core of our game is now finished! In the remaining steps we will be just polishing this basic version. What's missing, you ask? Well, firstly the final message, telling us our score!
Step Eight
module SheepInYourShoes
class Pasture
attr_reader :sheep, :dog, :catched
def initialize(num_sheep=0)
@catched = 0
# ...
end
# ...
def remove_sheep_on(x, y)
found = @sheep.select { |sheep| (x-13..x+13).include?(sheep.x) && (y-13..y+13).include?(sheep.y) }.first
found.back! and @sheep.delete(found) and @catched += 1 unless found.nil? || found.off?
end
end
Shoes.app do
# ...
def game_is_over(message)
@finale.replace(message)
@finale.move 0, 150
@finale.show
end
animate(30) do
unless @pasture.empty?
@sheep = @pasture.random_sheep
@sheep.run! if @sheep
else
message = @pasture.catched > 5 ? "Congratz! You have catched more than #{@pasture.catched} sheep!" : "Game Over!"
game_is_over(message)
end
end
# ...
@finale = title "Game Over!", :stroke => '#082299', :fill => '#fff', :align => 'center'
@finale.hide
end
That's not hard, is it? The code is rather long, of course, but we're not aiming for as little lines of code as possible. We just have to keep track of counted sheep in the catched variable. We then write a simple game_is_over() method to display a message to player, drawn previously using Shoes' title method. But wait? This message should be an object as well, possibly of a Message class, right? We surely are a bit tired after this coding ride. And the ugly $app global variable is still in the code! These are things we should fix before we can call our code „rather polished“.
Step Nine
But... you know... that can wait, right? We are rather refined types and the „message“ in the end looks very, very ugly. It's just not something you'd expect after playing a game! We have to make it pretty a little bit, with rounded corners and a Close button. Also, we need to give our game a title in the window titlebar!
module SheepInYourShoes
# ...
end
Shoes.app :title => 'Sheep Running In Your Shoes' do
# ...
stack :margin => 30, :margin_top => 100 do
background '#fff', :stroke => '#000',:curve => 15, :transparency => 0.9
title message, :stroke => '#082299', :align => 'center', :margin_top => 15
stack(:attach => @finale, :align => 'center', :margin => 5) do
b = button("Close", :align => 'center') { close }
b.displace(self.width/2-70-b.width/2, 0) # Center the button
end
end
# ...
end
Step Ten
Now, when we're making everything much more pretty, what about catching the sheep? That could also look much better, especially when we know that Shoes come with a handy star method!
module SheepInYourShoes
# ...
class Sheep
def back!
woof = $app.inscription( "Wooof!", :stroke => '#000',
:fill => '#ceffcf',
:margin => 5 ).move(@x, @y)
$app.timer(1) { woof.hide }
$app.fill "#14c2d2"; $app.stroke "#ceffcf"; $app.strokewidth 1
$app.star(@x, @y, 10, 10, 8)
# ...
end
end
#...
But now we really can't put off making the code pretty as well, when the game interface is polished already!
Step Eleven
In this step we will finally get rid of the global $app variable. Why? You may find two types of reasonings why, technical and aesthetic.
First, our global variable increases coupling, ie. dependency of one part of code on another. Usually, using global variable means we're overcoming a design problem: in our case it's referencing the main Shoes app in the Pasture, etc classes. Usually it means the problem wasn't really solved.
Second, as Matz says, the dollar sign in front of global variables is ugly, therefore you shouldn't use them. Our code is even more ugly: $app.height or $app.oval ... means just nothing. We want the API of our code to be something like Canvas.get.height or Canvas.draw { oval(0, 0, 10, 10) }.
Most importantly, refactoring everything related to drawing shapes, getting width of canvas, etc into separate class increases so called cohesion of our application. „Cohesion“ and it's cousin „coupling“ may sound like some weird lingo, but they relate to the already mentioned, essential concept: separation of concerns. So this is what we do:
module SheepInYourShoes
# The game's canvas
class Canvas
class << self
def get; @@canvas; end
def set(canvas); @@canvas = canvas; end
def draw(&b); @@canvas.instance_eval(&b) if block_given?; end
end
end
class Pasture
# ...
def initialize(num_sheep=0)
# ...
Canvas.draw do
background gradient( "#fff", "#00ff05")
border "#000", :strokewidth => 6
end
# ...
end
end
# ...
end
Shoes.app :title => 'Sheep Running In Your Shoes' do
# ...
SheepInYourShoes::Canvas.set( self )
# ...
end
Just as we have been passing around reference to Shoes app in global variable, we have now refactored it to a specific object, Canvas, which wraps, or encapsulates, it. Our code now reads much clearer, we could enhance the wrapping class (or swap Shoes with some other technology drawing shapes!), write unit tests for our game, and so on.
You could say: why not just inherit from the Shoes class? Well, as one mantra says, you should „favor composition over inheritance“. Inheritance is fine concept, until you have several and several layers of classes cluttering everything or until you use inheritance to get through to some loosely related funcionality. People often use inheritance just to use a tiny fraction of shared logic. Also, don't forget the powerful concept of a mixin in Ruby.
Step Twelve
In step twelve we don't add any code, but we add something very important: documentation. Computer programs are strange breed of texts: They are far easier to write than to read.
Step Thirteen
In this step we will refactor the game_over_message into a Message class:
module SheepInYourShoes
# ...
class Message
class << self
# Display final message
def display(message)
Canvas.draw do
stack :margin => 30, :margin_top => 100 do
background '#fff', :stroke => '#000',:curve => 15
title message, :stroke => '#082299', :align => 'center', :margin_top => 15
stack(:attach => @finale, :align => 'center', :margin => 5) do
b = button("Close", :align => 'center') { close }
b.displace(self.width/2-70-b.width/2, 0) # Center the button
end
end
end
end
end
end
end
Shoes.app :title => 'Sheep Running In Your Shoes' do
# ...
@timer = animate(30) do
# ...
else
message = @pasture.catched > 5 ? "Congratz! You have catched more than #{@pasture.catched-1} sheep!" : "Game Over!"
SheepInYourShoes::Message.display( message )
@timer.stop
end
end
# ...
end
... which we will put into much more use directly in next step.
Step Fourteen
We should definitely display some instructions when the game starts. And because we already have our Message class, it's just a matter of adding another method:
module SheepInYourShoes
# ...
class Message
class << self
def initial
# ...
end
# Display final message
def final(message)
Canvas.draw do
stack :margin => 30, :margin_top => 0 do
background '#fff', :stroke => '#000',:curve => 15
title message, :stroke => '#082299', :align => 'center', :margin_top => 15
stack(:attach => @finale, :align => 'center', :margin => 5) do
b = button("Close", :align => 'center') { close }
b.displace(self.width/2-70-b.width/2, 0) # Center the button
end
end
end
end
end
end
end
Shoes.app :title => 'Sheep Running In Your Shoes' do
# ...
SheepInYourShoes::Message.initial
# ...
end
Step Fifteen
Our game is nearly done. While the gradient looks nice, we want to have a real pasture as a game background, so we'll add a photograph with Shoes' image method:
module SheepInYourShoes
# ...
class Pasture
# ...
def initialize(num_sheep=0)
# ...
Canvas.draw do
background gradient( "#fff", "#00ff05")
image "pasture.jpg" # Copyright © 2008 Aardman Animations Ltd.
border "#000", :strokewidth => 6
end
end
# ...
end
Shoes.app :title => 'Sheep Running In Your Shoes' do
# ...
end
Conclusion
So! If you've made it so far — which I seriously doubt —, you (or your students) have some understanding not only what is object oriented programming, but also why it is a sane approach.
If you would like to extend the application, let's say add a bull, which the dog should avoid, you'd know what to do very well. No, not put some arbitrary code „somewhere“ in the game. You'd create a Bull class. Probably with a run_around method. You'd extend the Pasture class to be able to „butt“ the dog with the bull... and so on.
Let me know how it ended.
Join the discussion