Slide 1

Slide 1 text

Object Oriented Perl Code Reuse Made Simple Thursday, December 29, 2011

Slide 2

Slide 2 text

Agenda ✦ OO Intro ✦ Meet The Moose ✦ Classes, Methods, Attributes ✦ Roles ✦ Best Practices Thursday, December 29, 2011

Slide 3

Slide 3 text

Objects Model Real World Entities Car -year -max_speed -color +speed_up() +stop() Photo: http://www.flickr.com/photos/visualpanic/253081627/ Thursday, December 29, 2011

Slide 4

Slide 4 text

State & Behavior ✦ An object keeps a state that is internal to the object ✦ Behavior (or methods) affect the object’s state Internal State Behavior Thursday, December 29, 2011

Slide 5

Slide 5 text

Objects Relations My Bike Stop Me Thursday, December 29, 2011

Slide 6

Slide 6 text

Classes ✦ A group of objects sharing the same properties is called a class ✦ Each object in the class can take the same state, and provide the same behavior Thursday, December 29, 2011

Slide 7

Slide 7 text

✦ When several classes provide the same behavior, but not the same state - we say they share an interface ✦ Other classes interacting with the interface don’t “know” what exactly is there on the other side Interfaces Thursday, December 29, 2011

Slide 8

Slide 8 text

Duck World Duck +quack() +eat() http://www.flickr.com/photos/andertoons-cartoons/4328481784/ Thursday, December 29, 2011

Slide 9

Slide 9 text

Duck World ✦ Every duck can quack() and eat() ✦ Each duck does it differently ✦ Interfaces Thursday, December 29, 2011

Slide 10

Slide 10 text

Reasons To Use OO ✦ Code Reuse ✦ Easier Problem Domain Modeling ✦ Easy To Test ✦ Not polluting namespaces Thursday, December 29, 2011

Slide 11

Slide 11 text

OO Example ✦ Model a drawing application ✦ User can add rectangles, triangles and lines ✦ Can save final image to disk or print Thursday, December 29, 2011

Slide 12

Slide 12 text

OO Example Canvas Triangle Rectangle Circle Shapes Disk Print Output Thursday, December 29, 2011

Slide 13

Slide 13 text

Let’s Talk Perl ✦ Original p5 had a minimal object system ✦ Standard modern p5 object system is called Moose ✦ Not part of core perl - need to download from CPAN Thursday, December 29, 2011

Slide 14

Slide 14 text

Moose Post Modern Perl Object System http://www.flickr.com/photos/49404126@N00/86395457/ Thursday, December 29, 2011

Slide 15

Slide 15 text

Hello Moose ✦ A class is just a package ✦ A method is just a subroutine ✦ An attribute is... we’ll get back to that use strict; use warnings; package Person; use Moose; has 'name', is => 'ro', isa => 'Str'; has 'age', is => 'rw', isa => 'Int'; package main; use feature ':5.10'; my $p = Person->new(name => "James"); say $p->name; Class Def Class Use Thursday, December 29, 2011

Slide 16

Slide 16 text

Moose Starter ✦ Use a separate file for each class, named Class.pm (capital first) ✦ Use a package declaration at the head of each file ✦ Use ->new to create a new object ✦ Use .t files to test your class Thursday, December 29, 2011

Slide 17

Slide 17 text

Adding Functionality ✦ Use methods for functionality ✦ A method takes an object as its first argument ✦ All other arguments are passed as expected Thursday, December 29, 2011

Slide 18

Slide 18 text

Adding Functionality package Car; use Moose; has 'speed', is => 'ro', isa => 'Num', required => 1; sub go { my $self = shift; print "Vroom Vroom [speed: ", $self->speed, "]\n"; } package main; my $c = Car->new(speed => 10); $c->go; A Method takes the object as its first argument Calling is performed automatically by perl Thursday, December 29, 2011

Slide 19

Slide 19 text

Moose Attributes ✦ Use the ‘has’ function to declare an attribute ✦ has takes attribute name and a list of options ✦ Can use parens or no parens. Usually use parens with multi line option lists Thursday, December 29, 2011

Slide 20

Slide 20 text

Attribute Options ✦ is, reader, writer ✦ isa ✦ required, default, builder ✦ lazy Thursday, December 29, 2011

Slide 21

Slide 21 text

Readers & Writers ✦ Use ‘is’ to auto generate reader/writer ✦ Use optional ‘writer’ to specify writer name ✦ Use optional ‘reader’ to specify reader name package Product; use Moose; has 'name' => ( is => 'rw', reader => 'get_name', writer => '_set_name', ); has 'price' => ( is => 'rw', reader => 'get_price', writer => 'set_price', ); Thursday, December 29, 2011

Slide 22

Slide 22 text

isa ✦ Use isa to force a type constraint ✦ Available types: Bool, Str, Num, Int, ScalarRef, ArrayRef, HashRef, CodeRef ✦ Can use another object as type constraint Thursday, December 29, 2011

Slide 23

Slide 23 text

isa package Store; use Moose; has 'owner', is => 'ro', isa => 'Str'; has 'clients', is => 'rw', isa => 'ArrayRef[Client]'; has 'products', is => 'rw', isa => 'ArrayRef[Product]'; has 'revenue', is => 'rw', isa => 'Num'; 1; Thursday, December 29, 2011

Slide 24

Slide 24 text

Subtypes ✦ It’s easy to create a subtype and use that as extra constraints use Moose::Util::TypeConstraints; subtype 'age', as 'Int', where { $_ >= 0 && $_ <= 120 }, message { "Invalid Age: $_ "}; Thursday, December 29, 2011

Slide 25

Slide 25 text

Enumerations ✦ Use enum function to declare an enum subtype ✦ An enum can take a single value from a predefined list enum 'EyeColor', [qw/green blue brown gray/]; Thursday, December 29, 2011

Slide 26

Slide 26 text

Required / Default ✦ Use required for a field that takes its value from “outside” ✦ Use default for everything else ✦ Prefer required over default Thursday, December 29, 2011

Slide 27

Slide 27 text

Builder ✦ Use builder for attributes that need some special build logic ✦ A builder takes a method name as its value package Person; use Moose; has 'pet', is => 'ro', builder => '_build_pet'; has 'age', is => 'rw', required => 1; sub _build_pet { my $self = shift; if ( $self->age < 13 ) { return "None"; } else { return "Dog"; } } package main; my $p = Person->new(age => 10); print $p->pet; Thursday, December 29, 2011

Slide 28

Slide 28 text

Lazy ✦ Lazy load an attribute ✦ If that attribute has a builder, it will get called the first time the attribute is used. ✦ Lazy is highly recommended package Person; use Moose; has 'pet', is => 'ro', lazy_build => 1; has 'age', is => 'rw', required => 1; sub _build_pet { my $self = shift; if ( $self->age < 13 ) { return "None"; } else { return "Dog"; } } package main; my $p = Person->new(age => 10); print $p->pet; Thursday, December 29, 2011

Slide 29

Slide 29 text

Exercise 1 ✦ Implement a Logger class with the methods: ✦ info ✦ warning ✦ error ✦ Logger should print a message to the screen on each method Thursday, December 29, 2011

Slide 30

Slide 30 text

Exercise 2 ✦ Create a File and Screen classes which can write to a file or the screen ✦ Use them as log destinations, for example: my $fw = FileWriter->new(file => ‘app.log’); my $sw = Screen->new; my $logger = Logger->new(writers => [$fw, $sw]); Thursday, December 29, 2011

Slide 31

Slide 31 text

Q & A ✦ Objects ✦ Moose Classes ✦ Methods ✦ Attributes Thursday, December 29, 2011

Slide 32

Slide 32 text

Roles ✦ Something a class does ✦ Can be shared between classes ✦ Everything in a role is added directly to each class that “consumes” it Thursday, December 29, 2011

Slide 33

Slide 33 text

Classes & Roles Person Alive Think Computer Think Chicken Alive Thursday, December 29, 2011

Slide 34

Slide 34 text

Roles Example package Breakable; use Moose::Role; has 'is_broken', is => 'rw', isa => 'Bool'; sub break { my $self = shift; print "Ouch\n" if ! $self->is_broken; $self->is_broken(1); } sub fix { my $self = shift; print "Works now\n" if $self->is_broken; $self->is_broken(0); } package Glass; use Moose; with 'Breakable'; package main; my $g = Glass->new; $g->break; $g->fix; Thursday, December 29, 2011

Slide 35

Slide 35 text

Moose Roles ✦ Use Moose::Role to define a role ✦ Use ‘with’ to consume a role ✦ Inside a role, define methods and attributes ✦ Each class has a method ‘does’ that states if it consumes a given role Thursday, December 29, 2011

Slide 36

Slide 36 text

Partial Implementations ✦ A role can define “partial implementation” ✦ When a class consumes the role, it must provide full implementations of the missing methods package Writer; use Moose::Role; requires 'write'; package ScreenWriter; use Moose; with 'Writer'; sub write { print @_; } Thursday, December 29, 2011

Slide 37

Slide 37 text

Partial Implementations ✦ This works well with Moose type systems ✦ Can use a role as a type constraint ✦ Now only classes which consume the Writer role are allowed package Logger; use Moose; has 'writer' => ( is => 'ro', does => 'Writer', required => 1, ); package main; my $w = ScreenWriter->new; my $l = Logger->new(writer => $w); Thursday, December 29, 2011

Slide 38

Slide 38 text

Exercise ✦ Implement a program that takes a list of numbers and detects if the list forms a series ✦ Use scenario on the right package main; use SeriesDetector; use SeriesDetector::Arithmetic; my $arith = SeriesDetector::Arithmetic- >new; my $detector = SeriesDetector->new(plugins => [$arith]); # success - returns 'Arithmetic' $detector->check(qw/1 2 3 4/); $detector->check(qw/10 20 30/); #fail - returns undef $detector->check(qw/5 7 11); Thursday, December 29, 2011

Slide 39

Slide 39 text

Moose Tips http://www.flickr.com/photos/23054755@N00/149731833/ Thursday, December 29, 2011

Slide 40

Slide 40 text

Always Use default, required or builder Thursday, December 29, 2011

Slide 41

Slide 41 text

Defaults package Person; use Moose; has 'name', is => 'ro', required => 1; package Person; use Moose; has 'name', is => 'ro'; Thursday, December 29, 2011

Slide 42

Slide 42 text

Never Override new Use the simple attribute builders or BUILD method for that functionality Thursday, December 29, 2011

Slide 43

Slide 43 text

Be Lazy Use lazy_build or required on attributes by default. Everything else needs a good reason to use Thursday, December 29, 2011

Slide 44

Slide 44 text

Be Smart About Namespaces An Application::Logger is better than Logger A MyApp::Types::Employee is better than Employee Thursday, December 29, 2011

Slide 45

Slide 45 text

Prefer Delegation over Inheritance Moose is very kind with delegation and provides a good “has-a” syntax. Inheritance is harder to understand and does not save code Thursday, December 29, 2011

Slide 46

Slide 46 text

Q & A ✦ Objects ✦ Moose Classes ✦ Roles Thursday, December 29, 2011

Slide 47

Slide 47 text

Advanced Moose Delegation Method Modifiers Thursday, December 29, 2011

Slide 48

Slide 48 text

Delegation ✦ A relationship between objects where one object is an attribute of another object ✦ When that happens, it’s possible to redirect all calls on containing object to the attribute Thursday, December 29, 2011

Slide 49

Slide 49 text

Delegation Contact Email Send Mail Send Mail Phone Call Call Thursday, December 29, 2011

Slide 50

Slide 50 text

Delegation ✦ Moose handles delegation automatically ✦ Simply declare “delegates” on an attribute ✦ Moose will create the proxy methods for you package Contact; use Moose; has 'email' => ( is => 'ro', handles => [ qw/send_mail/ ] ); Thursday, December 29, 2011

Slide 51

Slide 51 text

Delegation ✦ You Can also delegate based on regular expressions ✦ Each method matches the regexp will be delegated to the attribute object has 'uri' => ( is => 'ro', isa => 'URI', handles => qr/^query.*)/, ); Thursday, December 29, 2011

Slide 52

Slide 52 text

Delegation ✦ You can also create delegations based on roles ✦ handles will automatically create methods for each method in the role has 'uri' => ( is => 'ro', isa => 'URI', handles => 'HasURI', ); Thursday, December 29, 2011

Slide 53

Slide 53 text

Native Delegation ✦ Native delegation gives your object a “native” feel by creating proxy methods that just work for native data types ✦ Currently supported: Array, Hash, Number, String, Bool, Counter ✦ Useful when you need an object to “work like an array” Thursday, December 29, 2011

Slide 54

Slide 54 text

Native Delegation ✦ Native arrays have push, pop, shift and unshift ✦ Here we take push and shift, and build proxy methods for them automatically by moose ✦ Can use $q->add_item and $q->next_item has 'q' => ( is => 'ro', isa => 'ArrayRef[Int]', default => sub { [] }, traits => [qw/Array/], handles => { add_item => 'push', next_item => 'shift', }, ); package main; my $q = Queue->new; $q->add_item(10, 20); Thursday, December 29, 2011

Slide 55

Slide 55 text

Native Delegation ✦ Available array functions include: push, pop, shift, unshift map, join, grep, reduce, sort, shuffle get, insert, count, is_empty ✦ Full list: Moose::Meta::Attribute::Native::Trait::Array Thursday, December 29, 2011

Slide 56

Slide 56 text

Native Delegation ✦ Use native delegation when you need to “borrow” native behavior ✦ A light switch (on/off) can use Bool traits ✦ A word frequency calculator (see example) Thursday, December 29, 2011

Slide 57

Slide 57 text

Lab ✦ Write Moose classes for painting: Canvas, Circle and ColoredCircle ✦ There can be multiple circles on a canvas, some of them may be colored. ✦ A ColoredCircle is a Circle with an extra color attribute ✦ See delegation/lab_starter.pl for usage Thursday, December 29, 2011

Slide 58

Slide 58 text

Method Modifiers ✦ A form of code reuse which alters a method by injecting code before or after it is executed ✦ Can modify method from roles, or even from the class itself ✦ Works best in mixins Thursday, December 29, 2011

Slide 59

Slide 59 text

after ✦ Used to add code after a method is executed ✦ Takes two arguments: method name and the code to add package Secret; use Mouse; has 'message', is => 'ro', required => 1, clearer => 'reset'; has 'counter', is => 'rw', default => 3; after 'message' => sub { my $self = shift; $self->counter( $self->counter - 1 ); if ( $self->counter <= 0 ) { $self->reset; } }; package main; my $secret = Secret->new( message => 'This message will self destroy'); print $secret->message, "\n" for (1..5); Thursday, December 29, 2011

Slide 60

Slide 60 text

before ✦ The before modifier lets us inject code before a method is executed ✦ The program on the right will log everything to standard error ✦ Can you spot the bug ? package Logger; use Mouse; sub log { my $self = shift; my ($msg) = @_; print $msg; } before 'log' => sub { select *STDERR }; after 'log' => sub { select *STDOUT }; package main; my $log = Logger->new; $log->log("hello\n"); Thursday, December 29, 2011

Slide 61

Slide 61 text

around ✦ Both before and after ignore return values from the code ✦ around modifier is far more powerful ✦ With around, you can manipulate function arguments and control the invocation of the original function Thursday, December 29, 2011

Slide 62

Slide 62 text

around ✦ The first parameter sent to around is the original method as a CODE ref ✦ The object ✦ around code must call: $self->$orig(@_) inside the wrapping (otherwise original method won’t get called) package AroundExample; use Mouse; use feature ':5.10'; sub foo { print "In Foo\n" } around 'foo' => sub { my $orig = shift; my $self = shift; say "Around: before calling method"; $self->$orig(@_); say "Around: after calling method"; }; Thursday, December 29, 2011

Slide 63

Slide 63 text

around ✦ Can use to control if a method is even called ✦ decision is based on environmental conditions package User; use Mouse; use DateTime; sub login { warn 'Welcome' } around 'login' => sub { my $now = DateTime->now; if ( $now->hour < 12 ) { my $orig = shift; my $self = shift; $self->$orig(@_); } }; Thursday, December 29, 2011

Slide 64

Slide 64 text

around ✦ Can use to log subroutine calls automatically ✦ Be careful with this one not to wrap Moose’s functions ✦ Remember to call original function package Test; use Mouse; sub foo { warn 'In foo' } sub bar { warn 'In bar' } sub buz { warn 'In buz' } around qr/^(foo|bar|buz)$/ => sub { warn 'Before call'; my $orig = shift; my $self = shift; $self->$orig(@_); warn 'After call'; }; Thursday, December 29, 2011

Slide 65

Slide 65 text

Method Modifiers ✦ around, before and after can modify existing methods behavior ✦ Use before and after to add some code “on top of” existing functionality ✦ Use around to completely alter functionality ✦ Q & A Thursday, December 29, 2011

Slide 66

Slide 66 text

Modifiers Lab ✦ Write a Document class that represents text in a file. Provide get($line), set ($line, $content), save() ✦ Write a PersistentDocument class that saves the file after each change ✦ Use the starter on the right my $d = Document->new('file1.txt'); my $p = PersistentDocument->new ('file2.txt'); # This one does not change file1.txt $d->set_line(2, 'This is the new line 2'); # This one changes file2.txt $p->set_line(2, 'This is the new line 2'); # Finally, this one changes file1.txt $d->save; Thursday, December 29, 2011

Slide 67

Slide 67 text

Thank You ✦ Ynon Perek ✦ ynonperek.com ✦ [email protected] ✦ This keynote is distributed under CC-BY-NC. License Details: http://creativecommons.org/licenses/by-nc/3.0/ Thursday, December 29, 2011