Object-Oriented Perl

Anton Berezin

tobez@tobez.org

Object-oriented programming

 

A language mechanism for binding data with methods that operate on that data. Method invocation is often referred to as "sending a message to the object".

Classic OOP Perl

 

To get objects in Perl, we need to store the data somewhere, and associate that data with methods.

References

 

Perl already has references to arbitrary data structures.

my $hashref = {
	a  =>  1,
	b  =>  2,
};

Code references

Perl already has references to arbitrary data structures.

Including references to functions.


sub x { return 42; }

my $funcref = \&x;
print $funcref->();

Objects by hand

So we can do a poor man OO like this:


sub method1 {
	my ($obj) = @_;
	print "Attr1 is $obj->{attr1}\n";
}
sub method2 { ... }

my $obj = {
	attr1 => 1,
	attr2 => 2,
	method1 => \&method1,
	method2 => \&method2,
};

$obj->{method1}->();

Objects by hand

This is UGLY.

(although it has an advantage of not needing an OO language)

One can write such objects in C, if one wants.

(yes, people are known to do just that)

(I've done that myself)

Syntactic help from the language

 

We need a special syntax for method calls.

But not much

 

But beyond the method calls syntax, we need surprizingly little.

Object representation

The "real" objects in Perl are still references.

A reference to pretty much anything can be made into an object.

How to associate an object with a class

Perl has namespaces (packages)

That's what normally used to create modules

But it can work for classes, too.

Blessing a reference

Special builtin called bless


bless $reference, Classname;
$reference is any reference. Classname is any package name.

The result is a blessed reference; an object.

Methods of an object

Methods are just ordinary subs defined in the class'es package.

Call them using dereferencing arrow:


$obj->method($param1, $param2);
The very first parameter passed will be an object reference.

Put it together


package MyClass;

sub my_method
{
	my ($self, $param) = @_;
	if ($self->{attr1} > $param) {
		print "I am the greatest\n";
	} else {
		print "I am the leanest\n";
	}
}

my $o = bless { attr1 => 42 }, 'MyClass';
$o->my_method(17);     # I am the greatest
$o->my_method(137);    # I am the leanest

That's it

 

 

That's it

Thank you.

 

Thank you!

 

Any questions?

Thank you.

 

Thank you!

 

Any questions?

 

Just kidding.

Accessing the data

Since an object is still a reference, the underlying data are easily accessible.


$o->{attr1} = 42;

No protection

Any code outside of the class can easily tinker with internal object representation.

This is considered bad.

Class methods

The same syntax $o->methodname can be used to call class methods.

The only difference is that the package name is used instead of the object reference


MyClass->method(...);

Method calls sidenote

Actually, anything that resolves to either a blessed reference or to a package name can be used with the method call syntax:


my $pkg = "Some::Package";
$pkg->method(...);   # OK

funcall()->{hashkey}->method(...);  # OK

Constructors

Constructors are simply class methods that return an object.

Constructors' names can be arbitrary, although new() and create() are prevalent.


package MyClass;

sub new {
	bless { attr1 => 42 }, 'MyClass';
}

Better constructors

Since constructor is typically called with a method call syntax, its first parameter will be class name.


package MyClass;

sub new {
	my ($class) = @_;
	bless { attr1 => 42 }, $class;
}

Inheritance

 

What about inheritance?

Luckily, the method call syntax in combination with a little extra feature takes care of that.

@ISA

Enter @ISA array.

It is a package global.


package MyInheritedClass;
use vars qw(@ISA);
@ISA = qw(MyClass);

sub my_method2 { ... }

@ISA cont.

When an object or class method is called, Perl gets the package name and tries to find a sub in that package with the same name as the method.

If found, it calls that.

If not, it looks up the @ISA array for other packages, and tries to find the method in it.

UNIVERSAL

All classes implicitly inherit from class "UNIVERSAL" as their last base class.

Which has some handy methods, mainly for introspection.

Shortcut for inheriting

It is a little bit tedious to write


use vars qw(@ISA);
@ISA = qw(MyClass);

so there is a shortcut:


use base 'MyClass';

Calling inherited methods

We can of course do


package MyInheritedClass;
sub method1
{
	my ($self) = @_;
	# do something
	MyClass::method1($self);
	# do something else
}

But this is not OOish.

Besides, it won't work if MyClass does not have sub method1 {}, inheriting it from some other class.

SUPER pseudo-class

The right thing to do would be


package MyInheritedClass;
sub method1
{
	my ($self) = @_;
	# do something
	$self->SUPER::method1();
	# do something else
}

SUPER can only be used with this syntax.

SUPER refers to the current package ancestor.

So don't use it outside of object methods.

Inheriting constructors

A properly written base class constructor will bless the reference into the right class, so we just need to do some initializing:


package MyInheritedClass;
sub new {
	my ($class, %params) = @_;
	my $self = $class->SUPER::new(%params);
	# do something else with params...
	return $self;
}

In many cases such constructors are not needed.

Destructors

Perl has automatic garbage collection, so in many cases destructors are not needed.

When they are, create a sub called DESTROY

.


sub DESTROY {
	my ($self) = @_;
	# free some resources
}

Introspection


my $h = { a => 1 };
my $a = [1,2,3];
my $s = \3;
my $f = \&somesub;
my $g = \*STDOUT;
my $o = bless {}, "MyClass";

Introspection


print "$h\n";   # HASH(0x840016c)
print "$a\n";   # ARRAY(0x8400afc)
print "$s\n";   # SCALAR(0x8400be0)
print "$f\n";   # CODE(0x8400d30)
print "$g\n";   # GLOB(0x8400820)
print "$o\n";   # MyClass=HASH(0x8400d24)

Introspection


print ref $h, "\n";   # HASH
print ref $a, "\n";   # ARRAY
print ref $s, "\n";   # SCALAR
print ref $f, "\n";   # CODE
print ref $g, "\n";   # GLOB
print ref $o, "\n";   # MyClass

is_a


sub is_a {
	my ($o, $isaclass) = @_;
	my $class = ref $o || $o;
	return 1 if $class eq $isaclass;
	for my $inhc (eval "\@$class\::ISA") {
		return 1 if is_a($inhc, $isaclass);
	}
	return 0;
}

UNIVERSAL::isa

Luckily, the UNIVERSAL package already provides isa for us.


	if ($some_object->isa("AnyClass")) {
		...
	}

	if (SomeClass->isa("AnyClass")) {
		...
	}

UNIVERSAL::can

Sometimes we need to know whether a particular object has a certain method.


	if ($some_object->can("drink")) {
		...
	}

	if (SomeClass->can("dance")) {
		...
	}

Indirect method call syntax


	print STDERR "A horrible error\n";

	print STDERR, "Yes indeed\n";

The same syntax got hijacked for objects

Indirect method call syntax


	my $o = MyClass->new(...);

	my $o = new MyClass ...;

Means the same thing.

TIMTOWTDI.

Multiple inheritance

What about multiple inheritance?

Easy.

Just put more stuff into the @ISA.


@ISA = qw(Class1 Class2);

or


use base qw(Class1 Class2);

Multiple inheritance

Conflicts resolution?

Deep-first tree search.

But can be overriddent by CPAN modules (Class::C3).

Conclusions for "classical" OO Perl

Classical OO in Perl: a bit ugly.

Looks like bolted on.

IS bolted on.

Easy to understand.

Almost no magic - just reference blessing.

Surprizingly flexible.

Data storage for objects

Most objects use hashrefs.

Convenient - any amount of data can be associated with an object.

But no protection.

Data storage for objects

But can be any ref.

Data storage as arrays

Array references are used sometimes.

One reason - efficiency, since array access is much faster than hash access.

Another reason - memory use, arrays typically are much smaller.

Not very convenient:


my $o = bless [1,2,3], MyClass;

$o->[0] = 42;   # what's that?
$o->[1] = 137;  # and this?

Data storage as arrays

One can use Class::ArrayObjects (and no doubt ten other CPAN modules) to somewhat alleviate the problem.


package MyClass;

use Class::ArrayObjects define => {
	fields => [qw(hest foo bar)],
};

my $o = ...;

$o->[hest] = 42;

Data storage as arrays

 

Still not protected. But harder to tinker with.

Data storage as scalar ref

Data storage as scalar ref - why?

Typically for really simple stuff.

Like counter class.

But also has other uses; more on that later.

Counter class

Counter class


package Counter;

sub new {
	my ($class, $initial) = @_;
	bless \$initial, $class;
}
sub get { my $self = shift;  $$self }
sub inc { my $self = shift;  $$self++ }
sub dec { my $self = shift;  $$self-- }

Data storage as filehandle

Data storage as filehandle.

Used in special cases.

Example: Net::Telnet module.

Data storage as sub ref

Data storage as sub ref.

This one can be used to protect data.

But the method is quite ugly.

The idea is to call the sub to get to the data.

ProtectedCounter


package SubCounter;
sub new
{
	my ($class, $init) = @_;
	bless sub {
		die "We exist to protect.\n"
			unless (caller)[0] eq "SubCounter";
		return \$init;
	}, $class;
}
sub get { ${$_[0]->()} }
sub inc { ${$_[0]->()}++ }
sub dec { ${$_[0]->()}-- }

ProtectedCounter example


my $c = new SubCounter 42;
print $c->get, "\n";
$c->inc;
print $c->get, "\n";
my $ref = $c->();  # dies here
$$ref++;
print $c->get, "\n";

A note of caution

A note of caution.

Do not over-do OO Perl.

Use objects where it makes sense.

String::Koremutake

NAME
    String::Koremutake -
    Convert to/from Koremutake Memorable Random Strings
SYNOPSIS
    use String::Koremutake;
    my $k = String::Koremutake->new;

    my $s = $k->integer_to_koremutake(65535);
    # botretre
    my $i = $k->koremutake_to_integer('koremutake');
    # 10610353957

What's the point of that??

Accessors

Good OO modules provide accessors where needed.


package MyClass;
sub new { ... }
sub hest { $_[0]->{hest} }
sub set_hest { $_[0]->{hest} = $_[1] }
sub foo { $_[0]->{foo} }
sub set_foo { $_[0]->{foo} = $_[1] }

Gets boring, FAST.

Accessors, improved


package MyClass;
sub new { ... }
sub hest { defined $_[1] ? $_[0]->{hest} = $_[1] : $_[0]->{hest} }
sub foo { defined $_[1] ? $_[0]->{foo} = $_[1] : $_[0]->{foo} }

Still boring.

Accessors, improved++


sub _gen_accessor {
	my $aname = shift;
	eval "sub $aname { defined \$_[1] ?
		\$_[0]->{$aname} = \$_[1] :
		\$_[0]->{$aname} }";
}
_gen_accessor($_) for qw(hest foo bar baz);

Now we have something.

Accessors, via CPAN

Accessors, via CPAN.

There's of course a brazillion of CPAN modules that will do that for you.

Sometimes I hate TIMTOWTDI.

Class::Accessor

Class::Accessor


package MyClass;
use base qw(Class::Accessors);
MyClass->mk_accessors(qw(hest foo));

my $o = MyClass->new({ hest => 42,
	foo => 137});
print $o->hest;
$o->foo("foom");

It also provides new for you.

Singleton classes

Sometimes what you want is that no more than one instance of the object exists in a program.

Example: print spooler.

Singleton classes, by hand


package MySingleton;

my $obj;

sub new { $obj ||= bless {}, $_[0] }

Singleton classes, CPAN

Or use Class::Singleton

Just derive from it.


package MySingleton;
use base 'Class::Singleton';

sub method1 { ... }

Default objects

Sometimes you wish to provide both procedural and OO interface.

In this case it is quite natural for the procedural interface to use a "default" object and then just call OO interface.

Or at least do something to the same effect.

Default objects: CGI.pm

The classic example would be CGI.pm:


use CGI qw/:std/;

my $q = new CGI;
print $q->header, $q->start_html;
or

use CGI qw/:std/;

print header, start_html;

Default objects, by hand


package MyDefault;

my $def;

sub new { ... }

sub do_something
{
	my $self = shift if ref($_[0]) && $_[0]->isa("MyDefault");
	$self ||= $def ||= new MyDefault;

	# now do something
}

Default objects, by hand

So either


my $o = new MyDefault;

$o->do_something;

or


MyDefault::do_something();

will work.

Default objects, CPAN

But of course there is (ARE) CPAN modules to do that.

For example, Class::Default.

More introspection

Class::ISA provides a function that goes through a complex hierarchy of classes and returns a list of all classes that will be searched by the method dispatcher in the order they will be searched.

plus variants of the above

More introspection

Class::Inspector provides a bunch of functions to extract useful information about a class:

More introspection

Class::Handle is a mix of UNIVERSAL, Class::ISA, and Class::Inspector.

Base classes

Class::Base provides a number of useful predefined subs:

Use it if you need those

Delegation

Sometimes an object holds instances of other objects as its attributes. Sometimes one wishes to delegate calls to certain methods of the object to one of the other objects it holds.

Delegation by hand

Delegation by hand is easy to do:

sub meth1 {
    my $self = shift;
    $self->{obj1}->some_meth(@_);
}

sub meth2 {
    my $self = shift;
    $self->{obj2}->some_other_meth(@_);
}

Class::Delegation

Class::Delegation does it for us:

use Class::Delegation
   send => 'meth1',
   to   => 'obj1',
   as   => 'some_meth',

   send => -OTHER,
   to   => 'fallbackobj';
Very useful.

Frameworks

Have a look at Class::Spiffy and at Moose

.

Inside-out objects

Inside-out objects is another way to do the protection of the internal object state.

Instead of using a hashref per object, which is indexed by attribute names to access object attributes, inside-out objects use a private hash per attribute which is indexed by object reference.

Inside-out objects

Traditional:


$o1->{hest} = 42;
$o1->{foo} = "moof";
$o2->{hest} = 137;
$o2->{foo} = "foom";

Inside-out:


my (%hest, %foo);
$hest{"$o1"} = 42;
$foo{"$o1"} = "moof";
$hest{"$o2"} = 137;
$foo{"$o2"} = "foom";

Inside-out objects

Since %hest and %foo are private, this approach provides a pretty good protection mechanism in comparison with the traditional Perl OO.

Even derived classes cannot tinker with our attributes.

It is also practically as efficient as the "normal way".

Since the reference is only used as the unique key, we can use a scalar reference (it's cheap)

Inside-out objects


package MyInsideOut;

{
my %hest;
my %foo;

sub new { bless \(my $dummy), $_[0] }
sub set_hest { $hest{"$_[0]"} = $_[1] }
sub hest { $hest{"$_[0]"} }
...
}

Inside-out objects

We need DESTROY to cleanup.


sub DESTROY {
	delete $hest{"$_[0]"};
	delete $foo{"$_[0]"};
}

Inside-out objects

Another advantage of inside-out objects is that we get compile-time checking of typos:


use strict;
$normal_obj->{tpyo_count}++;   # OK

use strict;
$tpyo_count{$inside_out_obj}++;   # %tpyo_count is unknown

Inside-out objects, CPAN

Use one of:

Class::Closure

Another way to use a closure to get decent data protection.

It does not represent the class as the closure.

But the methods are closures.

All in all, pretty nifty.

Class::Closure


package FurryAnimal;
use Class::Closure;

sub CLASS {
	extends HairlessAnimal;

	has my $hair;

	method shave => sub {
		$hair = undef;
	};
}

Objects with dynamic methods

Using Class::Prototyped, one can dynamically add methods and attributes to an existing, instantiated object.

One can also have objects inherit their behavior and state from another object.

So one can clone, then modify the behavior.

Thank you.

 

Thank you!

 

Any questions?