OOP for Perl.

What we expect from OOP

Classes (methods, types, attributes, open recursion...) Encapsulation Aggregation Message passing and dynamic dispatch Inheritance and polymorphism Abstraction
Skip definitions


Templates for defining objects state and behavior


Specifying which classes may use the members of an object e.g: public, protected or private, determining whether they are available to all classes, sub-classes or only the defining class.

Aggregation ● Object containing another object .

the object itself determines what code gets executed by looking up the method at run time in a table associated with the object (needed when multiple classes contain different implementations of the same method ) .Message passing and dynamic dispatch ● Message passing is the process by which an object sends data to another object or asks the other object to invoke a method ● when a method is invoked on an object.

which inherit attributes and behaviors from their parent classes. and can introduce their own.) ● a class has all the state and behavior of another class polymorphism .Inheritance and polymorphism ● allows the programmer to treat derived class members just like their parent class's members ("Subclasses" are more specialized versions of a class.

What OOP features offers Perl? ● MOP in Perl5: ● Class .a subroutine within a package Constructor – a method that returns reference to blessed variable ● .) ● ● Method . array.package Object . or reference to subroutine.blessed reference (a scalar type holding a reference to object data – object may be a hash.

} .What OOP features offers Perl? package Car. my $self = {}. bless ($self. return $self. sub new { my $class = shift. my $brand = shift. $self->{'brand'} = $brand. $class).

● ● “Inside-out” classes not handy. sub ask_for_pin{ my $self = shift. package Kido. } package main. } else { return 'forget!'. my $kido = Kido->new().. return $closure. sub new{ my $class = shift.. return &{$self}($how). private function package Mom.” . Some tricks: private variable. }. print $kido->ask_for_pin('not nice'). print $kido->ask_for_pin('nice').. “There is only one way to do it. my $how = shift.What OOP features offers Perl? ● Encapsulation – in general only by convention. private method. $class). if ($arg and $arg eq 'nice'){ return $self->{'CARD_PIN'}. use base qw(Mom). my $closure = sub { my $arg = shift. bless($closure. . }. my $self = { 'CARD_PIN' => '0000'. } }. my $mom = Mom->new()..

print "cookies ready\n".. . } } sub make_cookies{ _prepare_pastry(). my $mom = Mom->new(). { package Mom.) package main.. } } package main.) sub _prepare_pastry{ if (caller ne __PACKAGE__){ die. }. sub make_cookies{ &{$_pastry}..What OOP features offers Perl? { package Mom. (. (. $mom->_prepare_pastry().. } else { print "makin' pastry \n". $mom->make_cookies().) my $mom = Mom->new()...) my $_prepare_pastry = sub{ print "makin' pastry 2 \n". print "cookies ready\n".. (. } } package main. #this compiles! $mom->make_cookies(). (..

} return $self->cars. my $self = {}. $class ). $car. $eva->cars->[0]->brand(). return $self. sub new { my $class = shift. } my $eva = Person->new(). if ( ref( $car ) eq 'Car' ) { push @{ $self->{'cars'} }. my $car = shift. } sub cars { my $self = shift. $self->{'cars'} = (). my $car1 = Car->new(). $car1->brand( 'Fiat' ). bless( $self.What OOP features offers Perl? ● Aggregation package Person. #returns 'Fiat' .

bless( $self. $adam->say_name package Person.What OOP features offers Perl? ● Inheritance (multi-object) and polymorphism: use base() package Employee. printf( "My job is %s. return $self. } #inheritance and polymorphism my $adam = Employee->new( 'Developer' ). $class ). $class ). sub new { my $class = shift. \n". } sub name { my $self = shift. bless( $self. use base qw(Person). printf( "My name is %s. my $job = shift. $self->{'job'} ). } . } sub say_name { my $self = shift. $adam->name( 'Adam' ). sub new { my $class = shift. } #overriding sub say_name { my $self = shift. return $self. my $self = {}. $self->{'name'} ). $self->SUPER::say_name(). $self->{'job'} = $job. my $self = $class->SUPER::new().\n". if ( @_ ) { $self->{'name'} = shift } return $self->{'name'}.

sub load{ my $self = shift. } package BMP. use Carp. \n". print "File loaded. $bmp->load().What OOP features offers Perl? ● Abstraction – only objects package File. . } my $bmp = BMP->new(). sub new { my $class = shift. my $jpg = JPG->new(). package JPG. bless ($self. } sub load{ croak('not implemented'). my $self = {}. use base qw(File). $class). use base qw(File). return $self. $jpg->load().

● There is more than one way to do it! . no horizontal inheritance. no encapsulation..What OOP features offers Perl? What is missing? ● The in-language support for OO features is minimal. No type checking..

but sometimes consistency is not a bad thing either! .Moose There is more than one way to do it.

Moose = Class::MOP + semantic .Moose is Perl.

allowing you to improve your ● code with features that would otherwise be too complex or expensive to implement . ● common system for building classes new levels of code reuse.Moose is Perl.

the ability to convert types of one type to another when ● necessary ● Encapsulation Method Modifiers .roles .Moose is Perl.the ability to add predefined functionality to classes without sub-classing … and much more. . after or ● around an existing method ● Horizontal inheritance .the ability to add code that runs before. Everything missing in OO Perl you can find in Moose + some important bonuses! ● Type matching Coercion .

no Moose. print "Going to $place_name \n". print $acar->RegNum. 1. Sets strict and warnings MOOSE CLASS package Vehicle. 'isa' => 'Str'). We get new() for free We get also default accessors for free. use Moose. sub goto{ my $self = shift. $acar->goto('Oriente'). use Vehicle. has 'RegNum' => ( 'is' => 'rw'. Class attributes. } __PACKAGE__->meta->make_immutable. Class methods.Moose – first example. my $place_name = shift. $acar->RegNum('123456'). my $avehicle = Vehicle->new(). .

. } no Moose. package Vehicle. use strict. my %args = @_.Moose – first example. has 'RegNum' => ( 'is' => 'rw'. } 1. } return bless($self. my $place_name = shift. 12 lines of code from previous slide give us all this: package Vehicle. use Moose. } sub goto{ my $self = shift. } return $self->{'_RegNum'}. if ( exists( $args{'RegNum'} ) && defined( $args{'RegNum'} ) ) { $self->{'_RegNum'} = $args{'RegNum'}. } sub RegNum { my $self = shift. my $place_name = shift. my $self = { '_RegNum' => undef }. print "Going to $place_name \n". $class). sub new { my $class = shift. print "Going to $place_name \n". 1. 'isa' => 'Str'). sub goto{ my $self = shift. my $regnum = shift. if ( $regnum ) { $self->{'_RegNum'} = $regnum.

. Str.. ArrayRef. Num.Lets have a closer look.. Int.) 'rw' or 'ro' – tells which accessors will be created has 'RegNum' => ( 'is' => 'rw'. and more 'is' – defines if argument is read-write or read-only 'isa' – defines type of the attribute (and validate it during assignment) . (. 'isa' => 'Str' )...) Moose types: Bool. ● Arguments (.. HashRef.

Lets have a closer look.. The only right moment to set up the attribute is during initialization. If we restrict access to the attribute. $avehicle->RegNum('123456'). print $avehicle->RegNum..only arguments has 'RegNum' => ( 'is' => 'ro'. 'isa' => 'Str' )."\n". ● Read. the default accessor won't be created. .. We can still read it wherever we want. This method doesn't exist – it results in compilation error my $avehicle = Vehicle->new( 'RegNum'=>'1234' )..

… this will not compile #$acar = Vehicule->new().but we still can have illegal vehicle.. my $avehicle = Vehicle->new('RegNum'=>'123456'). Required attribute must be set. BUT If attribute has no type it can be set to undefined . 'required' => 1. It means that. 'required' => 1. RegNum can be set to empty string.What more we can do with attributes? ● Required arguments has 'RegNum' => ( 'is' => 'ro'. 'isa' => 'Str'. ). ). my $avehicle = Vehicle->new('RegNum'=>''). .... has 'RegNum' => ( 'is' => 'ro'. my $avehicle = Vehicle->new('RegNum'=>undef)..

'predicate'=>'is_registered'.. You can't use accessors because it is “ro”. if (!$avehicle->is_registered()){ print "Vehicle illegal!. the vehicle is a broken and is waiting to be scraped.. 'isa' => 'Str'. but first you must unregister it .What more we can do with attributes? ● Clearer and predicate Imagine. ). $avehicle->unregister().\n". .. print $avehicle->RegNum. but. you can set clearer to clean the value and predicate to check if the value exists my $avehicle = Vehicule->new('RegNum'=>'1234'). } The method you get for free. 'clearer' => 'unregister'. 'required'=> 1.. has 'RegNum' => ( 'is' => 'ro'.

. 'clearer' => 'unregister'. 'isa' => 'RegistrationNo'. compare $_ to something and use it my $avehicle = Vehicle->new('RegNum'=>'11-xx-22'). => message {"$_ is not valid registration number."}. has 'RegNum' => ( 'is' => 'ro'. 'required'=> 1. 'predicate'=>'is_registered'. ). package Vehicle. Subtypes can be more complex more examples here . use Moose.What more we can do with attributes? ● Subtypes use TypeConstraints and create subtype parent type Not every string can be the registration number. #my $avehicle = Vehicle->new('RegNum'=>'111-xx-22').. use Moose::Util::TypeConstraints. subtype 'RegistrationNo' => as 'Str'. => where { /^(\d\d)(-)(\w\w)(-)(\d\d)$/ }.

'. what we want to create what we use to create subtype Point.. as Int. where { Point->check( $_->{'X'} ) and Point->check( $_->{'Y'} )}.. message {'Incorect coordinate. message {'Must be positive number. a point A coordinate that is a has of two valid points subtype Coordinate. where { $_>= 0}. This is very useful type..$_ }.'}. as HashRef.One Step Further . package MyTypes. use Moose. .two points: x and y that must have positive value. a 2D coordinate . We can pack it in separate package and reuse later.MooseX. use MooseX::Types -declare => [qw(Coordinate Point)]. use MooseX::Types::Moose qw(Int HashRef). ● Custom types Lets define where is our vehicle.

.. (.MooseX. use it where ever you need . ● Custom types import custom type Lets use the coordinate to define where is the vehicle.One Step Further .pm) my $place = {'X'=>1..) modify the vehicle class (Vehicle.. 'Y'=>3 }. (. $avehicle->place( $place ).) has 'place' => ( 'is' => 'rw'. 'isa' => Coordinate. ). use MyTypes qw( Coordinate ).

where { $_> 0}.. 'coerce' => 1. as Int. package MyTypes. message {'Incorect coordinate...$_ }.? $avehicle->place( [4.One Step Further ..MooseX. Don't forget to turn on the coercion on the argument (. as HashRef. 'Y'=>$_->[1]} }. use Moose..'}. use MooseX::Types -declare => [qw(Coordinate Point)]. via { {'X'=>$_->[0]. message {'Must be positive number.'. where { Point->check( $_->{'X'} ) and Point->check( $_->{'Y'} )}.) coerce Coordinate. from ArrayRef. ). in class definition. 'isa' => Coordinate. (. . In the end. use MooseX::Types::Moose qw(Int HashRef ArrayRef).) has 'place' => ( 'is' => 'rw'. subtype Point.5] ).. 1. ● Custom types and coercion But maybe it would be easier to use it this way. We define how we can transform one type to another. subtype Coordinate.

MOOSE is PERL . 'writer' => '_set_place'.'. However this still would work. $self->_set_place($coordinate). but only privately settable. my $coordinate = shift. ).) #an attribute to be publicly readable.$avehicle->_place->{'X'}. This is what the manual says – but this is only CONVENTION. 'Y'=>3 }. reader and writer redefine the names of custom accessors Now we can “move” the vehicle. #my $place = {'X'=>1..Private attributes – only convention. 'isa' => Coordinate.. print 'The vehicle is at'.'.$avehicle->_place>{'Y'}.'. 'coerce' => 1. #my $other_place = [4. sub goto{ my $self = shift..5].'. print Dumper($avehicle).8]). The vehicle may change its place only when it moves ? (.. } (. #$avehicle->_set_place( $place ).) $avehicle->goto([7. has '_place' => ( 'is' => 'ro'.

$self->_set_mileage($self->_mileage + $distance). my $distance = sqrt(($x1-$x2)**2+($y1-$y2)**2). my $self = shift. 'default' => 0. Executing main method Accessing modified attributes other actions . ). 'writer' => '_set_mileage'. Method modifier Has access to attributes. my $coordinate2 = $self->_place. 'isa' => 'Num'. my $coordinate1 = $self->_place. my $y2 = $coordinate2->{'Y'}. One more private attribute. }. my $y1 = $coordinate1->{'Y'}. #@_ stores new coordinate $self->$orig(@_).Method modifiers Mileage – every time the vehicle moves its mileage increase. my $x1 = $coordinate1->{'X'}. 'required' => 1. before they are modified by method around 'goto' => sub { my $orig = shift. has '_mileage' => ( 'is' => 'ro'. my $x2 = $coordinate2->{'X'}.

'default' => sub {[0. 'writer' => '_set_place'. ).8]). 'coerce' => 1.0]}.5]). $avehicle->goto([4. $avehicle->goto([7.1]). 'isa' => Coordinate. has '_place' => ( 'is' => 'ro'. $avehicle->goto([1.Method modifiers Mileage – every time the vehicle moves its mileage increase. . print $avehicle->_mileage Every time we move vehicle the mileage increase. Define default value print $avehicle->_mileage.

Imagine we want to sell the vehicle. The price depends on the mileage. my $price = 100000. 'lazy_build'=>1. 'isa' => 'Num'. $avehicle->goto([1. lazy + clearer = lazy_build Lazy needs default value or builder Method building the value. $avehicle->goto([4. print $avehicle->price. } Lazy . 'predicate'=>'has_price'.. Lazy build calculates the value only if it is unset.Lazy. lazy_build and trigger. 'init_arg'=>undef. print $avehicle->price. has 'price' => ( 'is' => 'ro'. the price didn't change. sub calculate_price { my $self = shift. if ($self->_mileage != 0){ $price = $price / $self->_mileage. ).1]).value is calculated only when it is accessed and is unset. . Therefore we need to clean it every time we want to get updated value.despite we move.. 'builder'=>'calculate_price'.5]). } return $price. .

.$avehicle->sell_vehicle. $avehicle->goto([4. } ). Every time the mileage changes the clearer for price attribute is triggered.Lazy. lazy_build and trigger.1]). Useful for expensive calculation. 'trigger' => sub { my $self=shift. "\n". After this change the previous example shows updated value every time we access it Lazy build is very useful for attributes are expensive to calculate – we calculate it only while reading. $self->clear_price. } $avehicle->goto([1. print $avehicle->price. sub sell_vehicle { my $self = shift. print $avehicle->price. 'required' => 1. The price is updated every time we read it using the method. 'writer' => '_set_mileage'. return $self->price. print "The final price: ". $self->clear_price. 'isa' => 'Num'.5]). We can also use method instead the trigger. Let's run the clearer automatically. has '_mileage' => ( 'is' => 'ro'. 'default' => 0.

Code reuse . Is the vehicle car or bike? package Car. if($self->fuel eq 0){ print 'No fuel'. $self->fuel($self->fuel + 10).inheritance in Moose. no Moose Parent class New attribute New method Overwritten method Calling super . extends 'Vehicle'. use Moose. return. } return super. sub refuel { my $self = shift. 'isa' => 'Int'. }. } override goto => sub{ my $self = shift. ). has 'fuel' =>( 'is' => 'rw'. 'default'=>0.

use Moose. no Moose.Code reuse . registration number is not linger required . extends 'Vehicle'. 1. package Bike.inheritance in Moose. changing inherited attribute my $abike = Bike->new(). ). has '+RegNum' => ( 'required'=> 0.

But we can use a role in a class. ● ● .” Role is not a class – can't be subclassed. can't be extended … we can't create an object of a role . and methods of the role in an object.Better code reuse – inheritance and roles. Short explanation: ● Role is a “state or behavior that can be shared between classes.

my $load = shift. }else{ print 'Load unsuccessful. sub do_load { my $self = shift. ). if(($self->capacity >= $load) and !$self->load){ $self->load(1). has 'capacity' => ( 'is' => 'ro'.'.Better code reuse – inheritance and roles. 'isa' => 'Bool'. ). 'default'=>0. has 'load' => ( 'is' => 'rw'. my $load = shift. 'default'=>10. We create a piece of functionality exactly the same way as we were creating a moose class. } } sub do_unload { my $self = shift. The only difference package Truck. 'required'=>1. use Moose::Role. if($self->load){ $self->load(0). }else{ print 'Nothing to unload. 1. . } } no Moose. We can create a 'truck' role and use it in our vehicle.'. 'isa' => 'Int'.

with 'Truck'. #$alorry->goto([2. #unsecessfull . We want to have a car with a “truck functionality” . . Lorry = Car with Truck role How to use Lorry? (…) use Lorry. But Lorry is still a car so won't go without fuel. no Moose.a lorry. 'capacity'=>50). package Lorry.4]). my $alorry = Lorry->new('RegNum'=>'11-xx-23'. 1. Lorry has a Truck role – mast have capacity.still can't go without the fuel We can use all truck functions. use Moose. .is loaded $alorry->do_unload(). . extends 'Car'. $alorry->do_load(30). #$alorry->do_load(40). #$alorry->do_load(100).Better code reuse – inheritance and roles.more than capacity $alorry->do_load(40). (…) Lorry is a vehicle – mast have registration number.

with 'Truck'..) . no line 31 (. 'Moose::Meta::Class=HASH(0xa5f8754)') called at /usr/local/lib/perl5/site_perl/5.Better code reuse – inheritance and roles. 'Moose::Meta::Role=HASH(0xa5f6a6c)'. In the end we want to assure that the truck role will be only used with anything that uses fuel (Have you ever seen a lorry-bike?) package Truck. Since bike doesn't use fuel this code will not compile.0/i386-linux-thread-multi/Moose/Meta/Role/Application/ToClass. extends 'Bike'. (…) use Moose. 'Truck' requires the method 'fuel' to be implemented by 'Lorrybike' at /usr/local/lib/perl5/site_perl/5.. requires 'fuel'.10. use line 51 Moose::Meta::Role::Application::apply('Moose::Meta::Role::Application::ToClass=HASH(0xa5faa2c)'. We require that a method 'fuel' is present in a class where we use the Truck role.10.0/i386-linux-thread-multi/Moose/Meta/Role/Application.

return $class->$orig( %args ). } return $class->$orig( %a ). It would be awesome to create a vehicle this way : $acar = Vehicule->new('11-xx-11').BUILDARGS and BUILD Let's go back to Vehicle. my $class = shift. if ( @_ == 1 ) { $args{ 'RegNum' } = $_[0]. around BUILDARGS => sub { my $orig = shift. }. my %args = (). It has direct access to arguments passed in method call. my %a = @_. BUILDARGS method is called as a class method before an object is created. .

BUILD method is called after an object is created.. But we want to be warn about it. .\n". sub BUILD { my $self = shift.. my $abike2 = Bike->new('22-yy-66'). my $abike = Bike->new(). } } } We will be warn about creating a bike without a registration number.BUILDARGS and BUILD There is a possibility to have a bike without registration. if(!$self->RegNum){ if($self->isa('Bike')){ print "Vehicle without registration number.

no Moose.. map. sort. defined. set. toggle. (these accessors use type checking). Bool: set. Code: execute. 'handles'=> { 'add_vehicle' => 'push'. pop. use Moose::Util::TypeConstraints. delete. is_empty … String: append. More native traits: Array: push... Defining accessors. unset. 'default' => sub {[]}. 'get_vehicle' => 'shift'. . ). length. shift.. clear. has 'vehicles' => ( 'isa'=> 'ArrayRef[ Vehicle ]'. 'traits' => ['Array']. Here we make the vehicles attribute work as an array. package Garage. grep … Hash: get. substr. 1. use Moose. exists. Notice: there is no 'is' =>'ro|rw' Trait – a role added to attribute. }..Aggregation and Native Traits Let's create a garage for our vehicles.. unshit. execute_method.

$garage->add_vehicle($acar). $acar2 and $acar is the same (reference to the same object) . my $v = $garage->get_vehicle().. my $acar2 = $garage->get_vehicle(). my $garage = Garage->new(). $garage->add_vehicle($avehicle).Aggregation and Native Traits Let's park some vehicles..

What more you can do in Moose? For example: ● Delegation . ● Custom traits ..MooseX Custom OO system – sub-classing methaclasses And much more.."proxy" methods call some other method on an attribute. ● ● .

Some tips ● If you don't know what is wrong try to load package from command line : perl -Ilib/ -e 'use Coordinate' ● $object->dump for debugging .

slideshare. but also very Moose is Perl: A Guide to the New Revolution ● http://www.oscon.cpan.More information: ● http://www.21/ Moose::Manual and Moose::Cookbook ● .net/dtreder/moose-527243 Awesome presentation with huge dose of humor.

Sign up to vote on this title
UsefulNot useful