diff --git a/sections/advanced_oo.pod b/sections/advanced_oo.pod index c4d7ae02..c8583e4c 100644 --- a/sections/advanced_oo.pod +++ b/sections/advanced_oo.pod @@ -21,7 +21,7 @@ with code declared elsewhere? X X -Inheritance is one tool. It's not the only tool. Although C may extend +Inheritance is but one of many tools. Although C may extend C (an I), it's likely better for C to I contain several C objects as instance attributes (a I). @@ -35,15 +35,14 @@ entities are easier to understand, test, and maintain. X -When you design your object system, model the problem in terms of -responsibilities--the behavior each entity must provide. For example, an -C object may represent specific information about a person's name, -contact information, and other personal data, while a C object may -represent business responsibilities. Separating these entities in terms of -their responsibilities allows the C class to consider only the -problem of managing information specific to who the person is and the C -class to represent what the person does. (Two Cs may have a -C-sharing arrangement, for example.) +When you design your object system, consider the responsibilities of each +entity. For example, an C object may represent specific information +about a person's name, contact information, and other personal data, while a +C object may represent business responsibilities. Separating these +entities in terms of their responsibilities allows the C class to +consider only the problem of managing information specific to who the person is +and the C class to represent what the person does. (Two Cs may +have a C-sharing arrangement, for example.) When each class has a single responsibility, you improve the encapsulation of class-specific data and behaviors and reduce coupling between classes. diff --git a/sections/blessed_references.pod b/sections/blessed_references.pod index 5fbceb78..7c53ccc8 100644 --- a/sections/blessed_references.pod +++ b/sections/blessed_references.pod @@ -72,9 +72,9 @@ references are most common, but you can bless any type of reference: =end programlisting -Whereas classes built with Moose define their own object attributes -declaratively, Perl 5's default OO is lax. A class representing basketball -players which stores jersey number and position might use a constructor like: +Moose classes define object attributes declaratively, but Perl 5's default OO +is lax. A class representing basketball players which stores jersey number and +position might use a constructor like: =begin programlisting @@ -83,7 +83,6 @@ players which stores jersey number and position might use a constructor like: sub new { my ($class, %attrs) = @_; - bless \%attrs, $class; } } @@ -94,33 +93,29 @@ players which stores jersey number and position might use a constructor like: =begin programlisting - my $joel = Player->new( - number => 10, - position => 'center', - ); + my $joel = Player->new( number => 10, + position => 'center' ); - my $dante = Player->new( - number => 33, - position => 'forward', - ); + my $dante = Player->new( number => 33, + position => 'forward' ); =end programlisting -The object's methods can access hash elements directly: +The class's methods can access object attributes as hash elements directly: =begin programlisting sub format { my $self = shift; - return '#' . $self->{number} . ' plays ' . $self->{position}; + return '#' . $self->{number} + . ' plays ' . $self->{position}; } =end programlisting -... but so can any other code. If external code violates attribute -encapsulation, you can never change the object's internal representation -without breaking external code. Accessor methods are safer: +... but so can any other code, so any change to the object's internal +representation may break other code. Accessor methods are safer: =begin programlisting diff --git a/sections/chapter_07.pod b/sections/chapter_07.pod index 25554ab8..7382ca29 100644 --- a/sections/chapter_07.pod +++ b/sections/chapter_07.pod @@ -5,11 +5,15 @@ you must manage. Our only hope to manage this complexity is to exploit abstraction (treating similar things similarly) and encapsulation (grouping related details together). +X +X +X +X + Functions alone are insufficient for large problems. Several techniques group -functions into units of related behaviors--one popular technique is object -orientation. Perl 5's default object system is flexible, but minimal. You can -build great things on top of it, but it provides little assistance for some -basic tasks. +functions into units of related behaviors. One popular technique is I (OO), or I (OOP), where programs work +with I--discrete, unique entities with their own identities. L diff --git a/sections/moose.pod b/sections/moose.pod index bce9f10b..de77c6c4 100644 --- a/sections/moose.pod +++ b/sections/moose.pod @@ -2,33 +2,24 @@ Z -X -X -X -X - -In I (OO), or I (OOP), -programs work with I--discrete, unique entities with their own -identities. - X +Perl 5's default object system is flexible, but minimal. You can build great +things on top of it, but it provides little assistance for some basic tasks. I is a complete object system for Perl 5N for more information.>. It provides simpler defaults, and advanced features borrowed from languages such as Smalltalk, Common Lisp, and Perl 6. Moose code interoperates with the default object system and is currently the best way to write object oriented code in modern Perl 5. -A Moose object is a concrete instance of a I, which is a template -describing data and behavior specific to the object. - =head2 Classes X X> -If an object is a concrete thing, its class is its abstract definition. Perl 5 -classes use packages (L) to provide namespaces: +A Moose object is a concrete instance of a I, which is a template +describing data and behavior specific to the object. Classes use packages +(L) to provide namespaces: =begin programlisting @@ -137,15 +128,12 @@ rightly uses instance methods, as they have access to instance data. X X X - -Every object in Perl 5 is unique. Objects can contain private data associated -with each unique object--these are I, I, or object -I. - X X (read only)> -Define an attribute by declaring it as part of the class: +Every object in Perl 5 is unique. Objects can contain private data associated +with each unique object--these are I, I, or object +I. Define an attribute by declaring it as part of the class: =begin programlisting @@ -170,14 +158,11 @@ that the value of this attribute can only be a Cing. X X -As a result of C, Moose creates an I method named C, -from which you can retrieve an instance's name, and allows you to pass a -C parameter to C's constructor: +As a result of C, Moose creates an I method named C and +allows you to pass a C parameter to C's constructor: =begin programlisting - use Cat; - for my $name (qw( Tuxie Petunia Daisy )) { my $cat = Cat->new( name => $name ); @@ -186,31 +171,9 @@ C parameter to C's constructor: =end programlisting -X - -If you pass a non-string, Moose will complain. Attributes do not I to -have types. In that case, anything goes: - -=begin programlisting - - package Cat - { - use Moose; - - has 'name', is => 'ro', isa => 'Str'; - B<< has 'age', is => 'ro'; >> - } - - my $invalid = Cat->new( name => 'bizarre', age => 'purple' ); - -=end programlisting - -Specifying a type allows Moose to perform some data validations for you. -Sometimes this strictness is invaluable. - =begin sidebar -The Moose documentation uses parentheses to separate an attribute name from its +Moose's documentation uses parentheses to separate attribute names and characteristics: =begin programlisting @@ -219,17 +182,15 @@ characteristics: =end programlisting -The form used in this book is equivalent, as are: +This is equivalent to: =begin programlisting has( 'name', 'is', 'ro', 'isa', 'Str' ); - has( qw( name is ro isa Str ) ); =end programlisting -Choose the punctuation which offers you the most clarity. Moose's approach -works nicely for complex declarations: +Moose's approach works nicely for complex declarations: =begin programlisting @@ -245,9 +206,33 @@ works nicely for complex declarations: =end programlisting ... while this book prefers a low-punctuation approach for simple declarations. +Choose the punctuation which offers you the most clarity. =end sidebar +X + +Moose will complain if you pass something which isn't a string. Attributes do +not I to have types. In that case, anything goes: + +=begin programlisting + + package Cat + { + use Moose; + + has 'name', is => 'ro', isa => 'Str'; + B<< has 'age', is => 'ro'; >> + } + + my $invalid = Cat->new( name => 'bizarre', + age => 'purple' ); + +=end programlisting + +Specifying a type allows Moose to perform some data validations for you. +Sometimes this strictness is invaluable. + X (read-write)> X X @@ -266,7 +251,10 @@ Moose will create a I method which can change that attribute's value: B<< has 'diet', is => 'rw'; >> } - my $fat = Cat->new( name => 'Fatty', age => 8, diet => 'Sea Treats' ); + my $fat = Cat->new( name => 'Fatty', + age => 8, + diet => 'Sea Treats' ); + say $fat->name(), ' eats ', $fat->diet(); B<< $fat->diet( 'Low Sodium Kitty Lo Mein' ); >> @@ -296,10 +284,10 @@ X Moose allows you to declare I attributes class instances possess (a cat has a name) as well as the attributes of those attributes (you cannot change a -cat's name). Moose itself decides how to I those attributes. You can -change that if you like, but allowing Moose to manage your storage encourages -I: hiding the internal details of an object from external users -of that object. +cat's name; you can only read it). Moose itself decides how to I those +attributes. You can change that if you like, but allowing Moose to manage your +storage encourages I: hiding the internal details of an object +from external users of that object. Consider a change to how Cs manage their ages. Instead of passing a value for an age to the constructor, pass in the year of the cat's birth and @@ -350,10 +338,13 @@ birth year: { use Moose; - has 'name', is => 'ro', isa => 'Str'; - has 'diet', is => 'rw', isa => 'Str'; - B<< has 'birth_year', is => 'ro', isa => 'Int', >> - B<< default => sub { (localtime)[5] + 1900 }; >> + has 'name', is => 'ro', isa => 'Str'; + has 'diet', is => 'rw', isa => 'Str'; + + B<< has 'birth_year', >> + B<< is => 'ro', >> + B<< isa => 'Int', >> + B<< default => sub { (localtime)[5] + 1900 }; >> } =end programlisting @@ -461,8 +452,7 @@ in one class but not in another: =end programlisting -Sometimes it's useful to know I an object does. For that, you must -understand its type. +Sometimes it's useful to know I an object does and what that I. =head2 Roles @@ -473,9 +463,8 @@ A I is a named collection of behavior and stateN and research on Smalltalk traits at U for copious details.>. While a class organizes behaviors and state into a template for -objects, a role organizes a named collection of behaviors and state. - -You can instantiate a class, but not a role. A role is something a class does. +objects, a role organizes a named collection of behaviors and state. You can +instantiate a class, but not a role. A role is something a class does. Given an C which has an age and a C which can age, one difference may be that C does the C role, while the @@ -501,10 +490,13 @@ C methods. The C class must explicitly mark that it does the role: { use Moose; - has 'name', is => 'ro', isa => 'Str'; - has 'diet', is => 'rw', isa => 'Str'; - has 'birth_year', is => 'ro', isa => 'Int', - default => sub { (localtime)[5] + 1900 }; + has 'name', is => 'ro', isa => 'Str'; + has 'diet', is => 'rw', isa => 'Str'; + + has 'birth_year', + is => 'ro', + isa => 'Int', + default => sub { (localtime)[5] + 1900 }; B @@ -519,10 +511,8 @@ The C line causes Moose to I the C role into the C class. Composition ensures all of the attributes and methods of the role part of the class. C requires any composing class to provide methods named C, C, and C. C satisfies these -constraints. - -If C were composed into a class which did not provide those -methods, Moose would throw an exception. +constraints. If C were composed into a class which did not provide +those methods, Moose would throw an exception. =begin tip Order Matters! @@ -543,9 +533,8 @@ the C role. C objects should not: =end programlisting This design technique separates the I of classes and objects from -the I of those classes and objects. The special behavior of the -C class, where it stores the birth year of the animal and calculates the -age directly, could itself be a role: +the I of those classes and objects. The birth year calculation +behavior of the C class could itself be a role: =begin programlisting @@ -553,9 +542,10 @@ age directly, could itself be a role: { use Moose::Role; - has 'birth_year', is => 'ro', - isa => 'Int', - default => sub { (localtime)[5] + 1900 }; + has 'birth_year', + is => 'ro', + isa => 'Int', + default => sub { (localtime)[5] + 1900 }; sub age { @@ -580,7 +570,8 @@ classes. Now C can compose both roles: has 'name', is => 'ro', isa => 'Str'; has 'diet', is => 'rw'; - B + B + B<'CalculateAge::From::BirthYear';> } =end programlisting @@ -623,7 +614,8 @@ true value when you call C on them: =begin programlisting - say 'This Cat is alive!' if $kitten->DOES( 'LivingBeing' ); + say 'This Cat is alive!' + if $kitten->DOES( 'LivingBeing' ); =end programlisting @@ -655,7 +647,7 @@ behavior a meaningful name. =end tip Consider a C class which provides two public attributes -(C and C) and two methods (C and C): +(C and C) and two methods (C and C): =begin programlisting @@ -663,10 +655,14 @@ Consider a C class which provides two public attributes { use Moose; - has 'candle_power', is => 'ro', isa => 'Int', + has 'candle_power', is => 'ro', + isa => 'Int', default => 1; - has 'enabled', is => 'ro', isa => 'Bool', - default => 0, writer => '_set_enabled'; + + has 'enabled', is => 'ro', + isa => 'Bool', + default => 0, + writer => '_set_enabled'; sub light { @@ -724,7 +720,7 @@ X Attribute inheritance works similarly (see C). -=begin sidebar +=head3 Method Dispatch Order X X @@ -737,9 +733,9 @@ X I (or I or I) is obvious for single-parent classes. Look in the object's class, then its parent, and so on until you find the method or run out of parents. Classes which inherit from -multiple parents (I)--suppose your C extends -both C and C--require trickier dispatch. Reasoning about multiple -inheritance is complex. Avoid multiple inheritance when possible. +multiple parents (I)--C extends both C +and C--require trickier dispatch. Reasoning about multiple inheritance is +complex. Avoid multiple inheritance when possible. Perl 5 uses a depth-first method resolution strategy. It searches the class of the I named parent and all of that parent's parents recursively before @@ -749,8 +745,6 @@ given class's immediate parents before searching any of their parents. See C for more details. -=end sidebar - =head3 Inheritance and Methods X @@ -784,7 +778,7 @@ overridden method: package LightSource::Cranky { - use Carp; + use Carp 'carp'; use Moose; extends 'LightSource'; @@ -793,7 +787,7 @@ overridden method: { my $self = shift; - Carp::carp( "Can't light a lit light source!" ) + carp "Can't light a lit light source!" if $self->enabled; B; @@ -803,7 +797,7 @@ overridden method: { my $self = shift; - Carp::carp( "Can't extinguish an unlit light source!" ) + carp "Can't extinguish unlit light source!" unless $self->enabled; B; @@ -834,8 +828,11 @@ class. That invocant may be the name of a class or an instance of an object: =begin programlisting - say 'Looks like a LightSource' if $sconce->isa( 'LightSource' ); - say 'Hominidae do not glow' unless $chimpy->isa( 'LightSource' ); + say 'Looks like a LightSource' + if $sconce->isa( 'LightSource' ); + + say 'Hominidae do not glow' + unless $chimpy->isa( 'LightSource' ); =end programlisting @@ -874,7 +871,8 @@ object or which attributes an object supports, this information is available: say 'Monkey::Pants instances support the methods:'; - say $_->fully_qualified_name for $metaclass->get_all_methods; + say $_->fully_qualified_name + for $metaclass->get_all_methods; =end programlisting @@ -905,16 +903,20 @@ This is valid Perl 5 code: B CalculateAge::From::BirthYear { - has 'birth_year', is => 'ro', isa => 'Int', - default => sub { (localtime)[5] + 1900 }; + has 'birth_year', + is => 'ro', + isa => 'Int', + default => sub { (localtime)[5] + 1900 }; B age { - return (localtime)[5] + 1900 - $self->birth_year(); + return (localtime)[5] + 1900 + - $self->birth_year(); } } - B + B + B { has 'name', is => 'ro', isa => 'Str'; has 'diet', is => 'rw'; @@ -925,11 +927,11 @@ This is valid Perl 5 code: X> X> -The C CPAN distribution uses the CPAN distribution -C to add new Moose-specific syntax. The C, C, and -C keywords reduce the amount of boilerplate necessary to write good -object oriented code in Perl 5. Note specifically the declarative nature of -this example, as well as the lack of C in C. +The C CPAN distribution uses C to add new +Moose-specific syntax. The C, C, and C keywords reduce the +amount of boilerplate necessary to write good object oriented code in Perl 5. +Note specifically the declarative nature of this example, as well as the lack +of C in C. While Moose is not a part of the Perl 5 core, its popularity ensures that it's available on many OS distributions. Perl 5 distributions such as Strawberry diff --git a/sections/reflection.pod b/sections/reflection.pod index cd9de547..b7d4c86f 100644 --- a/sections/reflection.pod +++ b/sections/reflection.pod @@ -17,30 +17,6 @@ object systems. If you use Moose, its metaprogramming system will help you. If not, several other core Perl 5 idioms help you inspect and manipulate running programs. -=head2 Checking that a Package Exists - -To check that a package exists somewhere in your program--if some code -somewhere has executed a C directive with a given name--check that the -package inherits from C. Anything which extends C must -somehow provide the C method. If no such package exists, Perl will throw -an exception about an invalid invocant, so wrap this call in an C block: - -=begin programlisting - - say "$pkg exists" if eval { $pkg->can( 'can' ) }; - -=end programlisting - -An alternate approach is groveling through Perl's symbol tables. - -=head2 Checking that a Class Exists - -Because Perl 5 makes no strong distinction between packages and classes, the -best you can do without Moose is to check that a package of the expected class -name exists. You I check that the package C provide C, but -there is no guarantee that any C found is either a method or a -constructor. - =head2 Checking that a Module Has Loaded X> @@ -54,7 +30,7 @@ module. In other words, loading C effectively does: =begin programlisting $INC{'Modern/Perl.pm'} = - '/path/to/perl/lib/site_perl/5.12.1/Modern/Perl.pm'; + '.../lib/site_perl/5.12.1/Modern/Perl.pm'; =end programlisting @@ -86,10 +62,35 @@ X> The C CPAN module's C function encapsulates this C<%INC> check. +=head2 Checking that a Package Exists + +To check that a package exists somewhere in your program--if some code +somewhere has executed a C directive with a given name--check that the +package inherits from C. Anything which extends C must +somehow provide the C method. If no such package exists, Perl will throw +an exception about an invalid invocant, so wrap this call in an C block: + +=begin programlisting + + say "$pkg exists" if eval { $pkg->can( 'can' ) }; + +=end programlisting + +An alternate approach is groveling through Perl's symbol tables. + +=head2 Checking that a Class Exists + +Because Perl 5 makes no strong distinction between packages and classes, the +best you can do without Moose is to check that a package of the expected class +name exists. You I check that the package C provide C, but +there is no guarantee that any C found is either a method or a +constructor. + =head2 Checking a Module Version Number -Modules do not have to provide version numbers, but all package extend -C (L) and respond to its C method: +Modules do not have to provide version numbers, but every package inherits the +C method from the universal parent class C +(L): =begin programlisting