3. Without objects
we manipulate scalars, arrays, hashes...
$hash = {
key => 42,
another => {
one => 43,
deep => {
here => {
you => {
can => 'find me'
}
}
},
str => "Data value"
},
elts => [ 8, 25, 37, 64 ]
};
this lead to store data in /ugly/ hashes
4. Problems with this approach :
you need to know the "exact key" to use...
$hash->{key} = 42;
$hash->{anotherlevel}->{key} = "value";
$hash->{where}->{can}{be}{mykey} = { it => 's there' };
deep research into the code...
or need to dump huge hash
5. You can s/confound/mispell/ key name
somewhere
sub method {
my $h = shift;
$h->{key} = 42;
}
then later
...
$h->{keys}++ and $h->{kyes} = 51;
...
6. You cannot change the key name
or storage level
cannot become neither
$hash->{current_key} = 42;
nor
$hash->{updated_key} = 42;
$hash->{better}{key} = 42;
without updating
the full code !
7. You cannot control life cycle of an attribute :
sub somewhere {
...
$hash->{mykey} = 42 and additionnal_operation;
...
}
sub otherplace {
...
$hash->{mykey} = 43 and additionnal_operation;
...
}
•on create
•on update
•before, after, around
•hooks : innner, augment...
without objects
8. You would need an accessor
it should be the only single way
to access to this piece of data
sub mykey {
my ($self, $value) = @_;
if (defined $value) {
$self->{_mykey} = $value;
$self->additionnal_operation();
}
$self->{_mykey};
}
9. Writing documentation
=pod
=head attribute
Parameters : attribute value ( optional )
Description : attribute method can be used to read or update it !
Returns : current value
Usage :
# use as reader
my $read = $object->attribute();
# use as writter
$object->attribute($new_value);
=cut
sub attribute {
... some code there ( or not ! )
}
... is difficult
rather than
Please use $object->{level1}{level2} to access the expected value !
# at this time, and it could never change ?
10. What is an object ?
•functions
•data
Small piece of code
11. Why object / module ?
•code is organized
•Object is DRY, Lazy & Fun
•code is easier to read / share
•design & development can be dissociate
•easier to test
•provides fault containment
•reduce code maintenance
•simplify product evolution
•documentation is easier
12. Code organization
Object
|__ Human
| |___ Male
| |___ Female
|
|__ Transport
|___ Car
|___ Plane
|___ Bicycle
#!perl
package Object::Transport::Car;
13. DRY : Do not Repeat Yourself
package Document::Page;
sub create {
my $self = shift;
...
do_some_stuff; # inner();
...
}
package Document::Special;
extends 'Document::Page';
augment 'create' => sub {
my $self = shift;
$self->do_some_extra_stuff;
};
•inheritance help you factorize your code
•roles ( ~ interface )
14. Lazy
package Person;
use Moose; # or any other object module
has 'age' => ( is => 'rw', isa => 'Int', default => 42 );
package main;
my $p = Person->new() or Person->new(age => 64);
$current_age = $p->age();
$p->age($new_age);
•provide "new" method
•provide accessors
•parameters validation :
types, optional, default value...
15. Type checking
package Person;
use Try::Tiny;
use Moose;
use Moose::Util::TypeConstraints;
subtype 'Sex'
=> as 'Str'
=> where { $_ =~ m{^[mf]$}s };
has 'sex' => ( is => 'ro', isa => 'Sex', required => 1 );
has 'age' => ( is => 'rw', isa => 'Int', default => 42 );
my $person = Person->new( sex => 'm', age => 45 );
try {
Person->new( sex => 'unknown' );
} catch {
warn "Error has been detected";
};
16. Coercion
package My::Types::Date;
use Moose::Util::TypeConstraints; use DateTime; # ...
subtype 'MyType:Day' => as 'DateTime';
coerce 'MyType:Day' => from 'Str' => via {
/^d{4}d{2}d{2}$/ or croak "Unable to coerce '$_' into a valid date";
return DateTime::Format::DateParse->parse_datetime($_);
};
package Test::Coerce;
use Moose; use My::Types::Date;
has day => ( is => 'rw', isa => 'MyType:Day',
coerce => 1);
package main;
my $d1 = Test::Coerce->new(day => DateTime->now());
my $d2 = Test::Coerce->new(day => '20111130');
isa_ok $d2->day, 'DateTime', 'day is coerced to a DateTime object';
17. Which object library to choose ?
•Moose
•Mouse
•Moo
•Mo
•M
•...
•fields
•Object::Tiny(::XS)
•Class::XSAccessor
•write your own simple Object ?
and sometimes none
18. •the most advanced
•large ecosystem of extensions :
MooseX : validate, getopt, singleton,
types...
•can use advanced types and coercion
methods
•hooks : before, after, inner, augment...
•startup time
•memory usage
•dependencies...
Moose : the fat one !
fat but so good...
Advantages :
Disadvantages :
19. •same goal as Mouse : provide a Moose lighter
•but provide "as little as possible" : minimalist
Moo : the light one !
package Demo::Moo;
use Moo;
with 'Some::Role';
has bar => (
is => 'rw',
isa => sub { $_[0] =~ /^[+-]?d+$/ },
coerce => quote_sub q{ $_[0] + 1 unless $_[0] % 2 },
lazy => 1,
);
•can use only one role at a time
•not available : super, override, inner, augment
•no initializer
Start with Moo and if you need more, switch to Mouse / Moose
20. •use Class::XSAccessor
•fast, easy but extremely limited
•only implement getters
•do not provide setters
Object::Tiny[::XS] : The fast one !
package MyClass;
use Object::Tiny::XS qw{ list of attributes };
1;
package main;
my $object = MyClass->new( list => [ 42, 51 ], of => 'numbers' );
say join(',', @{$object->list}, $object->of);
eval { $object->attributes( 63 ); } or warn "Update value failed !";
21. Writing your own object module ?
package My::Object; # Inspired from Object::Tiny
sub import {
return unless shift eq __PACKAGE__;
my $pkg = caller;
@{"${pkg}::ISA"} = 'My::Object';
map {
my $method = "${pkg}::$_";
my $code = _get_accessor_for($_);
{ no strict 'refs'; *$method = $code; }
} @_;
return 1;
}
sub _get_accessor_for {
my ($key) = @_;
return unless caller eq __PACKAGE__; # Did I say private ?
defined $key and !ref $key and $key =~ /^[^Wd]w*z/s
or croak("Invalid key '$key'");
sub {
my ($self, $v) = @_;
$self->{$key} = $v if defined $v;
$self->{$key};
};
}
sub new {
my $class = shift;
bless { @_ }, $class;
}
# that’s all !
22. Using your own object module
package main;
# initialize your object using new method
my $elt = MyClass->new(list => [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ]);
# getter available
my $list = $elt->list;
# setter available
$elt->of('numbers');
package MyClass;
use My::Object qw{ list of attributes };
# that's all you need !
create a class
start playing with it
23. Extreme performance with Class::XSAccessor
package SuperFast;
use Class::XSAccessor
replace => 1,
constructor => 'new',
accessors => {
speed => 'speed',
},
predicates => {
has_speed => 'speed',
};
package main;
my $o = SuperFast->new(speed => 'flash');
is($o->speed, 'flash');
$o->speed('max') and say $o->speed;
is($o->speed, 'max');
•Fast read & write
•provide accessors, getters, setters and predicates
•use Devel::PPPort Perl Pollution Portability
24. Still want to keep using [better] hash ?
package Animal::Sounds;
use fields qw(cat dog bird);
sub new {
my Animal::Sounds $self = shift;
$self = fields::new($self) unless ref $self;
return $self;
}
package main;
my Animal::Sounds $sound = Animal::Sounds->new(); # typed lexical
$sound->{cat} = 'miaaooo'; # ok
$sound->{horse} = 'hiia'; # generates compile-time error
# No such class field "horse" in variable $sound of type Animal::Sounds
fields builtin provide compile-time class
Objects ( typed ) with named fields are
as compact and as fast arrays to access
25. fields limit
# extract from field.pm
*new = sub {
my $class = shift;
$class = ref $class if ref $class;
require Hash::Util;
my $self = bless {}, $class;
# The lock_keys() prototype won't work since we require Hash::Util :(
&Hash::Util::lock_keys(%$self, _accessible_keys($class));
# spent 6.00s making 20172 calls to Hash::Util::lock_keys, avg 297us/call
# spent 710ms making 20172 calls to fields::_accessible_keys, avg 35us/call
return $self;
}
be careful in production ( perl >= 5.009 )
Trick : mock *fields::new depending on current env
or switch to another object module
121 13.3ms
122 7.62ms
123 20.9ms
124 35.2ms
125
126
127 6.71s
128 61.6ms
129
•provides fast read access to attributes
•slow on object creation
26. Control hash life cycle
package main;
use Human;
use Test::More;
my $john = Human->new;
$john->{age} = 45;
is($john->{age}, 45, "nothing special");
$john->{age} = 123;
is($john->{age}, 99, "what happens there ?");
Still want to use hash ?
How is it possible ?
27. Use tie to hook
package Human;
sub TIESCALAR { die unless(ref $_[1] eq 'CODE'); my $cod=$_[1]; bless $cod, $_[0]; }
sub STORE { my $self = shift; $self->(@_); }
sub FETCH { shift->(); }
# many more available : DELETE, CLEAR, EXISTS, NEXTKEY...
sub new {
my $class = shift;
my $self = bless {}, $class;
my $age;
tie $self->{age}, __PACKAGE__, sub {
my ($value) = @_;
return $age unless defined $value;
warn "You looks too old" and $value = 99 if $value >= 100;
$age = $value;
return $age;
};
$self;
}
You can also tie array, hash...
28. When we should not choose object ?
"Premature optimization is the root of all evil"
Tony Hoare
•performance ?
•difficult to learn ?
•need to create too many objects ?
•do not like abstraction ?
29. Performance problems ?
1/ Analyze the problem origin
•waiting for database
•waiting for IO
•CPU usage
•Memory usage
•.... use Devel::NYTProf
2/ then apply the correct solution
•use parallel algorithm / poe
•use inline C / XS
•do not use object
•...
30. Object Benchmark
Moo : best average solution ( also consider Mouse )
Class::XSAccessor : performance
fields : when many update and few object creation needed
31. Common Errors & Tricks
•make Moose package immutable
no Moose;
__PACKAGE__->meta->make_immutable();
•object creation is time consuming :
use cache or singleton if possible
•use factory for abstraction
•fields use Hash::Util::Lock
# bad idea if too many objects creation
•be careful with Time syscall, try to share it :
my $now = Datetime->now();
•be careful with coercion
# do not try to do too much
~ /black mag/ic