perltoot.pod

来自「视频监控网络部分的协议ddns,的模块的实现代码,请大家大胆指正.」· POD 代码 · 共 1,743 行 · 第 1/5 页

POD
1,743
字号
here rather than traditional module importation.)For example, here is the POSIX module's @ISA:    package POSIX;    @ISA = qw(Exporter DynaLoader);The POSIX module isn't really an object module, but then,neither are Exporter or DynaLoader.  They're just lending theirclasses' behaviours to POSIX.Why don't people use MI for object methods much?  One reason is thatit can have complicated side-effects.  For one thing, your inheritancegraph (no longer a tree) might converge back to the same base class.Although Perl guards against recursive inheritance, merely having parentswho are related to each other via a common ancestor, incestuous thoughit sounds, is not forbidden.  What if in our Third class shown above wewanted its new() method to also call both overridden constructors in itstwo parent classes?  The SUPER notation would only find the first one.Also, what about if the Alpha and Beta classes both had a common ancestor,like Nought?  If you kept climbing up the inheritance tree callingoverridden methods, you'd end up calling Nought::new() twice,which might well be a bad idea.=head2 UNIVERSAL: The Root of All ObjectsWouldn't it be convenient if all objects were rooted at some ultimatebase class?  That way you could give every object common methods withouthaving to go and add it to each and every @ISA.  Well, it turns out thatyou can.  You don't see it, but Perl tacitly and irrevocably assumesthat there's an extra element at the end of @ISA: the class UNIVERSAL.In version 5.003, there were no predefined methods there, but you could putwhatever you felt like into it.However, as of version 5.004 (or some subversive releases, like 5.003_08),UNIVERSAL has some methods in it already.  These are builtin to your Perlbinary, so they don't take any extra time to load.  Predefined methodsinclude isa(), can(), and VERSION().  isa() tells you whether an object orclass "is" another one without having to traverse the hierarchy yourself:   $has_io = $fd->isa("IO::Handle");   $itza_handle = IO::Socket->isa("IO::Handle");The can() method, called against that object or class, reports backwhether its string argument is a callable method name in that class.In fact, it gives you back a function reference to that method:   $his_print_method = $obj->can('as_string');Finally, the VERSION method checks whether the class (or the object'sclass) has a package global called $VERSION that's high enough, as in:    Some_Module->VERSION(3.0);    $his_vers = $ob->VERSION();However, we don't usually call VERSION ourselves.  (Remember that an alluppercase function name is a Perl convention that indicates that thefunction will be automatically used by Perl in some way.)  In this case,it happens when you say    use Some_Module 3.0;If you wanted to add version checking to your Person class explainedabove, just add this to Person.pm:    our $VERSION = '1.1';and then in Employee.pm you can say    use Person 1.1;And it would make sure that you have at least that version number orhigher available.   This is not the same as loading in that exact versionnumber.  No mechanism currently exists for concurrent installation ofmultiple versions of a module.  Lamentably.=head2 Deeper UNIVERSAL detailsIt is also valid (though perhaps unwise in most cases) to put otherpackages' names in @UNIVERSAL::ISA.  These packages will also beimplicitly inherited by all classes, just as UNIVERSAL itself is.However, neither UNIVERSAL nor any of its parents from the @ISA treeare explicit base classes of all objects.  To clarify, given thefollowing:    @UNIVERSAL::ISA = ('REALLYUNIVERSAL');    package REALLYUNIVERSAL;    sub special_method { return "123" }    package Foo;    sub normal_method { return "321" }Calling Foo->special_method() will return "123", but callingFoo->isa('REALLYUNIVERSAL') or Foo->isa('UNIVERSAL') will returnfalse.If your class is using an alternate mro like C3 (seeL<mro>), method resolution within UNIVERSAL / @UNIVERSAL::ISA willstill occur in the default depth-first left-to-right manner,after the class's C3 mro is exhausted.All of the above is made more intuitive by realizing what reallyhappens during method lookup, which is roughly like thisugly pseudo-code:    get_mro(class) {        # recurses down the @ISA's starting at class,        # builds a single linear array of all        # classes to search in the appropriate order.        # The method resolution order (mro) to use        # for the ordering is whichever mro "class"        # has set on it (either default (depth first        # l-to-r) or C3 ordering).        # The first entry in the list is the class        # itself.    }    find_method(class, methname) {        foreach $class (get_mro(class)) {            if($class->has_method(methname)) {                return ref_to($class->$methname);            }        }        foreach $class (get_mro(UNIVERSAL)) {            if($class->has_method(methname)) {                return ref_to($class->$methname);            }        }        return undef;    }However the code that implements UNIVERSAL::isa does notsearch in UNIVERSAL itself, only in the package's actual@ISA.=head1 Alternate Object RepresentationsNothing requires objects to be implemented as hash references.  An objectcan be any sort of reference so long as its referent has been suitablyblessed.  That means scalar, array, and code references are also fairgame.A scalar would work if the object has only one datum to hold.  An arraywould work for most cases, but makes inheritance a bit dodgy becauseyou have to invent new indices for the derived classes.=head2 Arrays as ObjectsIf the user of your class honors the contract and sticks to the advertisedinterface, then you can change its underlying interface if you feellike it.  Here's another implementation that conforms to the sameinterface specification.  This time we'll use an array referenceinstead of a hash reference to represent the object.    package Person;    use strict;    my($NAME, $AGE, $PEERS) = ( 0 .. 2 );    ############################################    ## the object constructor (array version) ##    ############################################    sub new {        my $self = [];        $self->[$NAME]   = undef;  # this is unnecessary        $self->[$AGE]    = undef;  # as is this        $self->[$PEERS]  = [];     # but this isn't, really        bless($self);        return $self;    }    sub name {        my $self = shift;        if (@_) { $self->[$NAME] = shift }        return $self->[$NAME];    }    sub age {        my $self = shift;        if (@_) { $self->[$AGE] = shift }        return $self->[$AGE];    }    sub peers {        my $self = shift;        if (@_) { @{ $self->[$PEERS] } = @_ }        return @{ $self->[$PEERS] };    }    1;  # so the require or use succeedsYou might guess that the array access would be a lot faster than thehash access, but they're actually comparable.  The array is a I<little>bit faster, but not more than ten or fifteen percent, even when youreplace the variables above like $AGE with literal numbers, like 1.A bigger difference between the two approaches can be found in memory use.A hash representation takes up more memory than an array representationbecause you have to allocate memory for the keys as well as for the values.However, it really isn't that bad, especially since as of version 5.004,memory is only allocated once for a given hash key, no matter how manyhashes have that key.  It's expected that sometime in the future, eventhese differences will fade into obscurity as more efficient underlyingrepresentations are devised.Still, the tiny edge in speed (and somewhat larger one in memory)is enough to make some programmers choose an array representationfor simple classes.  There's still a little problem withscalability, though, because later in life when you feellike creating subclasses, you'll find that hashes just workout better.=head2 Closures as ObjectsUsing a code reference to represent an object offers some fascinatingpossibilities.  We can create a new anonymous function (closure) whoalone in all the world can see the object's data.  This is because weput the data into an anonymous hash that's lexically visible only tothe closure we create, bless, and return as the object.  This object'smethods turn around and call the closure as a regular subroutine call,passing it the field we want to affect.  (Yes,the double-function call is slow, but if you wanted fast, you wouldn'tbe using objects at all, eh? :-)Use would be similar to before:    use Person;    $him = Person->new();    $him->name("Jason");    $him->age(23);    $him->peers( [ "Norbert", "Rhys", "Phineas" ] );    printf "%s is %d years old.\n", $him->name, $him->age;    print "His peers are: ", join(", ", @{$him->peers}), "\n";but the implementation would be radically, perhaps even sublimelydifferent:    package Person;    sub new {	 my $class  = shift;	 my $self = {	    NAME  => undef,	    AGE   => undef,	    PEERS => [],	 };	 my $closure = sub {	    my $field = shift;	    if (@_) { $self->{$field} = shift }	    return    $self->{$field};	};	bless($closure, $class);	return $closure;    }    sub name   { &{ $_[0] }("NAME",  @_[ 1 .. $#_ ] ) }    sub age    { &{ $_[0] }("AGE",   @_[ 1 .. $#_ ] ) }    sub peers  { &{ $_[0] }("PEERS", @_[ 1 .. $#_ ] ) }    1;Because this object is hidden behind a code reference, it's probably a bitmysterious to those whose background is more firmly rooted in standardprocedural or object-based programming languages than in functionalprogramming languages whence closures derive.  The objectcreated and returned by the new() method is itself not a data referenceas we've seen before.  It's an anonymous code reference that has withinit access to a specific version (lexical binding and instantiation)of the object's data, which are stored in the private variable $self.Although this is the same function each time, it contains a differentversion of $self.When a method like C<$him-E<gt>name("Jason")> is called, its implicitzeroth argument is the invoking object--just as it is with all methodcalls.  But in this case, it's our code reference (something like afunction pointer in C++, but with deep binding of lexical variables).There's not a lot to be done with a code reference beyond calling it, sothat's just what we do when we say C<&{$_[0]}>.  This is just a regularfunction call, not a method call.  The initial argument is the string"NAME", and any remaining arguments are whatever had been passed to themethod itself.Once we're executing inside the closure that had been created in new(),the $self hash reference suddenly becomes visible.  The closure grabsits first argument ("NAME" in this case because that's what the name()method passed it), and uses that string to subscript into the privatehash hidden in its unique version of $self.Nothing under the sun will allow anyone outside the executing method tobe able to get at this hidden data.  Well, nearly nothing.  You I<could>single step through the program using the debugger and find out thepieces while you're in the method, but everyone else is out of luck.There, if that doesn't excite the Scheme folks, then I just don't knowwhat will.  Translation of this technique into C++, Java, or any otherbraindead-static language is left as a futile exercise for aficionadosof those camps.You could even add a bit of nosiness via the caller() function andmake the closure refuse to operate unless called via its own package.This would no doubt satisfy certain fastidious concerns of programmingpolice and related puritans.If you were wondering when Hubris, the third principle virtue of aprogrammer, would come into play, here you have it. (More seriously,Hubris is just the pride in craftsmanship that comes from having writtena sound bit of well-designed code.)=head1 AUTOLOAD: Proxy MethodsAutoloading is a way to intercept calls to undefined methods.  An autoloadroutine may choose to create a new function on the fly, either loadedfrom disk or perhaps just eval()ed right there.  This define-on-the-flystrategy is why it's called autoloading.But that's only one possible approach.  Another one is to justhave the autoloaded method itself directly provide therequested service.  When used in this way, you may thinkof autoloaded methods as "proxy" methods.When Perl tries to call an undefined function in a particular packageand that function is not defined, it looks for a function inthat same package called AUTOLOAD.  If one exists, it's calledwith the same arguments as the original function would have had.The fully-qualified name of the function is stored in that package'sglobal variable $AUTOLOAD.  Once called, the function can do anythingit would like, including defining a new function by the right name, andthen doing a really fancy kind of C<goto> right to it, erasing itselffrom the call stack.What does this have to do with objects?  After all, we keep talking aboutfunctions, not methods.  Well, since a method is just a function withan extra argument and some fancier semantics about where it's found,we can use autoloading for methods, too.  Perl doesn't start lookingfor an AUTOLOAD method until it has exhausted the recursive hunt upthrough @ISA, though.  Some programmers have even been known to definea UNIVERSAL::AUTOLOAD method to trap unresolved method calls to anykind of object.=head2 Autoloaded Data MethodsYou probably began to get a little suspicious about the duplicatedcode way back earlier when we first showed you the Person class, andthen later the Employee class.  Each method used to access thehash fields looked virtually identical.  This should have tickledthat great programming virtue, Impatience, but for the time,we let Laziness win out, and so did nothing.  Proxy methods can curethis.

⌨️ 快捷键说明

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