You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
684 lines
31 KiB
684 lines
31 KiB
|
|
#Time-stamp: "2001-02-23 20:07:25 MST" -*-Text-*-
|
|
# This document contains text in Perl "POD" format.
|
|
# Use a POD viewer like perldoc or perlman to render it.
|
|
|
|
=head1 NAME
|
|
|
|
HTML::Tree::AboutObjects -- article: "User's View of Object-Oriented Modules"
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
# This an article, not a module.
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
The following article by Sean M. Burke first appeared in I<The Perl
|
|
Journal> #17 and is copyright 2000 The Perl Journal. It appears
|
|
courtesy of Jon Orwant and The Perl Journal. This document may be
|
|
distributed under the same terms as Perl itself.
|
|
|
|
=head1 A User's View of Object-Oriented Modules
|
|
|
|
-- Sean M. Burke
|
|
|
|
The first time that most Perl programmers run into object-oriented
|
|
programming when they need to use a module whose interface is
|
|
object-oriented. This is often a mystifying experience, since talk of
|
|
"methods" and "constructors" is unintelligible to programmers who
|
|
thought that functions and variables was all there was to worry about.
|
|
|
|
Articles and books that explain object-oriented programming (OOP), do so
|
|
in terms of how to program that way. That's understandable, and if you
|
|
learn to write object-oriented code of your own, you'd find it easy to
|
|
use object-oriented code that others write. But this approach is the
|
|
I<long> way around for people whose immediate goal is just to use
|
|
existing object-oriented modules, but who don't yet want to know all the
|
|
gory details of having to write such modules for themselves.
|
|
|
|
This article is for those programmers -- programmers who want to know
|
|
about objects from the perspective of using object-oriented modules.
|
|
|
|
=head2 Modules and Their Functional Interfaces
|
|
|
|
Modules are the main way that Perl provides for bundling up code for
|
|
later use by yourself or others. As I'm sure you can't help noticing
|
|
from reading
|
|
I<The Perl Journal>, CPAN (the Comprehensive Perl Archive
|
|
Network) is the repository for modules (or groups of modules) that
|
|
others have written, to do anything from composing music to accessing
|
|
Web pages. A good deal of those modules even come with every
|
|
installation of Perl.
|
|
|
|
One module that you may have used before, and which is fairly typical in
|
|
its interface, is Text::Wrap. It comes with Perl, so you don't even
|
|
need to install it from CPAN. You use it in a program of yours, by
|
|
having your program code say early on:
|
|
|
|
use Text::Wrap;
|
|
|
|
and after that, you can access a function called C<wrap>, which inserts
|
|
line-breaks in text that you feed it, so that the text will be wrapped to
|
|
seventy-two (or however many) columns.
|
|
|
|
The way this C<use Text::Wrap> business works is that the module
|
|
Text::Wrap exists as a file "Text/Wrap.pm" somewhere in one of your
|
|
library directories. That file contains Perl code...
|
|
|
|
=over
|
|
|
|
Footnote: And mixed in with the Perl code, there's documentation, which
|
|
is what you read with "perldoc Text::Wrap". The perldoc program simply
|
|
ignores the code and formats the documentation text, whereas "use
|
|
Text::Wrap" loads and runs the code while ignoring the documentation.
|
|
|
|
=back
|
|
|
|
...which, among other things, defines a function called C<Text::Wrap::wrap>,
|
|
and then C<exports> that function, which means that when you say C<wrap>
|
|
after having said "use Text::Wrap", you'll be actually calling the
|
|
C<Text::Wrap::wrap> function. Some modules don't export their
|
|
functions, so you have to call them by their full name, like
|
|
C<Text::Wrap::wrap(...parameters...)>.
|
|
|
|
Regardless of whether the typical module exports the functions it
|
|
provides, a module is basically just a container for chunks of code that
|
|
do useful things. The way the module allows for you to interact with
|
|
it, is its I<interface>. And when, like with Text::Wrap, its interface
|
|
consists of functions, the module is said to have a B<functional
|
|
interface>.
|
|
|
|
=over
|
|
|
|
Footnote: the term "function" (and therefore "functionI<al>") has
|
|
various senses. I'm using the term here in its broadest sense, to
|
|
refer to routines -- bits of code that are called by some name and
|
|
which take parameters and return some value.
|
|
|
|
=back
|
|
|
|
Using modules with functional interfaces is straightforward -- instead
|
|
of defining your own "wrap" function with C<sub wrap { ... }>, you
|
|
entrust "use Text::Wrap" to do that for you, along with whatever other
|
|
functions its defines and exports, according to the module's
|
|
documentation. Without too much bother, you can even write your own
|
|
modules to contain your frequently used functions; I suggest having a look at
|
|
the C<perlmod> man page for more leads on doing this.
|
|
|
|
=head2 Modules with Object-Oriented Interfaces
|
|
|
|
So suppose that one day you want to write a program that will automate
|
|
the process of C<ftp>ing a bunch of files from one server down to your
|
|
local machine, and then off to another server.
|
|
|
|
A quick browse through search.cpan.org turns up the module "Net::FTP",
|
|
which you can download and install it using normal installation
|
|
instructions (unless your sysadmin has already installed it, as many
|
|
have).
|
|
|
|
Like Text::Wrap or any other module with a familiarly functional
|
|
interface, you start off using Net::FTP in your program by saying:
|
|
|
|
use Net::FTP;
|
|
|
|
However, that's where the similarity ends. The first hint of
|
|
difference is that the documentation for Net::FTP refers to it as a
|
|
B<class>. A class is a kind of module, but one that has an
|
|
object-oriented interface.
|
|
|
|
Whereas modules like Text::Wrap
|
|
provide bits of useful code as I<functions>, to be called like
|
|
C<function(...parameters...)> or like
|
|
C<PackageName::function(...parameters...)>, Net::FTP and other modules
|
|
with object-oriented interfaces provide B<methods>. Methods are sort of
|
|
like functions in that they have a name and parameters; but methods
|
|
look different, and are different, because you have to call them with a
|
|
syntax that has a class name or an object as a special argument. I'll
|
|
explain the syntax for method calls, and then later explain what they
|
|
all mean.
|
|
|
|
Some methods are meant to be called as B<class methods>, with the class
|
|
name (same as the module name) as a special argument. Class methods
|
|
look like this:
|
|
|
|
ClassName->methodname(parameter1, parameter2, ...)
|
|
ClassName->methodname() # if no parameters
|
|
ClassName->methodname # same as above
|
|
|
|
which you will sometimes see written:
|
|
|
|
methodname ClassName (parameter1, parameter2, ...)
|
|
methodname ClassName # if no parameters
|
|
|
|
Basically all class methods are for making new objects, and methods that
|
|
make objects are called "B<constructors>" (and the process of making them
|
|
is called "constructing" or "instantiating"). Constructor methods
|
|
typically have the name "new", or something including "new"
|
|
("new_from_file", etc.); but they can conceivably be named
|
|
anything -- DBI's constructor method is named "connect", for example.
|
|
|
|
The object that a constructor method returns is
|
|
typically captured in a scalar variable:
|
|
|
|
$object = ClassName->new(param1, param2...);
|
|
|
|
Once you have an object (more later on exactly what that is), you can
|
|
use the other kind of method call syntax, the syntax for B<object method>
|
|
calls. Calling object methods is just like class methods, except
|
|
that instead of the ClassName as the special argument,
|
|
you use an expression that yeilds an "object". Usually this is
|
|
just a scalar variable that you earlier captured the
|
|
output of the constructor in. Object method calls look like this:
|
|
|
|
$object->methodname(parameter1, parameter2, ...);
|
|
$object->methodname() # if no parameters
|
|
$object->methodname # same as above
|
|
|
|
which is occasionally written as:
|
|
|
|
methodname $object (parameter1, parameter2, ...)
|
|
methodname $object # if no parameters
|
|
|
|
Examples of method calls are:
|
|
|
|
my $session1 = Net::FTP->new("ftp.myhost.com");
|
|
# Calls a class method "new", from class Net::FTP,
|
|
# with the single parameter "ftp.myhost.com",
|
|
# and saves the return value (which is, as usual,
|
|
# an object), in $session1.
|
|
# Could also be written:
|
|
# new Net::FTP('ftp.myhost.com')
|
|
$session1->login("sburke","aoeuaoeu")
|
|
|| die "failed to login!\n";
|
|
# calling the object method "login"
|
|
print "Dir:\n", $session1->dir(), "\n";
|
|
$session1->quit;
|
|
# same as $session1->quit()
|
|
print "Done\n";
|
|
exit;
|
|
|
|
Incidentally, I suggest always using the syntaxes with parentheses and
|
|
"->" in them,
|
|
|
|
=over
|
|
|
|
Footnote: the character-pair "->" is supposed to look like an
|
|
arrow, not "negative greater-than"!
|
|
|
|
=back
|
|
|
|
and avoiding the syntaxes that start out "methodname $object" or
|
|
"methodname ModuleName". When everything's going right, they all mean
|
|
the same thing as the "->" variants, but the syntax with "->" is more
|
|
visually distinct from function calls, as well as being immune to some
|
|
kinds of rare but puzzling ambiguities that can arise when you're trying
|
|
to call methods that have the same name as subroutines you've defined.
|
|
|
|
But, syntactic alternatives aside, all this talk of constructing objects
|
|
and object methods begs the question -- what I<is> an object? There are
|
|
several angles to this question that the rest of this article will
|
|
answer in turn: what can you do with objects? what's in an object?
|
|
what's an object value? and why do some modules use objects at all?
|
|
|
|
=head2 What Can You Do with Objects?
|
|
|
|
You've seen that you can make objects, and call object methods with
|
|
them. But what are object methods for? The answer depends on the class:
|
|
|
|
A Net::FTP object represents a session between your computer and an FTP
|
|
server. So the methods you call on a Net::FTP object are for doing
|
|
whatever you'd need to do across an FTP connection. You make the
|
|
session and log in:
|
|
|
|
my $session = Net::FTP->new('ftp.aol.com');
|
|
die "Couldn't connect!" unless defined $session;
|
|
# The class method call to "new" will return
|
|
# the new object if it goes OK, otherwise it
|
|
# will return undef.
|
|
|
|
$session->login('sburke', 'p@ssw3rD')
|
|
|| die "Did I change my password again?";
|
|
# The object method "login" will give a true
|
|
# return value if actually logs in, otherwise
|
|
# it'll return false.
|
|
|
|
You can use the session object to change directory on that session:
|
|
|
|
$session->cwd("/home/sburke/public_html")
|
|
|| die "Hey, that was REALLY supposed to work!";
|
|
# if the cwd fails, it'll return false
|
|
|
|
...get files from the machine at the other end of the session...
|
|
|
|
foreach my $f ('log_report_ua.txt', 'log_report_dom.txt',
|
|
'log_report_browsers.txt')
|
|
{
|
|
$session->get($f) || warn "Getting $f failed!"
|
|
};
|
|
|
|
...and plenty else, ending finally with closing the connection:
|
|
|
|
$session->quit();
|
|
|
|
In short, object methods are for doing things related to (or with)
|
|
whatever the object represents. For FTP sessions, it's about sending
|
|
commands to the server at the other end of the connection, and that's
|
|
about it -- there, methods are for doing something to the world outside
|
|
the object, and the objects is just something that specifies what bit
|
|
of the world (well, what FTP session) to act upon.
|
|
|
|
With most other classes, however, the object itself stores some kind of
|
|
information, and it typically makes no sense to do things with such an
|
|
object without considering the data that's in the object.
|
|
|
|
=head2 What's I<in> an Object?
|
|
|
|
An object is (with rare exceptions) a data structure containing a
|
|
bunch of attributes, each of which has a value, as well as a name
|
|
that you use when you
|
|
read or set the attribute's value. Some of the object's attributes are
|
|
private, meaning you'll never see them documented because they're not
|
|
for you to read or write; but most of the object's documented attributes
|
|
are at least readable, and usually writeable, by you. Net::FTP objects
|
|
are a bit thin on attributes, so we'll use objects from the class
|
|
Business::US_Amort for this example. Business::US_Amort is a very
|
|
simple class (available from CPAN) that I wrote for making calculations
|
|
to do with loans (specifically, amortization, using US-style
|
|
algorithms).
|
|
|
|
An object of the class Business::US_Amort represents a loan with
|
|
particular parameters, i.e., attributes. The most basic attributes of a
|
|
"loan object" are its interest rate, its principal (how much money it's
|
|
for), and it's term (how long it'll take to repay). You need to set
|
|
these attributes before anything else can be done with the object. The
|
|
way to get at those attributes for loan objects is just like the
|
|
way to get at attributes for any class's objects: through accessors.
|
|
An B<accessor> is simply any method that accesses (whether reading or
|
|
writing, AKA getting or putting) some attribute in the given object.
|
|
Moreover, accessors are the B<only> way that you can change
|
|
an object's attributes. (If a module's documentation wants you to
|
|
know about any other way, it'll tell you.)
|
|
|
|
Usually, for simplicity's sake, an accessor is named after the attribute
|
|
it reads or writes. With Business::US_Amort objects, the accessors you
|
|
need to use first are C<principal>, C<interest_rate>, and C<term>.
|
|
Then, with at least those attributes set, you can call the C<run> method
|
|
to figure out several things about the loan. Then you can call various
|
|
accessors, like C<total_paid_toward_interest>, to read the results:
|
|
|
|
use Business::US_Amort;
|
|
my $loan = Business::US_Amort->new;
|
|
# Set the necessary attributes:
|
|
$loan->principal(123654);
|
|
$loan->interest_rate(9.25);
|
|
$loan->term(20); # twenty years
|
|
|
|
# NOW we know enough to calculate:
|
|
$loan->run;
|
|
|
|
# And see what came of that:
|
|
print
|
|
"Total paid toward interest: A WHOPPING ",
|
|
$loan->total_paid_interest, "!!\n";
|
|
|
|
This illustrates a convention that's common with accessors: calling the
|
|
accessor with no arguments (as with $loan->total_paid_interest) usually
|
|
means to read the value of that attribute, but providing a value (as
|
|
with $loan->term(20)) means you want that attribute to be set to that
|
|
value. This stands to reason: why would you be providing a value, if
|
|
not to set the attribute to that value?
|
|
|
|
Although a loan's term, principal, and interest rates are all single
|
|
numeric values, an objects values can any kind of scalar, or an array,
|
|
or even a hash. Moreover, an attribute's value(s) can be objects
|
|
themselves. For example, consider MIDI files (as I wrote about in
|
|
TPJ#13): a MIDI file usually consists of several tracks. A MIDI file is
|
|
complex enough to merit being an object with attributes like its overall
|
|
tempo, the file-format variant it's in, and the list of instrument
|
|
tracks in the file. But tracks themselves are complex enough to be
|
|
objects too, with attributes like their track-type, a list of MIDI
|
|
commands if they're a MIDI track, or raw data if they're not. So I
|
|
ended up writing the MIDI modules so that the "tracks" attribute of a
|
|
MIDI::Opus object is an array of objects from the class MIDI::Track.
|
|
This may seem like a runaround -- you ask what's in one object, and get
|
|
I<another> object, or several! But in this case, it exactly reflects
|
|
what the module is for -- MIDI files contain MIDI tracks, which then
|
|
contain data.
|
|
|
|
=head2 What is an Object Value?
|
|
|
|
When you call a constructor like Net::FTP->new(I<hostname>), you get
|
|
back an object value, a value you can later use, in combination with a
|
|
method name, to call object methods.
|
|
|
|
Now, so far we've been pretending, in the above examples, that the
|
|
variables $session or $loan I<are> the objects you're dealing with.
|
|
This idea is innocuous up to a point, but it's really a misconception
|
|
that will, at best, limit you in what you know how to do. The reality
|
|
is not that the variables $session or $query are objects; it's a little
|
|
more indirect -- they I<hold> values that symbolize objects. The kind of
|
|
value that $session or $query hold is what I'm calling an object value.
|
|
|
|
To understand what kind of value this is, first think about the other
|
|
kinds of scalar values you know about: The first two scalar values you
|
|
probably ever ran into in Perl are B<numbers> and B<strings>, which you
|
|
learned (or just assumed) will usually turn into each other on demand;
|
|
that is, the three-character string "2.5" can become the quantity two
|
|
and a half, and vice versa. Then, especially if you started using
|
|
C<perl -w> early on, you learned about the B<undefined value>, which can
|
|
turn into 0 if you treat it as a number, or the empty-string if you
|
|
treat it as a string.
|
|
|
|
=over
|
|
|
|
Footnote: You may I<also> have been learning about references, in which
|
|
case you're ready to hear that object values are just a kind of
|
|
reference, except that they reflect the class that created thing they point
|
|
to, instead of merely being a plain old array reference, hash reference,
|
|
etc. I<If> this makes makes sense to you, and you want to know more
|
|
about how objects are implemented in Perl, have a look at the
|
|
C<perltoot> man page.
|
|
|
|
=back
|
|
|
|
And now you're learning about B<object values>. An object value is a
|
|
value that points to a data structure somewhere in memory, which is
|
|
where all the attributes for this object are stored. That data
|
|
structure as a whole belongs to a class (probably the one you named in
|
|
the constructor method, like ClassName->new), so that the object value
|
|
can be used as part of object method calls.
|
|
|
|
If you want to actually I<see> what an object value is, you might try
|
|
just saying "print $object". That'll get you something like this:
|
|
|
|
Net::FTP=GLOB(0x20154240)
|
|
|
|
or
|
|
|
|
Business::US_Amort=HASH(0x15424020)
|
|
|
|
That's not very helpful if you wanted to really get at the object's
|
|
insides, but that's because the object value is only a symbol for the
|
|
object. This may all sound very abstruse and metaphysical, so a
|
|
real-world allegory might be very helpful:
|
|
|
|
=over
|
|
|
|
You get an advertisement in the mail saying that you have been
|
|
(im)personally selected to have the rare privilege of applying for a
|
|
credit card. For whatever reason, I<this> offer sounds good to you, so you
|
|
fill out the form and mail it back to the credit card company. They
|
|
gleefully approve the application and create your account, and send you
|
|
a card with a number on it.
|
|
|
|
Now, you can do things with the number on that card -- clerks at stores
|
|
can ring up things you want to buy, and charge your account by keying in
|
|
the number on the card. You can pay for things you order online by
|
|
punching in the card number as part of your online order. You can pay
|
|
off part of the account by sending the credit card people some of your
|
|
money (well, a check) with some note (usually the pre-printed slip)
|
|
that has the card number for the account you want to pay toward. And you
|
|
should be able to call the credit card company's computer and ask it
|
|
things about the card, like its balance, its credit limit, its APR, and
|
|
maybe an itemization of recent purchases ad payments.
|
|
|
|
Now, what you're I<really> doing is manipulating a credit card
|
|
I<account>, a completely abstract entity with some data attached to it
|
|
(balance, APR, etc). But for ease of access, you have a credit card
|
|
I<number> that is a symbol for that account. Now, that symbol is just a
|
|
bunch of digits, and the number is effectively meaningless and useless
|
|
in and of itself -- but in the appropriate context, it's understood to
|
|
I<mean> the credit card account you're accessing.
|
|
|
|
This is exactly the relationship between objects and object values, and
|
|
from this analogy, several facts about object values are a bit more
|
|
explicable:
|
|
|
|
* An object value does nothing in and of itself, but it's useful when
|
|
you use it in the context of an $object->method call, the same way that
|
|
a card number is useful in the context of some operation dealing with a
|
|
card account.
|
|
|
|
Moreover, several copies of the same object value all refer to the same
|
|
object, the same way that making several copies of your card number
|
|
won't change the fact that they all still refer to the same single
|
|
account (this is true whether you're "copying" the number by just
|
|
writing it down on different slips of paper, or whether you go to the
|
|
trouble of forging exact replicas of your own plastic credit card). That's
|
|
why this:
|
|
|
|
$x = Net::FTP->new("ftp.aol.com");
|
|
$x->login("sburke", "aoeuaoeu");
|
|
|
|
does the same thing as this:
|
|
|
|
$x = Net::FTP->new("ftp.aol.com");
|
|
$y = $x;
|
|
$z = $y;
|
|
$z->login("sburke", "aoeuaoeu");
|
|
|
|
That is, $z and $y and $x are three different I<slots> for values,
|
|
but what's in those slots are all object values pointing to the same
|
|
object -- you don't have three different FTP connections, just three
|
|
variables with values pointing to the some single FTP connection.
|
|
|
|
* You can't tell much of anything about the object just by looking at
|
|
the object value, any more than you can see your credit account balance
|
|
by holding the plastic card up to the light, or by adding up the digits
|
|
in your credit card number.
|
|
|
|
* You can't just make up your own object values and have them work --
|
|
they can come only from constructor methods of the appropriate class.
|
|
Similarly, you get a credit card number I<only> by having a bank approve
|
|
your application for a credit card account -- at which point I<they>
|
|
let I<you> know what the number of your new card is.
|
|
|
|
Now, there's even more to the fact that you can't just make up your own
|
|
object value: even though you can print an object value and get a string
|
|
like "Net::FTP=GLOB(0x20154240)", that string is just a
|
|
I<representation> of an object value.
|
|
|
|
Internally, an object value has a basically different type from a
|
|
string, or a number, or the undefined value -- if $x holds a real
|
|
string, then that value's slot in memory says "this is a value of type
|
|
I<string>, and its characters are...", whereas if it's an object value,
|
|
the value's slot in memory says, "this is a value of type I<reference>,
|
|
and the location in memory that it points to is..." (and by looking at
|
|
what's at that location, Perl can tell the class of what's there).
|
|
|
|
Perl programmers typically don't have to think about all these details
|
|
of Perl's internals. Many other languages force you to be more
|
|
conscious of the differences between all of these (and also between
|
|
types of numbers, which are stored differently depending on their size
|
|
and whether they have fractional parts). But Perl does its best to
|
|
hide the different types of scalars from you -- it turns numbers into
|
|
strings and back as needed, and takes the string or number
|
|
representation of undef or of object values as needed. However, you
|
|
can't go from a string representation of an object value, back to an
|
|
object value. And that's why this doesn't work:
|
|
|
|
$x = Net::FTP->new('ftp.aol.com');
|
|
$y = Net::FTP->new('ftp.netcom.com');
|
|
$z = Net::FTP->new('ftp.qualcomm.com');
|
|
$all = join(' ', $x,$y,$z); # !!!
|
|
...later...
|
|
($aol, $netcom, $qualcomm) = split(' ', $all); # !!!
|
|
$aol->login("sburke", "aoeuaoeu");
|
|
$netcom->login("sburke", "qjkxqjkx");
|
|
$qualcomm->login("smb", "dhtndhtn");
|
|
|
|
This fails because $aol ends up holding merely the B<string representation>
|
|
of the object value from $x, not the object value itself -- when
|
|
C<join> tried to join the characters of the "strings" $x, $y, and $z,
|
|
Perl saw that they weren't strings at all, so it gave C<join> their
|
|
string representations.
|
|
|
|
Unfortunately, this distinction between object values and their string
|
|
representations doesn't really fit into the analogy of credit card
|
|
numbers, because credit card numbers really I<are> numbers -- even
|
|
thought they don't express any meaningful quantity, if you stored them
|
|
in a database as a quantity (as opposed to just an ASCII string),
|
|
that wouldn't stop them from being valid as credit card numbers.
|
|
|
|
This may seem rather academic, but there's there's two common mistakes
|
|
programmers new to objects often make, which make sense only in terms of
|
|
the distinction between object values and their string representations:
|
|
|
|
The first common error involves forgetting (or never having known in the
|
|
first place) that when you go to use a value as a hash key, Perl uses
|
|
the string representation of that value. When you want to use the
|
|
numeric value two and a half as a key, Perl turns it into the
|
|
three-character string "2.5". But if you then want to use that string
|
|
as a number, Perl will treat it as meaning two and a half, so you're
|
|
usually none the wiser that Perl converted the number to a string and
|
|
back. But recall that Perl can't turn strings back into objects -- so
|
|
if you tried to use a Net::FTP object value as a hash key, Perl actually
|
|
used its string representation, like "Net::FTP=GLOB(0x20154240)", but
|
|
that string is unusable as an object value. (Incidentally, there's
|
|
a module Tie::RefHash that implements hashes that I<do> let you use
|
|
real object-values as keys.)
|
|
|
|
The second common error with object values is in
|
|
trying to save an object value to disk (whether printing it to a
|
|
file, or storing it in a conventional database file). All you'll get is the
|
|
string, which will be useless.
|
|
|
|
When you want to save an object and restore it later, you may find that
|
|
the object's class already provides a method specifically for this. For
|
|
example, MIDI::Opus provides methods for writing an object to disk as a
|
|
standard MIDI file. The file can later be read back into memory by
|
|
a MIDI::Opus constructor method, which will return a new MIDI::Opus
|
|
object representing whatever file you tell it to read into memory.
|
|
Similar methods are available with, for example, classes that
|
|
manipulate graphic images and can save them to files, which can be read
|
|
back later.
|
|
|
|
But some classes, like Business::US_Amort, provide no such methods for
|
|
storing an object in a file. When this is the case, you can try
|
|
using any of the Data::Dumper, Storable, or FreezeThaw modules. Using
|
|
these will be unproblematic for objects of most classes, but it may run
|
|
into limitations with others. For example, a Business::US_Amort
|
|
object can be turned into a string with Data::Dumper, and that string
|
|
written to a file. When it's restored later, its attributes will be
|
|
accessable as normal. But in the unlikely case that the loan object was
|
|
saved in mid-calculation, the calculation may not be resumable. This is
|
|
because of the way that that I<particular> class does its calculations,
|
|
but similar limitations may occur with objects from other classses.
|
|
|
|
But often, even I<wanting> to save an object is basically wrong -- what would
|
|
saving an ftp I<session> even mean? Saving the hostname, username, and
|
|
password? current directory on both machines? the local TCP/IP port
|
|
number? In the case of "saving" a Net::FTP object, you're better off
|
|
just saving whatever details you actually need for your own purposes,
|
|
so that you can make a new object later and just set those values for it.
|
|
|
|
=head2 So Why Do Some Modules Use Objects?
|
|
|
|
All these details of using objects are definitely enough to make you
|
|
wonder -- is it worth the bother? If you're a module author, writing
|
|
your module with an object-oriented interface restricts the audience of
|
|
potential users to those who understand the basic concepts of objects
|
|
and object values, as well as Perl's syntax for calling methods. Why
|
|
complicate things by having an object-oriented interface?
|
|
|
|
A somewhat esoteric answer is that a module has an object-oriented
|
|
interface because the module's insides are written in an
|
|
object-oriented style. This article is about the basics of
|
|
object-oriented I<interfaces>, and it'd be going far afield to explain
|
|
what object-oriented I<design> is. But the short story is that
|
|
object-oriented design is just one way of attacking messy problems.
|
|
It's a way that many programmers find very helpful (and which others
|
|
happen to find to be far more of a hassle than it's worth,
|
|
incidentally), and it just happens to show up for you, the module user,
|
|
as merely the style of interface.
|
|
|
|
But a simpler answer is that a functional interface is sometimes a
|
|
hindrance, because it limits the number of things you can do at once --
|
|
limiting it, in fact, to one. For many problems that some modules are
|
|
meant to solve, doing without an object-oriented interface would be like
|
|
wishing that Perl didn't use filehandles. The ideas are rather simpler
|
|
-- just imagine that Perl let you access files, but I<only> one at a
|
|
time, with code like:
|
|
|
|
open("foo.txt") || die "Can't open foo.txt: $!";
|
|
while(readline) {
|
|
print $_ if /bar/;
|
|
}
|
|
close;
|
|
|
|
That hypothetical kind of Perl would be simpler, by doing without
|
|
filehandles. But you'd be out of luck if you wanted to read from
|
|
one file while reading from another, or read from two and print to a
|
|
third.
|
|
|
|
In the same way, a functional FTP module would be fine for just
|
|
uploading files to one server at a time, but it wouldn't allow you to
|
|
easily write programs that make need to use I<several> simultaneous
|
|
sessions (like "look at server A and server B, and if A has a file
|
|
called X.dat, then download it locally and then upload it to server B --
|
|
except if B has a file called Y.dat, in which case do it the other way
|
|
around").
|
|
|
|
Some kinds of problems that modules solve just lend themselves to an
|
|
object-oriented interface. For those kinds of tasks, a functional
|
|
interface would be more familiar, but less powerful. Learning to use
|
|
object-oriented modules' interfaces does require becoming comfortable
|
|
with the concepts from this article. But in the end it will allow you
|
|
to use a broader range of modules and, with them, to write programs
|
|
that can do more.
|
|
|
|
B<[end body of article]>
|
|
|
|
=head2 [Author Credit]
|
|
|
|
Sean M. Burke has contributed several modules to CPAN, about half of
|
|
them object-oriented.
|
|
|
|
[The next section should be in a greybox:]
|
|
|
|
=head2 The Gory Details
|
|
|
|
For sake of clarity of explanation, I had to oversimplify some of the
|
|
facts about objects. Here's a few of the gorier details:
|
|
|
|
* Every example I gave of a constructor was a class method. But object
|
|
methods can be constructors, too, if the class was written to work that
|
|
way: $new = $old->copy, $node_y = $node_x->new_subnode, or the like.
|
|
|
|
* I've given the impression that there's two kinds of methods: object
|
|
methods and class methods. In fact, the same method can be both,
|
|
because it's not the kind of method it is, but the kind of calls it's
|
|
written to accept -- calls that pass an object, or calls that pass a
|
|
class-name.
|
|
|
|
* The term "object value" isn't something you'll find used much anywhere
|
|
else. It's just my shorthand for what would properly be called an
|
|
"object reference" or "reference to a blessed item". In fact, people
|
|
usually say "object" when they properly mean a reference to that object.
|
|
|
|
* I mentioned creating objects with I<con>structors, but I didn't
|
|
mention destroying them with I<de>structor -- a destructor is a kind of
|
|
method that you call to tidy up the object once you're done with it, and
|
|
want it to neatly go away (close connections, delete temporary files,
|
|
free up memory, etc). But because of the way Perl handles memory,
|
|
most modules won't require the user to know about destructors.
|
|
|
|
* I said that class method syntax has to have the class name, as in
|
|
$session = B<Net::FTP>->new($host). Actually, you can instead use any
|
|
expression that returns a class name: $ftp_class = 'Net::FTP'; $session
|
|
= B<$ftp_class>->new($host). Moreover, instead of the method name for
|
|
object- or class-method calls, you can use a scalar holding the method
|
|
name: $foo->B<$method>($host). But, in practice, these syntaxes are
|
|
rarely useful.
|
|
|
|
And finally, to learn about objects from the perspective of writing
|
|
your own classes, see the C<perltoot> documentation,
|
|
or Damian Conway's exhaustive and clear book I<Object Oriented Perl>
|
|
(Manning Publications 1999, ISBN 1-884777-79-1).
|
|
|
|
=head1 BACK
|
|
|
|
Return to the L<HTML::Tree|HTML::Tree> docs.
|
|
|
|
=cut
|
|
|