In today’s post, I’m going to show you how to use OOP to vastly improve the readability of your code — by translating Little Red Riding Hood into a computer program!
Gather 'Round, Kids n' Machines, It's Story Time!
First, we need to declare an opening sentence:
main( )
This is equivalent to introductory phrases like, “Once upon a time … ,” “Call me Ishmael,” or “It was a dark and stormy night … .” It alerts both the computer and the reader that the story is about to begin.
Java, C#, C, and C++ all use main( )
, but many languages use other phrases. Pascal, for example, uses begin
instead. There are also a number of languages that don’t require any opening statements. JavaScript is one — it simply starts at the top of the source file.
The next step is to declare the characters and objects that will be in our story:
main( ) /* "Once upon a time … " */
{
littleRedRidingHood = new Protagonist( );
bigBadWolf = new Villain( { new BigEyes( ),
new BigEars( ),
new BigTeeth( ),
new BigStomach( ) } );
mama = new MiddleAgedWoman( );
grandma = new ElderlyWoman( );
woodsman = new SupportingCharacter( );
…
}
In the first line right after the opening curly brace, the keyword new
tells the computer to create an object called littleRedRidingHood
, and give it the characteristics of a Protagonist
. The computer has no idea what a Protagonist
is, so we’ll have to define it. The same goes for all the other characters.
There are a number of ways to build an object-oriented programming paradigm, but the most popular way is to use classes. They’re essentially blueprints that describe how you want an object to look and behave.
So, let’s do that! While we can declare classes inside the same source file that contains our main( )
program, we normally place each class in its own file for easier maintenance.
main( ) /* "Once upon a time … " */
{
littleRedRidingHood = new Protagonist( );
bigBadWolf = new Villain( { new BigEyes( ),
new BigEars( ),
new BigTeeth( ),
new BigStomach( ) } );
mama = new MiddleAgedWoman( );
grandma = new ElderlyWoman( );
woodsman = new SupportingCharacter( );
…
}
/* separate source file */
class Protagonist
{
…
}
/* separate source file */
class ElderlyWoman
{
…
}
/* separate source file */
class Villain
{
…
}
/* separate source file */
class SupportingCharacter
{
…
}
/* separate source file */
class MiddleAgedWoman
{
…
}
/* separate file */
class BigEyes
{
…
}
/* separate file */
class BigEars
{
…
}
/* separate file */
class BigTeeth
{
…
}
/* separate file */
class BigStomach
{
…
}
To specify how we want our objects built, we define constructor methods. The new
keyword tells the computer to look for the constructor of the specified class.
/* separate source file */
class Protagonist
{
constructor( )
{ … }
…
}
/* separate source file */
class ElderlyWoman
{
constructor( )
{ … }
…
}
/* separate source file */
class Villain
{
bodyParts;
constructor( listOfParts )
{
bodyParts = listOfParts;
…
}
…
}
/* separate source file */
class MiddleAgedWoman
{
constructor( )
{ … }
…
}
/* separate source file */
class SupportingCharacter
{
constructor( )
{ … }
…
}
/* separate file */
class BigEyes
{
constructor( )
{ … }
…
}
/* separate file */
class BigEars
{
constructor( )
{ … }
…
}
/* separate file */
class BigTeeth
{
constructor( )
{ … }
…
}
/* separate file */
class BigStomach
{
constructor( )
{ … }
…
}
The constructors for Protagonist
, MiddleAgedWoman
, ElderlyWoman
, and
SupportingCharacter
do not accept any arguments, while that of Villain
accepts a list of body parts. It passes the list to the property, bodyParts
.
If you don’t define a constructor for your class, most programming languages will generate a boilerplate for you.
The next step is to define other properties. Since Grandma owns a house, we’ll create a property called house
.
main( ) /* "Once upon a time ... " */
{
littleRedRidingHood = new Protagonist( );
bigBadWolf = new Villain( { new BigEyes( ),
new BigEars( ),
new BigTeeth( ),
new BigStomach( ) } );
mama = new MiddleAgedWoman( );
grandma = new ElderlyWoman( );
grandma.house = new House( );
woodsman = new SupportingCharacter( );
…
}
/* separate source file */
class ElderlyWoman
{
house;
constructor( )
{ … }
…
}
/* separate source file */
class House
{
constructor( )
{ … }
…
}
Since we want the characters to do stuff, we’ll need to define methods. Grandma will be opening doors, so we’ll create a door-opening method for her. Because of arthritis, she’s recently replaced the door knob on her front door with a lever.
main( ) /* "Once upon a time ... " */
{
littleRedRidingHood = new Protagonist( );
bigBadWolf = new Villain( { new BigEyes( ),
new BigEars( ),
new BigTeeth( ),
new BigStomach( ) } );
mama = new MiddleAgedWoman( );
grandma = new ElderlyWoman( );
grandma.house = new House( );
woodsman = new SupportingCharacter( );
…
grandma.opens( grandma.house.door );
…
}
/* separate source file */
class ElderlyWoman
{
house;
hand;
constructor( )
{ … }
opens( thing )
{
if ( thing == door )
{
if ( thing.doorHandle == Door.knob )
{
hand.twist_door_knob( thing.doorHandle );
}
else if ( thing.doorHandle == Door.lever )
{
hand.push_down_lever( thing.doorHandle );
}
else if ( thing.doorHandle == Door.keyPad )
{
hand.punch_in_code( thing.doorHandle );
}
else if …
}
…
}
…
}
/* separate source file */
class House
{
door;
constructor( )
{
door = new Door( );
door.doorHandle =
Door.lever;
…
}
…
}
In addition, both littleRedRidingHood
and bigBadWolf
will be going places:
main( ) /* "Once upon a time ... " */
{
littleRedRidingHood = new Protagonist( );
bigBadWolf = new Villain( { new BigEyes( ),
new BigEars( ),
new BigTeeth( ),
new BigStomach( ) } );
mama = new MiddleAgedWoman( );
grandma = new ElderlyWoman( );
grandma.house = new House( );
woodsman = new SupportingCharacter( );
…
littleRedRidingHood.walks_to( grandma.house );
…
bigBadWolf.gallops_to( grandma.house );
…
grandma.opens( grandma.house.door );
…
}
/* separate source file */
class Protagonist
{
constructor( )
{ … }
walks_to( location )
{ … }
…
}
/* separate source file */
class Villain
{
bodyParts;
constructor( listOfParts )
{
bodyParts = listOfParts;
…
}
gallops_to( location )
{ … }
…
}
Also, bigBadWolf
‘s various body parts can do different things:
/* separate file */
class BigEyes
{
constructor( )
{ … }
verb( )
{
see( );
}
see( )
{ … }
…
}
/* separate file */
class BigEars
{
constructor( )
{ … }
verb( )
{
hear( );
}
hear( )
{ … }
…
}
/* separate file */
class BigTeeth
{
constructor( )
{ … }
verb( )
{
eat( );
}
eat( )
{ … }
…
}
/* separate file */
class BigStomach
{
constructor( )
{ … }
verb( )
{
digest( );
}
digest( )
{ … }
…
}
And so on. Here’s main( )
written out:
main( ) /* "Once upon a time … " */
{
littleRedRidingHood = new Protagonist( );
bigBadWolf = new Villain( { new BigEyes( ),
new BigEars( ),
new BigTeeth( ),
new BigStomach( ) } );
mama = new MiddleAgedWoman( );
grandma = new ElderlyWoman( );
grandma.house = new House( );
woodsman = new SupportingCharacter( );
mama.bakes( cake );
mama.puts( cake, basket );
mama.gives( basket, littleRedRidingHood );
mama.says( "Sweetie, bring this cake to Grandma, won't you?" );
mama.warns( "And be careful. Make sure you stay on the public road." );
littleRedRidingHood.walks_to( grandma.house );
bigBadWolf.approaches( littleRedRidingHood );
bigBadWolf.asks( "Well, hello, little girl! Where are you off to?" );
littleRedRidingHood.replies( "I'm off to visit my Grandma, who lives at
123 Forest Woods Lane — all alone by
herself." );
bigBadWolf.salivates( ).worries_about( woodsman.nearby );
bigBadWolf.concocts( plan.to_eat( littleRedRidingHood, grandma ) );
bigBadWolf.says( "Why don't you pick some flowers for your Grandma? I'm
sure she would love that." );
littleRedRidingHood.picks( flowers );
bigBadWolf.gallops_to( grandma.house );
bigBadWolf.knocks( grandma.house.door );
grandma.opens( grandma.house.door );
bigBadWolf.eats( grandma );
littleRedRidingHood.arrives( grandma.house ).knocks( grandma.house.door );
bigBadWolf.disguised_as( grandma ).opens( grandma.house.door );
for( i = 0; i < 3; i++ )
{
littleRedRidingHood.says( "My, Grandma! What " +
bigBadWolf.bodyParts[ i ] + " you have!" );
bigBadWolf.replies( "The better to " +
bigBadWolf.bodyParts[ i ].verb( ) +
" you with, my dear!" );
}
bigBadWolf.chases( littleRedRidingHood );
littleRedRidingHood.sees( woodsman.chopping( wood ) );
littleRedRidingHood.says( "Please help me, sir! A big bad wolf is
chasing me!" );
woodsman.kills( bigBadWolf );
woodsman.slices_open( bigBadWolf.bodyParts[ 3 ] ).saves( grandma );
Group.formed_by( littleRedRidingHood, grandma, mama, woodsman )
.celebrates_at( grandma.house );
}
This story is clear and concise. You’ll notice that all the mind-numbing details are buried inside the classes rather than littered all over the main( )
story. If someone wants to learn more about, say, how Grandma opens doors, he can go to ElderlyWoman
and crack open opens( )
to read all about it.
Lost In Translation
Unfortunately, a lot of programs out there look more like the following:
main( ) /* "Once upon a time … " */
{
…
wolf = [ new big_eyes( ), new big_ears( ), new big_teeth( ),
new big_stomach( ) ];
/* Uhh, is the wolf some sort of black market that sells organs?
* I'm not liking where this story is headed … .
*/
…
x = new Hand( grandma, fiveFingers, onePalm );
/* Wait, the programmer took time to define an entire Hand class, but
* doesn't bother to define who the owner of that hand is?
* Why is Grandma being passed in as an argument? Is the disembodied
* hand a pervert? I'm DEFINITELY not liking where this is going … .
*/
…
if ( doorHandle == Door.doorKnob )
{
x.twist_door_knob( );
}
else if ( doorHandle == Door.lever )
{
x.push_down_on_lever( );
}
else if ( doorHandle == Door.keyPad )
{
x.punch_in_code( );
}
else if …
/* Why are there over 200 lines of code describing how the
* disembodied hand opens various types of doors? Who cares?
* Does the programmer have a weird fetish or something??
*/
…
wolfEats_grandma = new EatsGrandma( );
/* Huh? Why define an object that represents the process of
* eating Grandma? Yuck … .
*/
…
String a = "The better to see you with, my dear!";
String b = "The better to hear you with, my dear!";
String c = "The better to eat you with, my dear!";
String d = "Grandma, what big ears you have!";
String e = "Grandma, what big teeth you have!";
String f = "Grandma, what big eyes you have!";
println( f );
println( a );
println( d );
println( b );
println( e );
println( c );
/* You talkin' to me? You talkin' to me?
* You must be talkin' to me, 'cause it looks like you're breaking
* the fourth wall.
*/
…
ending = Red_ridingHoodRuns_into_forest( chase( little_red_riding_hood,
wolf ) );
/* Are you kidding me?! The end of the story gets buried inside a
* function — and is replaced with a "yada, yada, yada"?!
*/
celebration( grandmasHouse, little_red_riding_hood, mama,
ending.woodsMan, grandma );
/* Huh? Who the heck is this woodsMan? When was he introduced?
* And why is he owned by the ending?! Does the ending represent Death,
* and the woodsMan is about to die?
* Is that why grandmasHouse is celebrating? Because it realizes that
* once the woodsMan is dead, it can no longer be chopped to pieces?
* What in God's name is going on?!
*/
}
/* Seriously, this story is bizarre. Was the programmer high on drugs
* while writing this?!
*/
Read Me Another Bedtime Story, Pretty Please?
It was so much fun translating Little Red Riding Hood. So let’s do another — The Three Little Pigs!
(image by Neas_Artwork from Pixabay)
main( ) /* "Once upon a time … " */
{
mama = new Pig( );
veryLazy = new Pig( );
notAsLazy = new Pig( );
hardWorker = new Pig( );
bigBadWolf = new Wolf( );
threeLittlePigs = Group.formed_by( veryLazy, notAsLazy, hardWorker );
mama.gathers( threeLittlePigs ).says( "Children, you're all grown up now.
It's time to set out and make your
mark on the world! );
threeLittlePigs.go_out( ).decide_to_build_house( );
veryLazy.builds( new House( straw ) );
notAsLazy.builds( new House( sticks ) );
hardWorker.builds( new House( bricks ) );
veryLazy.finishes( ).plays( );
notAsLazy.builds( still );
hardWorker.builds( still );
veryLazy.plays( someMore );
notAsLazy.finishes( ).plays( );
hardWorker.builds( stillMore );
veryLazy.plays( yetSomeMore );
notAsLazy.plays( someMore );
hardWorker.finishes( ).goes_to( market ).buys( bigGiantPot );
bigBadWolf.sees( veryLazy ).goes_to( veryLazy.house );
threat = "Little pig! Little pig! Let me in! Let me in!
Or else I'll huff, and I'll puff, and I'll blow your house down!" );
bigBadWolf.bangs_on( veryLazy.house.door ).shouts( threat );
response = "Not by the hair on my chinny-chin-chin!";
veryLazy.shouts( response );
bigBadWolf.huffs( ).puffs( ).blows_down( veryLazy.house );
veryLazy.escapes( barely ).runs_to( notAsLazy.house );
bigBadWolf.chases( veryLazy );
bigBadWolf.bangs_on( notAsLazy.house.door ).shouts( threat );
notAsLazy.shouts( response );
bigBadWolf.huffs( ).puffs( ).cannot_blow_down( notAsLazy.house );
bigBadWolf.huffs( again ).puffs( again ).blows_down( notAsLazy.house );
veryLazy.escapes( barely ).runs_to( hardWorker.house );
notAsLazy.escapes( barely ).runs_to( hardWorker.house );
bigBadWolf.chases( veryLazy, notAsLazy );
bigBadWolf.bangs_on( hardWorker.house.door ).shouts( threat );
hardWorker.shouts( response );
bigBadWolf.huffs( ).puffs( ).cannot_blow_down( hardWorker.house );
bigBadWolf.huffs( again ).puffs( again ).cannot_blow_down( hardWorker.house );
bigBadWolf.huffs( yetAgain ).puffs( yetAgain )
.cannot_blow_down( hardWorker.house );
bigBadWolf.thinks( );
bigBadWolf.sees( chimney );
bigBadWolf.jumps_on( roof );
hardWorker.quickly_lights( fireplace )
.places_on_top_of( fire, bigGiantPot.filled_with( water ) );
bigBadWolf.climbs_down( chimney )
.falls_into( bigGiantPot.filled_with( water.boiling ) );
threeLittlePigs.eat( bigBadWolf.fullyCooked );
threeLittlePigs.rebuild( veryLazy.house, bricks );
threeLittlePigs.rebuild( notAsLazy.house, bricks );
threeLittlePigs.finish( ).go_to( market ).buy( twoMoreBigGiantPots );
threeLittlePigs.play( );
}