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".
To get objects in Perl, we need to store the data somewhere, and associate that data with methods.
Perl already has references to arbitrary data structures.
my $hashref = {
a => 1,
b => 2,
};
Perl already has references to arbitrary data structures.
Including references to functions.
sub x { return 42; }
my $funcref = \&x;
print $funcref->();
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}->();
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)
We need a special syntax for method calls.
But beyond the method calls syntax, we need surprizingly little.
The "real" objects in Perl are still references.
A reference to pretty much anything can be made into an object.
Perl has namespaces (packages)
That's what normally used to create modules
But it can work for classes, too.
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 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.
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
Thank you!
Any questions?
Thank you!
Any questions?
Since an object is still a reference, the underlying data are easily accessible.
$o->{attr1} = 42;
Any code outside of the class can easily tinker with internal object representation.
This is considered bad.
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(...);
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 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';
}
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;
}
What about inheritance?
Luckily, the method call syntax in combination with a little extra feature takes care of that.
Enter @ISA
array.
It is a package global.
package MyInheritedClass;
use vars qw(@ISA);
@ISA = qw(MyClass);
sub my_method2 { ... }
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.
All classes implicitly inherit from class "UNIVERSAL" as their last base class.
Which has some handy methods, mainly for introspection.
It is a little bit tedious to write
use vars qw(@ISA);
@ISA = qw(MyClass);
so there is a shortcut:
use base 'MyClass';
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.
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.
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.
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
}
my $h = { a => 1 };
my $a = [1,2,3];
my $s = \3;
my $f = \&somesub;
my $g = \*STDOUT;
my $o = bless {}, "MyClass";
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)
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
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;
}
Luckily, the UNIVERSAL package already provides isa
for us.
if ($some_object->isa("AnyClass")) {
...
}
if (SomeClass->isa("AnyClass")) {
...
}
Sometimes we need to know whether a particular object has a certain method.
if ($some_object->can("drink")) {
...
}
if (SomeClass->can("dance")) {
...
}
print STDERR "A horrible error\n";
print STDERR, "Yes indeed\n";
The same syntax got hijacked for objects
my $o = MyClass->new(...);
my $o = new MyClass ...;
Means the same thing.
TIMTOWTDI.
What about multiple inheritance?
Easy.
Just put more stuff into the @ISA
.
@ISA = qw(Class1 Class2);
or
use base qw(Class1 Class2);
Conflicts resolution?
Deep-first tree search.
But can be overriddent by CPAN modules (Class::C3
).
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.
Most objects use hashrefs.
Convenient - any amount of data can be associated with an object.
But no protection.
But can be any ref.
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?
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;
Still not protected. But harder to tinker with.
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
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.
Used in special cases.
Example: Net::Telnet
module.
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.
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]->()}-- }
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.
Do not over-do OO Perl.
Use objects where it makes sense.
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??
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.
package MyClass;
sub new { ... }
sub hest { defined $_[1] ? $_[0]->{hest} = $_[1] : $_[0]->{hest} }
sub foo { defined $_[1] ? $_[0]->{foo} = $_[1] : $_[0]->{foo} }
Still boring.
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.
There's of course a brazillion of CPAN modules that will do that for you.
Sometimes I hate TIMTOWTDI.
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.
Sometimes what you want is that no more than one instance of the object exists in a program.
Example: print spooler.
package MySingleton;
my $obj;
sub new { $obj ||= bless {}, $_[0] }
Or use Class::Singleton
Just derive from it.
package MySingleton;
use base 'Class::Singleton';
sub method1 { ... }
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.
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;
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
}
So either
my $o = new MyDefault;
$o->do_something;
or
MyDefault::do_something();
will work.
But of course there is (ARE) CPAN modules to do that.
For example, Class::Default
.
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
Class::Inspector
provides
a bunch of functions to extract useful information about
a class:
Class::Handle
is a mix
of UNIVERSAL, Class::ISA, and Class::Inspector.
Class::Base
provides a number
of useful predefined subs:
Use it if you need those
sub meth1 {
my $self = shift;
$self->{obj1}->some_meth(@_);
}
sub meth2 {
my $self = shift;
$self->{obj2}->some_other_meth(@_);
}
use Class::Delegation
send => 'meth1',
to => 'obj1',
as => 'some_meth',
send => -OTHER,
to => 'fallbackobj';
Very useful.
Have a look at Class::Spiffy
and
at Moose
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.
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";
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)
package MyInsideOut;
{
my %hest;
my %foo;
sub new { bless \(my $dummy), $_[0] }
sub set_hest { $hest{"$_[0]"} = $_[1] }
sub hest { $hest{"$_[0]"} }
...
}
We need DESTROY
to cleanup.
sub DESTROY {
delete $hest{"$_[0]"};
delete $foo{"$_[0]"};
}
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
Use one of:
Class::Std
Object::InsideOut
Class::InsideOut
...or roll your own.
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.
package FurryAnimal;
use Class::Closure;
sub CLASS {
extends HairlessAnimal;
has my $hair;
method shave => sub {
$hair = undef;
};
}
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!
Any questions?