Skip to main content

The road to better programming: Chapter 5

Modules and objects

Teodor Zlatanov (tzz@iglou.com), Programmer, Gold Software Systems
Teodor Zlatanov graduated with an M.S. in computer engineering from Boston University in 1999. He has worked as a programmer since 1992, using Perl, Java, C, and C++. His interests are in open source work on text parsing, 3-tier client-server database architectures, UNIX system administration, CORBA, and project management.

Summary:  This series of articles on developerWorks comprises a complete guide to better programming in Perl. In this fifth installment, Teodor explains what Object Oriented Programming is, when to use it, and how it works in Perl. Object Oriented Programming (OOP) is a powerful programming technique, but it is not a panacea. Good programmers must understand how to use it, and must also know when to rely on more traditional programming techniques. Using OOP in Perl is easy. Unlike more restrictive OOP languages like C++ and Java, OOP in Perl places very few arbitrary constraints on the programmer. OOP is an essential addition to every programmer's toolkit, and a very useful technique for expanding the range of problems that may be solved with Perl.

Date:  01 Jan 2002
Level:  Introductory
Activity:  1142 views

What is Object Oriented Programming (OOP)?

OOP is a programming methodology, or general approach to solving problems. Algorithms, on the other hand, are specific approaches to solving specific problems. OOP is inherently a strong methodology; it tends to make procedural and functional programming methodologies less relevant to the problem, and it does not mesh well with them unless the mix strongly favors OOP. In Perl, this strong-arming is diminished, but still alive and well.

This article covers the basics of OOP versus functional and procedural programming in Perl, and shows how to use OOP in Perl programs and modules. Keep in mind that it will be a summary, rather than a detailed explanation of all aspects of OOP in Perl. Such an explanation would take up several books, and has been written several times already. See Resources later in this article for more information.

What is OOP exactly?

OOP is the technique of solving problems by using objects. Objects in programming terms are entities whose properties and behavior are essential to solving the problems at hand. The definition should be more specific, but can't be because of the incredible variety of OOP approaches in the computer industry today.

In the context of Perl programming, OOP is not essential to using the language. Perl version 5 and above encourages OOP, but definitely does not require it. All Perl libraries are modules, meaning that they use at least the rudiments of OOP. Furthermore, most Perl libraries are implemented as objects, meaning that their user must use them as OOP entities with specific behavior and properties, going through a well-defined interface.


Essential OO programming language features

Generally, three language properties are essential to an OO programming language. They are inheritance, polymorphism, and encapsulation.

Perl supports inheritance. Inheritance is applied when one object (the child) uses another as a starting point (the parent), and then modifies its properties and behavior as necessary. This child-parent relationship is essential to OOP, as it makes it possible to build objects upon other objects. This reuse is one of the benefits of OOP that make it a favorite with programmers.

There are two types of inheritance: single and multiple. Single inheritance requires a child to have only one parent, while multiple inheritance is more liberal (in programming, as in real life, having more than two parents can cause confusion and a difficult childhood, so don't overdo multiple inheritance). Perl supports multiple inheritance, though two or more parents are rarely seen in practice.

Polymorphism (from Greek, meaning "many forms") is the technique of making an object be seen as another. This is a little bit complex, so let's use an example. Say you have a sheep farm with four sheep (Ovis aries), but you just bought two goats (Capra hircus) and one German Shepherd dog (Canis lupis familiaris). How many animals do you own? You'd add up all the sheep, goats, and the dog to get seven. You just applied polymorphism, treating three specific kinds of animal as one generic type of "Animal" for the purposes of counting. If you imagine sheep, goats, and dogs as mammalian species, this is an easy leap of faith. Biologists use polymorphism in this fashion every day, and programmers are famous for stealing -- I mean, "reusing" -- good ideas from other sciences.

In Perl, polymorphism is fully supported. It is not used very frequently, as Perl programmers seem to prefer to modify generic behavior with object properties rather than by modifying inherited behavior. This means that you are more likely to see code that creates three IO::Socket::INET objects, one for UDP packets reception and transmission on port 234, one for TCP packet reception on port 80, and one for TCP packet transmission on port 1024, than you are to see code that uses IO::Socket::INET::UDPTransceiver for the first case, IO::Socket::INET::TCPReceiver for the second, and IO::Socket::TCPTransmitter for the third. In biological terms, this would be like saying that dogs and goats are just mammals, but the goat has the Capra flag turned on, while the dog has the Canis flag turned on.

OOP purists feel that everything should be classified properly, but Perl programmers are not purists by any means. They tend to be more relaxed about OOP rules, which makes them more fun at parties than OOP purists.

Encapsulation refers to enclosing object behavior and properties in a manner that makes them inaccessible to users unless the object author wants to allow that access. That way, object users can't do things they are not supposed to do, access data they are not supposed to access, and generally be a pest. Perl allows encapsulation in the usual laid-back way. See Listing 1.


Why is OOP a strong methodology?

Returning to our original topic of how OOP is a strong methodology, we can see now that OOP incorporates several key concepts that make it difficult to apply in a mix with procedural and functional programming (PP and FP). First and foremost, neither PP nor FP have the concepts of inheritance or class polymorphism, since there are no classes in PP and FP. Encapsulation in PP and FP does exist, but only on a procedural level, never as a class or object attribute. Going to the trouble of using these essential OOP tools means that programmers are generally more likely to stick to OOP for the whole project rather than mix incompatible methodologies.

Some would argue that all programs are eventually reduced to a procedural execution of instructions, and so every OOP program, no matter how purely implemented it is, contains procedural code in its functions (a.k.a. methods) and in the code that creates the first object(s) that do the rest of the work. Even a language as close to pure OOP as Java can't avoid requiring a main() function. Therefore, it would seem that OOP is just a subset of PP. But this reduction of OOP into sequential instructions is of no more concern to a system architect or a programmer than the actual assembler instructions executed for every operation. Remember that OOP is a methodology, not an end in itself.

OOP does not work well with the procedural programming methodology because it concentrates on objects, while procedural programming is based on procedures (we will loosely define procedures as functions available without OOP techniques, and methods as functions available only from within objects). Procedures, just like methods, are just functions invoked by the user, but there are some differences between the two.

Procedures don't have object data to work with. Either they must be passed the data in their parameter list, or they must use data in their scope. A procedure can access any data passed to it during its invocation, or even global data for the whole program. Methods should access only their object's data. In effect, the function scope for a method is usually the object that contains the method.

Procedures are often found using global data, though this should be done only when absolutely necessary. Methods that use global data should be rewritten as soon as possible. Procedures usually invoke other procedures with several parameters. Methods should only have a few parameters, and they invoke other methods more often than other procedures.

Functional programming (FP) does not mix well with OOP for several reasons. The most important reasons are that FP is based on a detailed functional approach to solving a problem, while OOP uses objects to express concepts, and that FP procedures are meant to be used everywhere, as opposed to OOP methods which can be used only from within the object that holds them.

With all that said, we can now explain why Perl is one of the best languages for mixing OOP, FP, and PP methodologies.


How does Perl mix OOP with procedural and functional programming?

Perl is a laid-back language. It goes to great lengths to let programmers do whatever they want, in whatever way is convenient to them. This contrasts sharply with languages like Java and C++. For instance, Perl is happy to allow the programmer to automatically create variables if they were not previously declared (though this is not encouraged, and can in fact be prevented with the use of the highly recommended "use strict" pragma). If you want to shoot yourself in the foot, Perl will give you ten bullets and a laser scope, then stand by and cheer you on.

Perl is, therefore, a great language for abusing methodologies. Don't cringe. It's OK. Accessing internal object data, changing classes on the fly, and redefining methods on the fly are all allowed, for example. The Perl way is that programmers should be allowed to break the rules in the interest of coding, debugging, and execution efficiency. If it helps get the job done, it's OK. Thus, Perl itself can be the programmer's best friend or worst enemy.

Why would anyone want to mix OOP, FP, and PP, if it means breaking the rules? Let's step back and consider the question. What are OOP, FP, and PP? They are just programming methodologies, bundles of concepts, stacks of rules that exist to serve the programming team. OOP, FP, and PP are tools, and every programmer's first job is to know his tools. If a programmer fails to utilize the FP Schwartzian transform in sorting a hash, but writes his own Sort::Hashtable, or fails to reuse the Sys::Hostname module and instead writes procedural code to obtain the system's hostname, that programmer has wasted time, effort, and money, and has reduced code quality and reliability.

A programming team can become complacent with the tools it knows best, and this is just about the worst thing that can happen to them. Using only a subset of the tools available in an industry as exciting and innovative as the computer programming industry guarantees that the team will become useless in a few years. A programmer should be able to mix any methodology whatsoever if it makes him more efficient, his code better, and the team more innovative. Perl recognizes and encourages this attitude.


Benefits of OOP

The benefits of OOP are too many to list in this article, and as I mentioned before, many books have been written on the subject. A small subset is: ease of code reuse, code quality improvements, consistent interfaces, and adaptability.

Because OOP rests on the foundation of classes and objects, reusing OO code means simply importing the classes when needed. Code reuse is, by far, the single most important reason for using OOP, and the reason why OOP has gained importance and popularity in today's industry.

There are some pitfalls here. For instance, solutions to previous problems may not be ideal for the current situation, and badly documented libraries can take as long to understand and use as it would take to write them anew. The system architect's job is to see and avoid these pitfalls.

Code quality is improved with OOP because encapsulation cuts down on data corruption ("friendly fire"), while inheritance and polymorphism decrease the amount and complexity of new code that has to be written. There is a delicate balance between code quality and programming innovation that is best left to the team to discover, as it depends entirely on the team's makeup and purpose.

OOP inheritance and reuse facilitate consistent interfaces in code, but it's not a foregone conclusion that all OO code will have consistent interfaces. The programmers still have to adhere to a general architecture. For instance, the team should agree on the format and interface for error logging, preferably through a modular interface that allows future expansion and is extremely easy to use. Only then will every programmer commit to using that interface instead of erratic print statements because they will realize that the investment of learning that interface will not be wasted when the next error logging function comes along.

Adaptability is a somewhat nebulous concept in programming. I would define it as the acceptance and anticipation of environment and usage changes. Adaptability is important to well-written software because all software must evolve with the world around it. Well-written software should evolve gracefully. OOP facilitates this with modular design, improved code quality, and consistent interfaces that ensure that a new operating system or a new report format will not require radical changes to the core of the architecture.


How to use OOP in Perl

Believe it or not, OOP in Perl is not hard on a beginner or intermediate level, and even advanced usage is not that complicated. You might think otherwise based on all the discussion we've had so far about the intricate workings of OOP. Perl, however, delights in placing as few restrictions on the programmer as possible. Perl OOP is, if you'll excuse the analogy, like a barbecue. Everyone brings their own cut of meat and cooks it the way they like it. Even the communal spirit of a barbecue is there, as data can be easily shared between unrelated objects.

The first step we must take is to understand Perl packages. Packages are like namespaces in C++ and libraries in Java: fences meant to corral data into specific areas. Perl packages, however, are only meant as advisories to the programmer. By default, Perl does not restrict data exchange across packages (although the programmer may do so with via lexical variables).


Listing 1. Package names, switching packages, sharing data between packages, package variables

#!/usr/bin/perl
# note: the following code will generate warnings with the -w switch, 
# and won't even compile with "use strict".  It is meant to demonstrate
# package and lexical variables.  You should always "use strict".
# pay attention to every line!
# this is a global package variable; you shouldn't have any with "use strict"
# it is implicitly in the package called "main" 
$global_sound = "
";
package Cow;                              # the Cow package starts here
# this is a package variable, accessible from any other package as $Cow::sound
$sound = "moo";
# this is a lexical variable, accessible anywhere in this file
my $extra_sound = "stampede";
package Pig;                              # the Pig package starts, Cow ends
# this is a package variable, accessible from any other package as $Pig::sound
$Pig::sound = "oink";                     
$::global_sound = "pigs do it better";    # another "main" package variable
# we're back to the default (main) package
package main;
print "Cows go: ", $Cow::sound;           # prints "moo"
print "\nPigs go: ", $Pig::sound;         # prints "oink"
print "\nExtra sound: ", $extra_sound;    # prints "stampede"
print "\nWhat's this I hear: ", $sound;   # $main::sound is undefined!
print "\nEveryone says: ", $global_sound; # prints "pigs do it better"

Note that the file-scoped lexical variable $extra_sound is accessible in all three packages ("main", "Pig", and "Cow") because they are all defined within the same file in this example. Normally, each packages is defined within its own file, ensuring that lexical variables are private to the package. Thus, encapsulation can be achieved. (Run "perldoc perlmod" for more information.)

Next, we have to relate packages to classes. As far as Perl is concerned, a class is just a fancy package (objects, on the other hand, are specifically created with the bless() function). Again, Perl relaxes the OOP rules so that the programmer is not constrained by them.

The new() method is the conventional name for the class constructor (although you may use any name you want, in typically relaxed Perl fashion). It is invoked whenever a class is instantiated into an object.


Listing 2. A barebones class

#!/usr/bin/perl -w
package Barebones;
use strict;
# this class takes no constructor parameters
sub new
{
  my $classname = shift;  # we know our class name
  bless {}, $classname;   # and bless an anonymous hash
}
1;

You can test this code for yourself by putting the code in Listing 2 in a file called Barebones.pm in any directory, and running the following from that directory (meaning, "include the current directory in the library path, use the Barebones module, and create a new Barebones object"):


perl -I. -MBarebones -e 'my $b = Barebones->new()'

For instance, you can put print statements inside the new() method so you can see what the $classname variable holds.

If you invoke Barebones::new() instead of Barebones->new(), the class name will not be passed to new(). In other words, new() will not act as a constructor, but as a plain function.

You may wonder why $classname needs to be passed in. Why not just say bless {}, "Barebones";? Because of inheritance, this constructor may be called by a class that inherits from Barebones, but is not called Barebones. You would be blessing the wrong thing with the wrong name, and that is a bad idea in OOP.

Every class needs member data and methods other than new(). Defining those is as easy as writing a few procedures.


Listing 3. A class with member data and methods

#!/usr/bin/perl -w
package Barebones;
use strict;
my $count = 0;
# this class takes no constructor parameters
sub new
{
  my $classname = shift;  # we know our class name
  $count++;               # remember how many objects
  bless {}, $classname;   # and bless an anonymous hash
}
sub count
{
  my $self = shift;       # this is the object itself
  return $count;
}
1;

You can test this code with:


perl -I. -MBarebones -e 'my $b = Barebones->new(); Barebones->new(); print $b->count'

and you should get the result '2'. The constructor is invoked twice, and it modifies a lexical variable ($count) that is confined to the scope of the Barebones package, not to each Barebones object. Object-local data should be stored in the object itself. In the case of Barebones, that is the anonymous hash blessed as an object. Note how we can access the object whenever its methods are called, because a reference to the object is the first parameter passed to those methods.

There are a few special methods such as DESTROY(), and AUTOLOAD(), which are invoked automatically by Perl under certain conditions. AUTOLOAD() is the catch-all method used to allow dynamic method names. DESTROY() is the object destructor, but it should not be used unless you really, really need it. Using destructors in Perl usually indicates that you are still thinking like a C/C++ programmer.

Let's look at inheritance. It is done in Perl by changing the @ISA variable. You just assign a list of class names to that variable. That's it. You can put anything in @ISA. You can make your class a child of Satan. Perl doesn't care (although your priest, minister, imam, rabbi, etc. might).


Listing 4. Inheritance

#!/usr/bin/perl -w
package Barebones;
# add these lines to your module's beginning, before other code or
# variable declarations
require Animal;                      # the parent class
@ISA = qw(Animal);                   # announce we're a child of Animal
# note that @ISA was left as a global default variable, and "use
# strict" comes after its declaration.  That's the easiest way to do it.
use strict;
use Carp;
# make your new() method look like this:
sub new
{
  my $proto = shift;
  my $class = ref($proto) || $proto;
  my $self  = $class->SUPER::new();  # use the parent's new() method
  bless ($self, $class);             # but bless $self (an Animal) as Barebones
}
1;

These are the very basics of OOP in Perl. There is a lot more in the language that you should explore. Many good books have been written on the subject. Consult Resources for a sampling.


h2xs: your new best friend

Don't you wish there was a tool that could write your Perl class for you, that could write a documentation (POD) skeleton as well, and generally make your life easier by doing things right? Perl comes with exactly that sort of tool: h2xs.

The important flags to remember are "-A -n Module". With those, h2xs will generate a skeleton directory called "Module" full of useful files. They are:

  • Module.pm, the module itself, with skeleton documentation already written.
  • Module.xs, for linking your module to C code. (Run "perldoc perlxs" for more information.)
  • MANIFEST, a list of files for packaging.
  • test.pl, a skeleton testing script.
  • Changes, a log of changes made to the module.
  • Makefile.PL, a makefile generator (run it with "perl Makefile.PL".)

You don't need to use all these files, but it's nice to know they are there when you do need them.


Exercises

  1. What is the difference between OOP, PP, and FP?

  2. What are the essential OO programming language features? Give an example of how each might be used.

  3. When would you avoid OOP?

  4. Write a class with a new() method that, when instantiated into an object, stores the current object count into the object itself, as a sort of unique object identifier. Why is or isn't this a good idea?

  5. Draw a diagram of your immediate family. Why is this not the same sort of inheritance as the one we discussed regarding OOP? How would you express family relationships in OOP?

  6. If every object had to inherit from a single source, the base object of all objects, what kind of properties, methods, and attributes would you put in that base object? Would you make it have a unique identifier in all cases, for instance? Why is a base object for all objects not necessarily a good idea?

  7. Look carefully at the files that h2xs generates. What does Makefile.PL do when you run it with Perl? Check out the targets in the resulting Makefile. Use test.pl for a simple test in addition to the one specified by default.


Resources

About the author

Teodor Zlatanov graduated with an M.S. in computer engineering from Boston University in 1999. He has worked as a programmer since 1992, using Perl, Java, C, and C++. His interests are in open source work on text parsing, 3-tier client-server database architectures, UNIX system administration, CORBA, and project management.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Linux, Open source
ArticleID=11190
ArticleTitle=The road to better programming: Chapter 5
publish-date=01012002
author1-email=tzz@iglou.com
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers