[ Team LiB ] |
Recipe 13.4 Managing Class Data13.4.1 ProblemYou need a method invoked on behalf of the whole class, not just on one object. This might be a procedural request, or it might be a global data attribute shared by all instances of the class. 13.4.2 SolutionInstead of expecting a reference as their first argument as object methods do, class methods expect a string containing the name of the class. Class methods access package data, not object data, as in the population method shown here: package Person;
$Body_Count = 0;
sub population { return $Body_Count }
sub new { # constructor
$Body_Count++;
return bless({ }, shift);
}
sub DESTROY { --$Body_Count } # destructor
# later, the user can say this:
package main;
for (1..10) { push @people, Person->new }
printf "There are %d people alive.\n", Person->population( );
There are 10 people alive.
13.4.3 DiscussionNormally, each object has its own complete state stored within itself. The value of a data attribute in one object is unrelated to the value that attribute might have in another instance of the same class. For example, setting her gender here does nothing to his gender, because they are different objects with distinct states: $him = Person->new( ); $him->gender("male"); $her = Person->new( ); $her->gender("female"); Imagine a classwide attribute where changing the attribute for one instance changes it for all of them. Just as some programmers prefer capitalized global variables, some prefer uppercase names when the method affects class data instead of instance data. Here's an example of using a class method named Max_Bounds: FixedArray->Max_Bounds(100); # set for whole class $alpha = FixedArray->new( ); printf "Bound on alpha is %d\n", $alpha->Max_Bounds( ); 100 $beta = FixedArray->new( ); $beta->Max_Bounds(50); # still sets for whole class printf "Bound on alpha is %d\n", $alpha->Max_Bounds( ); 50 The implementation is simple: package FixedArray; $Bounds = 7; # default sub new { bless( { }, shift ) } sub Max_Bounds { my $proto = shift; $Bounds = shift if @_; # allow updates return $Bounds; } To make the value effectively read-only, simply remove the update possibility, as in: sub Max_Bounds { $Bounds } If you're deeply paranoid, make $Bounds a lexical variable private to the scope of the file containing the class. Then no one could say $FixedArray::Bounds to discover its values. They'd be forced to go through the method interface instead. Here's a tip to help build scalable classes: store object data on the object's namespace (in the hash), and store class data in the class namespace (package variables or file-scoped lexicals). Only class methods should directly access classwide attributes. Object methods should access only instance data. If the object method needs access to class data, its constructor should store a reference to that data in the object. Here's an example: sub new { my $class = shift; my $self = bless({ }, $class); $self->{Max_Bounds_ref} = \$Bounds; return $self; } 13.4.4 See Alsoperltoot(1), perlboot(1), perlobj(1), and perlbot(1); the section on "Managing Class Data" in Chapter 12 of Programming Perl; Recipe 13.3; the places method in Recipe 13.14.5 in Recipe 13.14 |
[ Team LiB ] |