perltoot.pod

来自「MSYS在windows下模拟了一个类unix的终端」· POD 代码 · 共 1,734 行 · 第 1/5 页

POD
1,734
字号
    }    sub happy_birthday {        my $self = shift;        return $self->age( $self->age() + 1 );    }But since these methods are all executing in the class itself, thismay not be critical.  There are tradeoffs to be made.  Using directhash access is faster (about an order of magnitude faster, in fact), andit's more convenient when you want to interpolate in strings.  But usingmethods (the external interface) internally shields not just the users ofyour class but even you yourself from changes in your data representation.=head1 Class DataWhat about "class data", data items common to each object in a class?What would you want that for?  Well, in your Person class, you mightlike to keep track of the total people alive.  How do you implement that?You I<could> make it a global variable called $Person::Census.  But aboutonly reason you'd do that would be if you I<wanted> people to be able toget at your class data directly.  They could just say $Person::Censusand play around with it.  Maybe this is ok in your design scheme.You might even conceivably want to make it an exported variable.  To beexportable, a variable must be a (package) global.  If this were atraditional module rather than an object-oriented one, you might do that.While this approach is expected in most traditional modules, it'sgenerally considered rather poor form in most object modules.  In anobject module, you should set up a protective veil to separate interfacefrom implementation.  So provide a class method to access class datajust as you provide object methods to access object data.So, you I<could> still keep $Census as a package global and rely uponothers to honor the contract of the module and therefore not play aroundwith its implementation.  You could even be supertricky and make $Census atied object as described in L<perltie>, thereby intercepting all accesses.But more often than not, you just want to make your class data afile-scoped lexical.  To do so, simply put this at the top of the file:    my $Census = 0;Even though the scope of a my() normally expires when the block in whichit was declared is done (in this case the whole file being required orused), Perl's deep binding of lexical variables guarantees that thevariable will not be deallocated, remaining accessible to functionsdeclared within that scope.  This doesn't work with global variablesgiven temporary values via local(), though.Irrespective of whether you leave $Census a package global or makeit instead a file-scoped lexical, you should make thesechanges to your Person::new() constructor:    sub new {        my $proto = shift;        my $class = ref($proto) || $proto;        my $self  = {};        $Census++;        $self->{NAME}   = undef;        $self->{AGE}    = undef;        $self->{PEERS}  = [];        bless ($self, $class);        return $self;    }    sub population {        return $Census;    }Now that we've done this, we certainly do need a destructor so thatwhen Person is destroyed, the $Census goes down.  Here's howthis could be done:    sub DESTROY { --$Census }Notice how there's no memory to deallocate in the destructor?  That'ssomething that Perl takes care of for you all by itself.Alternatively, you could use the Class::Data::Inheritable module fromCPAN.=head2 Accessing Class DataIt turns out that this is not really a good way to go about handlingclass data.  A good scalable rule is that I<you must never reference classdata directly from an object method>.  Otherwise you aren't building ascalable, inheritable class.  The object must be the rendezvous pointfor all operations, especially from an object method.  The globals(class data) would in some sense be in the "wrong" package in yourderived classes.  In Perl, methods execute in the context of the classthey were defined in, I<not> that of the object that triggered them.Therefore, namespace visibility of package globals in methods is unrelatedto inheritance.Got that?  Maybe not.  Ok, let's say that some other class "borrowed"(well, inherited) the DESTROY method as it was defined above.  When thoseobjects are destroyed, the original $Census variable will be altered,not the one in the new class's package namespace.  Perhaps this is whatyou want, but probably it isn't.Here's how to fix this.  We'll store a reference to the data in thevalue accessed by the hash key "_CENSUS".  Why the underscore?  Well,mostly because an initial underscore already conveys strong feelingsof magicalness to a C programmer.  It's really just a mnemonic deviceto remind ourselves that this field is special and not to be used asa public data member in the same way that NAME, AGE, and PEERS are.(Because we've been developing this code under the strict pragma, priorto perl version 5.004 we'll have to quote the field name.)    sub new {        my $proto = shift;        my $class = ref($proto) || $proto;        my $self  = {};        $self->{NAME}     = undef;        $self->{AGE}      = undef;        $self->{PEERS}    = [];        # "private" data        $self->{"_CENSUS"} = \$Census;        bless ($self, $class);        ++ ${ $self->{"_CENSUS"} };        return $self;    }    sub population {        my $self = shift;        if (ref $self) {            return ${ $self->{"_CENSUS"} };        } else {            return $Census;        }    }    sub DESTROY {        my $self = shift;        -- ${ $self->{"_CENSUS"} };    }=head2 Debugging MethodsIt's common for a class to have a debugging mechanism.  For example,you might want to see when objects are created or destroyed.  To do that,add a debugging variable as a file-scoped lexical.  For this, we'll pullin the standard Carp module to emit our warnings and fatal messages.That way messages will come out with the caller's filename andline number instead of our own; if we wanted them to be from our ownperspective, we'd just use die() and warn() directly instead of croak()and carp() respectively.    use Carp;    my $Debugging = 0;Now add a new class method to access the variable.    sub debug {        my $class = shift;        if (ref $class)  { confess "Class method called as object method" }        unless (@_ == 1) { confess "usage: CLASSNAME->debug(level)" }        $Debugging = shift;    }Now fix up DESTROY to murmur a bit as the moribund object expires:    sub DESTROY {        my $self = shift;        if ($Debugging) { carp "Destroying $self " . $self->name }        -- ${ $self->{"_CENSUS"} };    }One could conceivably make a per-object debug state.  Thatway you could call both of these:    Person->debug(1);   # entire class    $him->debug(1);     # just this objectTo do so, we need our debugging method to be a "bimodal" one, one thatworks on both classes I<and> objects.  Therefore, adjust the debug()and DESTROY methods as follows:    sub debug {        my $self = shift;        confess "usage: thing->debug(level)"    unless @_ == 1;        my $level = shift;        if (ref($self))  {            $self->{"_DEBUG"} = $level;		# just myself        } else {            $Debugging        = $level;         # whole class        }    }    sub DESTROY {        my $self = shift;        if ($Debugging || $self->{"_DEBUG"}) {            carp "Destroying $self " . $self->name;        }        -- ${ $self->{"_CENSUS"} };    }What happens if a derived class (which we'll call Employee) inheritsmethods from this Person base class?  Then C<< Employee->debug() >>, when calledas a class method, manipulates $Person::Debugging not $Employee::Debugging.=head2 Class DestructorsThe object destructor handles the death of each distinct object.  But sometimesyou want a bit of cleanup when the entire class is shut down, whichcurrently only happens when the program exits.  To make such aI<class destructor>, create a function in that class's package namedEND.  This works just like the END function in traditional modules,meaning that it gets called whenever your program exits unless it execsor dies of an uncaught signal.  For example,    sub END {        if ($Debugging) {            print "All persons are going away now.\n";        }    }When the program exits, all the class destructors (END functions) arebe called in the opposite order that they were loaded in (LIFO order).=head2 Documenting the InterfaceAnd there you have it: we've just shown you the I<implementation> of thisPerson class.  Its I<interface> would be its documentation.  Usually thismeans putting it in pod ("plain old documentation") format right therein the same file.  In our Person example, we would place the followingdocs anywhere in the Person.pm file.  Even though it looks mostly likecode, it's not.  It's embedded documentation such as would be used bythe pod2man, pod2html, or pod2text programs.  The Perl compiler ignorespods entirely, just as the translators ignore code.  Here's an example ofsome pods describing the informal interface:    =head1 NAME    Person - class to implement people    =head1 SYNOPSIS     use Person;     #################     # class methods #     #################     $ob    = Person->new;     $count = Person->population;     #######################     # object data methods #     #######################     ### get versions ###         $who   = $ob->name;         $years = $ob->age;         @pals  = $ob->peers;     ### set versions ###         $ob->name("Jason");         $ob->age(23);         $ob->peers( "Norbert", "Rhys", "Phineas" );     ########################     # other object methods #     ########################     $phrase = $ob->exclaim;     $ob->happy_birthday;    =head1 DESCRIPTION    The Person class implements dah dee dah dee dah....That's all there is to the matter of interface versus implementation.A programmer who opens up the module and plays around with all the privatelittle shiny bits that were safely locked up behind the interface contracthas voided the warranty, and you shouldn't worry about their fate.=head1 AggregationSuppose you later want to change the class to implement better names.Perhaps you'd like to support both given names (called Christian names,irrespective of one's religion) and family names (called surnames), plusnicknames and titles.  If users of your Person class have been properlyaccessing it through its documented interface, then you can easily changethe underlying implementation.  If they haven't, then they lose andit's their fault for breaking the contract and voiding their warranty.To do this, we'll make another class, this one called Fullname.  What'sthe Fullname class look like?  To answer that question, you have tofirst figure out how you want to use it.  How about we use it this way:    $him = Person->new();    $him->fullname->title("St");    $him->fullname->christian("Thomas");    $him->fullname->surname("Aquinas");    $him->fullname->nickname("Tommy");    printf "His normal name is %s\n", $him->name;    printf "But his real name is %s\n", $him->fullname->as_string;Ok.  To do this, we'll change Person::new() so that it supportsa full name field this way:    sub new {        my $proto = shift;        my $class = ref($proto) || $proto;        my $self  = {};        $self->{FULLNAME} = Fullname->new();        $self->{AGE}      = undef;        $self->{PEERS}    = [];        $self->{"_CENSUS"} = \$Census;        bless ($self, $class);        ++ ${ $self->{"_CENSUS"} };        return $self;    }    sub fullname {        my $self = shift;        return $self->{FULLNAME};    }Then to support old code, define Person::name() this way:    sub name {        my $self = shift;        return $self->{FULLNAME}->nickname(@_)          ||   $self->{FULLNAME}->christian(@_);    }Here's the Fullname class.  We'll use the same techniqueof using a hash reference to hold data fields, and methodsby the appropriate name to access them:    package Fullname;    use strict;    sub new {        my $proto = shift;        my $class = ref($proto) || $proto;        my $self  = {            TITLE       => undef,            CHRISTIAN   => undef,            SURNAME     => undef,            NICK        => undef,        };        bless ($self, $class);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?