Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Moose OO

ynonperek
December 12, 2011

Moose OO

Introducing perl Object Oriented code using Moose

ynonperek

December 12, 2011
Tweet

More Decks by ynonperek

Other Decks in Programming

Transcript

  1. Agenda ✦ OO Intro ✦ Meet The Moose ✦ Classes,

    Methods, Attributes ✦ Roles ✦ Best Practices Thursday, December 29, 2011
  2. 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
  3. 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
  4. 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
  5. ✦ 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
  6. Duck World ✦ Every duck can quack() and eat() ✦

    Each duck does it differently ✦ Interfaces Thursday, December 29, 2011
  7. Reasons To Use OO ✦ Code Reuse ✦ Easier Problem

    Domain Modeling ✦ Easy To Test ✦ Not polluting namespaces Thursday, December 29, 2011
  8. 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
  9. 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
  10. 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
  11. 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
  12. 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
  13. 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
  14. 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
  15. Attribute Options ✦ is, reader, writer ✦ isa ✦ required,

    default, builder ✦ lazy Thursday, December 29, 2011
  16. 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
  17. 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
  18. 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
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. Q & A ✦ Objects ✦ Moose Classes ✦ Methods

    ✦ Attributes Thursday, December 29, 2011
  27. 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
  28. 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
  29. 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
  30. 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
  31. 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
  32. 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
  33. Defaults package Person; use Moose; has 'name', is => 'ro',

    required => 1; package Person; use Moose; has 'name', is => 'ro'; Thursday, December 29, 2011
  34. Never Override new Use the simple attribute builders or BUILD

    method for that functionality Thursday, December 29, 2011
  35. Be Lazy Use lazy_build or required on attributes by default.

    Everything else needs a good reason to use Thursday, December 29, 2011
  36. Be Smart About Namespaces An Application::Logger is better than Logger

    A MyApp::Types::Employee is better than Employee Thursday, December 29, 2011
  37. 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
  38. Q & A ✦ Objects ✦ Moose Classes ✦ Roles

    Thursday, December 29, 2011
  39. 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
  40. 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
  41. 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
  42. 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
  43. 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
  44. 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
  45. 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
  46. 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
  47. 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
  48. 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
  49. 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
  50. 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
  51. 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
  52. 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
  53. 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
  54. 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
  55. 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
  56. 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
  57. 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