To understand this tutorial, you should be familiar with using subroutines and data structures in Perl.

An Introduction to Objects

A subroutine encapsulates some behavior into a named container, allowing you to think of it in terms of what inputs it expects and what outputs it returns. You don't need to think much about what happens inside the subroutine. But, you have to correctly structure the inputs and keep track of all of the data. This becomes error-prone when working with complex data structures. When you find yourself passing some common data between multiple subroutines, your problem is a good candidate for an object-oriented solution.

An object is a container for "some data" which is associated with "some behavior". That is, an object's definition links the structure of the data to some subroutines which accept and modify that data while keeping track of the structure. By defining the data structure and behavior as "an object", you're able to create an abstraction where you no longer have to think about the inner details of the object.

Using Objects

If we are going to create an adventure game, our character needs to have some supplies to get out of sticky situations and survive the adventure. She cannot carry all of these supplies without a backpack, so we need to define some items and a backpack to carry them. Our supplies are going to have some weight, which limits whether our character can traverse an obstacle or how far she can travel without eating.

Given some objects to represent our backpack and supplies, we might have some code like this:

  1 #!/usr/bin/env perl
  2 
  3 use v5.10.0;
  4 use warnings;
  5 use strict;
  6 
  7 use Backpack;
  8 use Items;
  9 
 10 my $bag = Backpack->new(color => 'red');
 11 
 12 say "The bag is a ", $bag->color, ' ', $bag->name, '.';
 13 
 14 $bag->add_items(
 15   Candle->new,
 16   Spellbook->new,
 17   Sandwich->new,
 18 );
 19 
 20 say "It weighs ", $bag->weight, "kg.";
 21 say "Contents:";
 22 say "  ", $_->name, " (weight: ", $_->weight, "kg)"
 23   for($bag->contents);

This only shows the interface to these objects, not their definition (we'll get to that later.) This is the key abstraction of objects: the interface insulates us from the implementation. So, when we are using objects, we only have to be thinking about their interface and not about what is happening behind the scenes.

Note the constructor Backpack->new, which returns a new Backpack object. A constructor is a special class method which returns a new instance of an object in that class. The method named new is conventional for object constructors.

The code $bag->color is calling an instance method on $bag. In this case, 'color' is an attribute of the instance. A method which provides an interface to access an attribute is also known as an accessor method. As we'll see later, this attribute might simply be the value $bag->{color}, but reaching directly into the reference like that violates the object's encapsulation. This voids our warranty because it is not part of the object's interface.

We're also calling constructors for 'Candle', 'Spellbook', and 'Sandwich'. These are passed to the Backpack's add_items method. Note that we don't have to hold onto variables for these objects because our Backpack holds them for us. We can retrieve a list of the items in the backpack with $bag->contents.

Note that each of the items in our backpack has name and weight attributes. Our backpack also has a weight method, though this represents the weight of the bag and everything in it.

This concept, where objects of different types respond to the same methods (e.g. weight), is known as polymorphism. Using polymorphism, we can pass an object to any code which expects it to have a weight method and we'll get the expected result even if that object represents a container holding several other items.

Defining Objects

Objects in Perl are built on three fundamental definitions (from perlobj.)

  1. An object is just a reference with an associated class.
  2. A class is just a package containing methods.
  3. A method is just a subroutine which takes an object or class as its first argument.

Objects are Blessed References

The bless function associates a reference with a class, therefore making it an object. So, when we bless a hash reference into the 'Backpack' class, it becomes a backpack object.

  my $bag = {};
  bless($bag, 'Backpack');
  say $bag;

But this object doesn't do much because we have not defined the 'Backpack' class or any methods in it.

  $ perl -e 'my $bag = bless({}, "Backpack"); $bag->foo'
  Can't locate object method "foo" via package "Backpack" at -e line 1.

Classes are Packages

When we call a method on an object, perl takes the name of the associated class and then looks in that class to find the appropriate method. This is true for class methods, where the class is just a string containing the classname.

  $ perl -E 'say Backpack->new'
  Can't locate object method "new" via package "Backpack"
    (perhaps you forgot to load "Backpack"?) at -e line 1.

This time, we get an extra hint in the error message (perl does this when we call a class method and a package matching that classname has not been defined.)

  $ perl -E 'say Backpack->new; package Backpack'
  Can't locate object method "new" via package "Backpack" at -e line 1.

But the package needs to have methods if we want it to do anything.

Methods are Subroutines

When we run the code Backpack->new, perl will find the classe's new method and call it as new("Backpack"), so the first parameter to a method call is the thing on the left of the arrow operator. Similarly, $bag->contents is called as contents($bag) — with the instance as the first parameter in @_.

It is customary to begin methods with my $self = shift; or my $class = shift;, though you could say e.g. my ($self) = @_; instead. Let's define our 'Backpack' class and implement a constructor. Create the following Backpack.pm file.

  1   package Backpack;
  2 
  3   use warnings;
  4   use strict;
  5 
  6   sub new {
  7     my $class = shift;
  8 
  9     my $self = {};
 10     bless($self, $class);
 11     return($self);
 12   }
 13 
 14   1;

This creates the hash reference $self, blesses it into $class (which is 'Backpack'), and returns the new instance. Now we can use the module and create a new backpack.

  $ perl -E 'use Backpack; say Backpack->new;'
  Backpack=HASH(0x62b238)

This Backpack=HASH(...) output is typical when we print a raw object. But our class still can't do anything except construct instances. We can add a color attribute (and make the default color 'blue'.)

  6   sub new {
  7     my $class = shift;
  8     my %options = @_;
  9 
 10     my $self = {
 11       color => 'blue',
 12       %options,
 13     };
 14     bless($self, $class);
 15     return($self);
 16   }
 17 
 18   sub color {
 19     my $self = shift;
 20     return $self->{color};
 21   }

Note that the default color comes before %options when we define $self. So if %options has a 'color' key, it overwrites the default.

  $ perl -E 'use Backpack;
    my $b1 = Backpack->new; say "$b1 is ", $b1->color;
    my $b2 = Backpack->new(color => "green"); say "$b2 is ", $b2->color;'
  Backpack=HASH(0x62b238) is blue
  Backpack=HASH(0x653bc8) is green

A Bag of Objects

Now we're getting somewhere. We just need to implement name, add_items, weight, and contents in the 'Backpack' class, and then some items to put in it.

 24   sub name {'backpack'}
 25 
 26   sub add_items {
 27     my $self = shift;
 28     my (@items) = @_;
 29 
 30     push(@{$self->{items}}, @items);
 31 
 32     return @items;
 33   }
 34 
 35   sub weight {
 36     my $self = shift;
 37 
 38     my $weight = 0.5;
 39     $weight += $_->weight for($self->contents);
 40     return $weight;
 41   }
 42 
 43   sub contents {
 44     my $self = shift;
 45 
 46     return @{$self->{items}};
 47   }

But we also need to initialize the $self->{items} array reference in the constructor. We'll put that after the %options to overwrite any 'items' passed to the constructor.

 10     my $self = {
 11       color => 'blue',
 12       %options,
 13       items => [],
 14     };

Aside: whether you silently overwrite 'items', complain loudly about it being present in %options, or allow it and verify that it is an arrayref full of 'item' objects is a subtle detail of design. You have to decide the trade-offs between convenience, speed, amount of code, and strictness. Technically, anything that the class documentation does not define as part of the interface is not allowed. So, undocumented features might work, but it's your fault if they change later and your code breaks.

Now we know almost everything we need to know about objects. If you wanted to move on to the next topic, you would write a module Items.pm like this to make our example program work.

  1   use warnings;
  2   use strict;
  3 
  4   package Candle;
  5 
  6   sub new {bless({}, $_[0])};
  7   sub weight {0.05}
  8   sub name {'candle'}
  9 
 10 
 11   package Spellbook;
 12 
 13   sub new {bless({}, $_[0])};
 14   sub weight {1}
 15   sub name {'spellbook'}
 16 
 17 
 18   package Sandwich;
 19 
 20   sub new {bless({}, $_[0])};
 21   sub weight {0.1}
 22   sub name {'sandwich'}
 23 1;

That implements just enough of each of class to make the example work.

  $ perl bag.pl
  The bag is a red backpack.
  It weighs 1.65kg.
  Contents:
    candle (weight: 0.05kg)
    spellbook (weight: 1kg)
    sandwich (weight: 0.1kg)

Inheritance

The new method for each of our items is repetitive, and we will likely want to define more items, with more attributes or behaviors. When objects are similar like this, inheritance (aka subclassing) allows us to reuse the base definition. Perl uses the global class variable @ISA to lookup parent methods:

  package Your::SubClass;
  our @ISA = qw(Your::ParentClass);

The above package would simply look in Your::ParentClass for any methods called on Your::SubClass (or instances thereof.) But, if you override a method in Your::SubClass, perl will use that instead. Let's see this in action:

  1   package Item;
  2 
  3   sub name { shift->{name} }
  4   sub weight { shift->{weight} }

We've now defined an 'Item' class in Items.pm. It defines the 'name' and 'weight' accessors.

  1   package Backpack;
  2 
  3   use warnings;
  4   use strict;
  5 
  6   use Items;
  7   our @ISA = qw(Item);
  8 
  9   sub new {
 10     my $class = shift;
 11 
 12     my %options = @_;
 13 
 14     my $self = {
 15       color => 'blue',
 16       %options,
 17       items  => [],
 18       weight => 0.5,
 19       name   => 'backpack',
 20     };
 21     bless($self, $class);
 22     return($self);
 23   }

Now we've inherited 'Item' as the base definition for 'Backpack'. We'll use its default name accessor, but the weight of the Backpack is its empty weight (tare) plus the weight of the items it contains.

 39   sub tare { shift->SUPER::weight }
 40   sub weight {
 41     my $self = shift;
 42 
 43     my $weight = $self->tare;
 44     $weight += $_->weight for($self->contents);
 45     return $weight;
 46   }

When an object method calls $self->SUPER::whatever, perl looks for a method named whatever in that object's parent classes. Thus, a subclass can wrap, override, or otherwise specialize methods from its base class while still using the interface defined by the base class.

Note that none of these changes require us to change the original example bag.pl because we have not changed the interface of the Backpack object. Similarly, we could rewrite a few Backpack methods to keep track of its weight as items are added or removed (an optimization to avoid calculating total weight each time weight is called) without breaking any code which uses Backpack objects.

So, what would our Candle, Spellbook, and Sandwich classes look like if they inherited from the 'Item' class? The new Items.pm would be:

  1 package Item;
  2 use warnings;
  3 use strict;
  4 
  5 =head1 NAME
  6 
  7 Item - the base class for items
  8 
  9 =head1 Overview
 10 
 11 The Item class defines the interface for all of the
 12 manipulable items in the game.  Each subclass will have
 13 a C<new()> constructor and some standard attributes
 14 (defined below.)
 15 
 16 =head2 Items
 17 
 18 The following item subclasses are defined:
 19 
 20 =over
 21 
 22 =item Candle
 23 
 24 =item Spellbook
 25 
 26 =item Sandwich
 27 
 28 =back
 29 
 30 =head1 Constructor
 31 
 32 =head2 new
 33 
 34   my $item = Item->new(%options);
 35 
 36 =cut
 37 
 38 sub new {
 39   my $class = shift;
 40   my $self = {@_};
 41   bless($self, $class);
 42   return $self;
 43 }
 44 
 45 =head1 Standard Attributes
 46 
 47 All items have these attributes.
 48 
 49 =head2 name
 50 
 51 The name of the item.
 52 
 53 =head2 weight.
 54 
 55 The item's weight (in kilograms.)
 56 
 57 =cut
 58 
 59 sub name { shift->{name} }
 60 sub weight { shift->{weight} }
 61 
 62 package Candle;
 63 our @ISA = qw(Item);
 64 
 65 sub new {
 66   shift->SUPER::new(@_, weight => 0.05, name => 'candle')
 67 }
 68 
 69 package Spellbook;
 70 our @ISA = qw(Item);
 71 
 72 sub new {
 73   shift->SUPER::new(@_, weight => 1, name => 'spellbook')
 74 }
 75 
 76 package Sandwich;
 77 our @ISA = qw(Item);
 78 
 79 sub new {
 80   shift->SUPER::new(@_, weight => 0.1, name => 'sandwich')
 81 }
 82 
 83 1;

Aside: Our Items.pm now also has some documentation, which is important. The documentation is our primary means of describing the object interface and formalizing how our objects should be used. The POD ("Plain Old Documentation") is written inline with the code, which allows you to keep the documentation close to the code and update them both at the same time. See perlpod for more information on using POD in your modules.

Finally, the completed Backpack.pm is:

  1 package Backpack;
  2 
  3 use warnings;
  4 use strict;
  5 
  6 use Items;
  7 our @ISA = qw(Item);
  8 
  9 =head1 NAME
 10 
 11 Backpack - a way to carry Items
 12 
 13 =head1 About
 14 
 15 The backpack can have items added to it, and will keep
 16 track of its total weight.
 17 
 18   my $bag = Backpack->new;
 19   $bag->add_items(Spellbook->new);
 20   say $bag->weight;
 21 
 22 =head1 Constructor
 23 
 24 =head2 new
 25 
 26   my $backpack = Backpack->new(color => 'green');
 27 
 28 =over
 29 
 30 =item color (default = 'blue')
 31 
 32 Sets the backpack's color.
 33 
 34 =back
 35 
 36 =cut
 37 
 38 sub new {
 39   my $class = shift;
 40 
 41   my %options = @_;
 42 
 43   my $self = {
 44     color => 'blue',
 45     %options,
 46     items  => [],
 47     weight => 0.5,
 48     name   => 'backpack',
 49   };
 50   bless($self, $class);
 51   return($self);
 52 }
 53 
 54 =head1 Attributes
 55 
 56 =head2 name
 57 
 58 The name is always 'backpack'.
 59 
 60 =head2 color
 61 
 62 The bag's color.
 63 
 64 =cut
 65 
 66 sub color { shift->{color} }
 67 
 68 =head2 tare
 69 
 70 The empty weight of the backpack.
 71 
 72 =cut
 73 
 74 sub tare { shift->SUPER::weight }
 75 
 76 =head2 weight
 77 
 78 The total weight, including all contents.
 79 
 80 =cut
 81 
 82 sub weight {
 83   my $self = shift;
 84 
 85   my $weight = $self->tare;
 86   $weight += $_->weight for($self->contents);
 87   return $weight;
 88 }
 89 
 90 =head2 contents
 91 
 92 A list of the backpack's contents.
 93 
 94   my @items = $bag->contents;
 95 
 96 =cut
 97 
 98 sub contents {
 99   my $self = shift;
100 
101   return @{$self->{items}};
102 }
103 
104 =head1 Methods
105 
106 =head2 add_items
107 
108 Adds items to the backpack.  These must be 'Item' objects.
109 
110   my @added = $bag->add_items(
111     Spellbook->new, Sandwich->new);
112 
113 =cut
114 
115 sub add_items {
116   my $self = shift;
117   my (@items) = @_;
118 
119   push(@{$self->{items}}, @items);
120 
121   return @items;
122 }
123 
124 1;

The 'Backpack' documentation now has example usage sections which illustrate the expected inputs and outputs of each method. Attributes will not need a lot of descriptive text in the documentation, but methods which change the object or do something special deserve a bit of explanation.

When you write unit tests for your objects, the tests should match the documentation. Once you have documented the usage, and your tests prove that it works, you can more easily change the internals of the object because the tests will tell you if you broke anything.

Objects give you a good way to organize your code, documentation, and tests by abstracting complexity out of your main application. You can accomplish this with ordinary modules and subroutines, but objects encapsulate the behavior and the data.

Exercises

1.  Implement a maximum-capacity check in the Backpack class to prevent adding items to it if the total weight would exceed 10kg.

2.  Write a Bag class which subclasses Backpack and has a weight limit of 5kg.

3.  Add a burn method to the Candle class which reduces the size of the candle each time it is called. Adjust the weight to account for how much of the candle has been used. Your candle object should know when it has burned all the way down.

4.  Add methods to find and remove an item from the backpack.

5.  Write a test script which removes the candle from the backpack, uses it (calls burn once), and puts it back. Ensure that the weight of the backpack updates accordingly. What happens if you try to burn the candle while it is in the backpack? How should the bag and item code cooperate to address this issue?

6.  You did document your changes, right?

Further Reading

The perlobj documentation also covers UNIVERSAL methods and destructors, which are not addressed here.

Perl's object system is very simply built out of familiar constructs: references, packages, and subroutines. This makes it very flexible in terms of how you define your objects, but that flexibility can lead to option paralysis when designing your interface. The lack of more specific constructs can also be difficult or tedious for common things like accessors or validating parameters.

The CPAN has many object builders and accessories ranging from the minimalist Class::Accessor::Classy to the comprehensive Moose.