The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Moops::Manual::Objects101 - an introduction to object oriented programming using Moops

SYNOPSIS

This tutorial assumes some knowledge of procedural programming in Perl, and provides a gentle introduction to the object oriented programming paradigm.

DESCRIPTION

Object oriented programming is a programming paradigm based around programming classes, and manipulating instances of those classes called objects. These can be objects that appear on the screen (e.g., pictures, textboxes, etc.) or are part of the programming (e.g. actors, connections, particles, etc.).

Classes

Structures are very similar to classes in that they collect data together. However, classes extend this idea and are made from two different things:

Attributes

Things that the object stores data in, similar to variables.

Methods

Functions and Procedures attached to an Object and allowing the object to perform actions.

Let's take a look at the following example:

   use Moops;
   
   class Car
   {
      lexical_has max_speed => (
         is       => 'rw',
         accessor => \(my $max_speed),
         isa      => Int,
         default  => 90,
      );
      
      has fuel => (
         is       => 'rw',
         isa      => Int,
      );
      
      has speed => (
         is       => 'rw',
         isa      => Int,
         trigger  => method ($new, $old?) {
            confess "Cannot travel at a speed of $new; too fast"
               if $new > $self->$max_speed;
         },
      );
      
      method refuel (Int $x) {
         say "Pumping gas!";
         $self->fuel( $self->fuel + $x );
      }
      
      method drive () {
         $self->fuel( $self->fuel - 1 );
      }
   }

You can see that the class is called Car and it has:

  • Three attributes: $max_speed (which is a lexical/private attribute), fuel, and speed;

  • Two methods: refuel and drive.

Remember this is a class and therefore only a template, we need to "create" it using an object.

Attributes

These store information about the object. In the example above we store the fuel, speed, and $max_speed. The attributes are attached to the class, and if there are several instances (objects) of the class then each will store its own version of these variables.

Unlike many other programming languages, Perl has no native concept of attributes. The has keyword instead sets up some extra methods for your class, called "accessors", which allow you to access the data for the attributes.

$max_speed has a lexical accessor, stored in a lexical (my) variable, so cannot be directly accessed outside the scope where it was declared.

Methods

Unlike structures, OOP allows you to attach functions and procedures to your code. This means that not only can you store details about you car (the attributes), you can also allow for subroutines such as drive and refuel, which are attached to each class.

OO - PIIE

When talking about OOP you must remember OO-PIIE (yummy pie!)

OO

Object Orientation

PIIE

Polymorphism, Inheritance, Instantiation, and Encapsulation

Instantiation

As we have seen a class is a template for something; you can't actually execute a class; you must instantiate it: that is create an instance of an class in the form of an object.

   my $polo = Car->new;
   my $escort = Car->new;

The code above creates objects called $polo and $escort, both of class type Car (which we declared earlier). We can now use all the public attributes and methods:

   $polo->refuel(100);    # assuming fuel starts at 0
   $polo->drive();
   $polo->drive();
   
   $escort->refuel(50);   # assuming fuel starts at 0
   
   for my $x (1 .. 20) {
      $escort->drive();
      $polo->drive();
   }
   
   $polo->refuel(10);
   
   say "polo: ", $polo->fuel;
   say "escort: ", $escort->fuel;

This would output the following:

   Pumping gas!
   Pumping gas!
   Pumping gas!
   polo: 88
   escort: 30

Encapsulation

You noticed that we used private_has to declare max_speed instead of has. What this means is that these attributes are not directly accessible from outside the definition of the class. Let's take our polo class as an example:

   $polo->fuel(100);   # this directly alters the fuel level of $polo

In the example we access the fuel attribute of the polo class and give the car 100 units of fuel. Because fuel is declared as public, there are no restrictions in accessing it.

However, when we try the following we run into trouble:

   $polo->$max_speed(100);

The reason that this wouldn't work is because we have declared the $max_speed attribute lexically. If something is declared lexically you can't access it externally, but how do you access it? The only way to access a lexical (private) attribute is to use a public method.

In the Car code example we could have:

   method get_top_speed () {
      return $self->$max_speed;
   }

Because this method is public we can call it from outside the class. And because get_top_speed is declared inside the Car class, it can have access to all the private attributes and methods.

Inheritance

Building on the car example above, what would happen if we wanted to declare an electric car? Well we'd probably want to store some information on the number of batteries that it has:

   use Moops imports => ['Lexical::Accessor'];
   
   class ElectricCar
   {
      lexical_has max_speed => (
         is       => 'rw',
         accessor => \(my $max_speed),
         isa      => Int,
         default  => 90,
      );
      
      has num_batteries => (
         is       => 'rw',
         isa      => Int,
         default  => 8,
      );
      
      has fuel => (
         is       => 'rw',
         isa      => Int,
      );
      
      has speed => (
         is       => 'rw',
         isa      => Int,
         trigger  => method ($new, $old?) {
            confess "Cannot travel at a speed of $new; too fast"
               if $new > $self->$max_speed;
         },
      );
      
      method refuel (Int $x) {
         # HOLD ON!
      }
   }

This seems like a very long and tedious task rewriting all the same code again. You're right! It would be far better if we only had to declare all the new stuff we wanted to add. OOP allows for inheritance, where a new class can inherit the attributes and methods of a parent class:

   class ElectricCar extends Car {
      has num_batteries => (
         is       => 'ro',
         isa      => Int,
         default  => 8,
      );
   }

This means that everything that Car declared is now accessible from ElectricCar, as well as the new num_batteries attribute. Let's instantiate this example and see what's possible:

   my $gwiz = ElectricCar->new(num_batteries => 6, fuel => 100);
   say $gwiz->num_batteries;    # defined in ElectricCar
   say $gwiz->fuel;             # defined in Car

Using inheritance makes creating new classes very quick and easy. It also allows for a modular approach to creating classes, where you might never use the base class at all, but only as a means of creating other child classes.

Rather than having to rewrite the same functionality for similar objects, OOP allows you to reuse attributes and methods from parent classes.

Polymorphism

When you were young you might have watched the Mighty Morphin' Power Rangers. These guys could morph from regular people into their power suits. Sadly polymorphism isn't that exciting, but it does allow you to change the function of attributes and methods when you are inheriting from a parent class. Consider our car example again. When we created the ElectricCar object we inherited from Car and added the num_batteries attribute and methods. But what happens when we try and refuel, let's take a look at the code:

   method refuel (Int $x) {
      say "Pumping gas!";
      $self->fuel( $self->fuel + $x );
   }

Well this just won't do! We are creating an electric car and we don't want to say that we are pumping gas; what would our sandal-socked yoghurt eating friends say?! So for the ElectricCar class, we want to inherit everything from Car, but we want to morph(change) the refuel method. To do that we override the method inherited from the superclass:

   class ElectricCar extends Car {
      has num_batteries => (
         is       => 'ro',
         isa      => Int,
         default  => 8,
      );
      override refuel (Int $x) is override {
         say "Pure renewable energy!";
         $self->fuel( $self->fuel + $x );
      }
   }

BUGS

Please report any other bugs to http://rt.cpan.org/Dist/Display.html?Queue=Moops.

SEE ALSO

Moops.

AUTHOR

Toby Inkster <tobyink@cpan.org>, plus the authors credited at: http://en.wikibooks.org/w/index.php?title=A-level_Computing/AQA/Problem_Solving,_Programming,_Operating_Systems,_Databases_and_Networking/Programming_Concepts/Object-oriented_programming_(OOP)&action=history (as of 15 December 2013).

COPYRIGHT AND LICENCE

Copyright 2013-2014 Toby Inkster and others.

Text is available under the Creative Commons Attribution/Share-Alike License.

http://creativecommons.org/licenses/by-sa/3.0/.

DISCLAIMER OF WARRANTIES

THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.