📄 perltooc.1
字号:
.Ve.PPBecause we were careful to be good little creators when we designed ourCosmos class, we can now reuse it without touching a single line of codewhen it comes time to write our Multiverse class. The same code thatworked when invoked as a class method continues to work perfectly wellwhen invoked against separate instances of a derived class..PPThe astonishing thing about the Cosmos class above is that the valuereturned by the &bigbang \*(L"constructor\*(R" is not a reference to a blessedobject at all. It's just the class's own name. A class name is, forvirtually all intents and purposes, a perfectly acceptable object.It has state, behavior, and identity, the three crucial componentsof an object system. It even manifests inheritance, polymorphism,and encapsulation. And what more can you ask of an object?.PPTo understand object orientation in Perl, it's important to recognize theunification of what other programming languages might think of as classmethods and object methods into just plain methods. \*(L"Class methods\*(R"and \*(L"object methods\*(R" are distinct only in the compartmentalizing mindof the Perl programmer, not in the Perl language itself..PPAlong those same lines, a constructor is nothing special either, whichis one reason why Perl has no pre-ordained name for them. \*(L"Constructor\*(R"is just an informal term loosely used to describe a method that returnsa scalar value that you can make further method calls against. So longas it's either a class name or an object reference, that's good enough.It doesn't even have to be a reference to a brand new object..PPYou can have as many\*(--or as few\*(--constructors as you want, and you canname them whatever you care to. Blindly and obediently using \fInew()\fRfor each and every constructor you ever write is to speak Perl withsuch a severe \*(C+ accent that you do a disservice to both languages.There's no reason to insist that each class have but one constructor,or that a constructor be named \fInew()\fR, or that a constructor beused solely as a class method and not an object method..PPThe next section shows how useful it can be to further distance ourselvesfrom any formal distinction between class method calls and object methodcalls, both in constructors and in accessor methods..Sh "Translucent Attributes".IX Subsection "Translucent Attributes"A package's eponymous hash can be used for more than just containingper-class, global state data. It can also serve as a sort of templatecontaining default settings for object attributes. These defaultsettings can then be used in constructors for initialization of aparticular object. The class's eponymous hash can also be used toimplement \fItranslucent attributes\fR. A translucent attribute is onethat has a class-wide default. Each object can set its own value for theattribute, in which case \f(CW\*(C`$object\->attribute()\*(C'\fR returns that value.But if no value has been set, then \f(CW\*(C`$object\->attribute()\*(C'\fR returnsthe class-wide default..PPWe'll apply something of a copy-on-write approach to these translucentattributes. If you're just fetching values from them, you gettranslucency. But if you store a new value to them, that new value isset on the current object. On the other hand, if you use the class asan object and store the attribute value directly on the class, then themeta-object's value changes, and later fetch operations on objects withuninitialized values for those attributes will retrieve the meta-object'snew values. Objects with their own initialized values, however, won'tsee any change..PPLet's look at some concrete examples of using these properties before weshow how to implement them. Suppose that a class named Some_Classhad a translucent data attribute called \*(L"color\*(R". First you set the colorin the meta-object, then you create three objects using a constructorthat happens to be named &spawn..PP.Vb 2\& use Vermin;\& Vermin\->color("vermilion");\&\& $ob1 = Vermin\->spawn(); # so that\*(Aqs where Jedi come from\& $ob2 = Vermin\->spawn(); \& $ob3 = Vermin\->spawn(); \&\& print $obj3\->color(); # prints "vermilion".Ve.PPEach of these objects' colors is now \*(L"vermilion\*(R", because that's themeta-object's value for that attribute, and these objects do not haveindividual color values set..PPChanging the attribute on one object has no effect on other objectspreviously created..PP.Vb 3\& $ob3\->color("chartreuse"); \& print $ob3\->color(); # prints "chartreuse"\& print $ob1\->color(); # prints "vermilion", translucently.Ve.PPIf you now use \f(CW$ob3\fR to spawn off another object, the new object willtake the color its parent held, which now happens to be \*(L"chartreuse\*(R".That's because the constructor uses the invoking object as its templatefor initializing attributes. When that invoking object is theclass name, the object used as a template is the eponymous meta-object.When the invoking object is a reference to an instantiated object, the&spawn constructor uses that existing object as a template..PP.Vb 2\& $ob4 = $ob3\->spawn(); # $ob3 now template, not %Vermin\& print $ob4\->color(); # prints "chartreuse".Ve.PPAny actual values set on the template object will be copied to thenew object. But attributes undefined in the template object, beingtranslucent, will remain undefined and consequently translucent in thenew one as well..PPNow let's change the color attribute on the entire class:.PP.Vb 5\& Vermin\->color("azure"); \& print $ob1\->color(); # prints "azure"\& print $ob2\->color(); # prints "azure"\& print $ob3\->color(); # prints "chartreuse"\& print $ob4\->color(); # prints "chartreuse".Ve.PPThat color change took effect only in the first pair of objects, whichwere still translucently accessing the meta-object's values. The secondpair had per-object initialized colors, and so didn't change..PPOne important question remains. Changes to the meta-object are reflectedin translucent attributes in the entire class, but what aboutchanges to discrete objects? If you change the color of \f(CW$ob3\fR, does thevalue of \f(CW$ob4\fR see that change? Or vice-versa. If you change the colorof \f(CW$ob4\fR, does then the value of \f(CW$ob3\fR shift?.PP.Vb 3\& $ob3\->color("amethyst"); \& print $ob3\->color(); # prints "amethyst"\& print $ob4\->color(); # hmm: "chartreuse" or "amethyst"?.Ve.PPWhile one could argue that in certain rare cases it should, let's notdo that. Good taste aside, we want the answer to the question posed inthe comment above to be \*(L"chartreuse\*(R", not \*(L"amethyst\*(R". So we'll treatthese attributes similar to the way process attributes like environmentvariables, user and group IDs, or the current working directory aretreated across a \fIfork()\fR. You can change only yourself, but you will seethose changes reflected in your unspawned children. Changes to one objectwill propagate neither up to the parent nor down to any existing child objects.Those objects made later, however, will see the changes..PPIf you have an object with an actual attribute value, and you want tomake that object's attribute value translucent again, what do you do?Let's design the class so that when you invoke an accessor method with\&\f(CW\*(C`undef\*(C'\fR as its argument, that attribute returns to translucency..PP.Vb 1\& $ob4\->color(undef); # back to "azure".Ve.PPHere's a complete implementation of Vermin as described above..PP.Vb 1\& package Vermin;\&\& # here\*(Aqs the class meta\-object, eponymously named.\& # it holds all class attributes, and also all instance attributes \& # so the latter can be used for both initialization \& # and translucency.\&\& our %Vermin = ( # our() is new to perl5.6\& PopCount => 0, # capital for class attributes\& color => "beige", # small for instance attributes \& );\&\& # constructor method\& # invoked as class method or object method\& sub spawn {\& my $obclass = shift;\& my $class = ref($obclass) || $obclass;\& my $self = {};\& bless($self, $class);\& $class\->{PopCount}++;\& # init fields from invoking object, or omit if \& # invoking object is the class to provide translucency\& %$self = %$obclass if ref $obclass;\& return $self;\& } \&\& # translucent accessor for "color" attribute\& # invoked as class method or object method\& sub color {\& my $self = shift;\& my $class = ref($self) || $self;\&\& # handle class invocation\& unless (ref $self) {\& $class\->{color} = shift if @_;\& return $class\->{color}\& }\&\& # handle object invocation\& $self\->{color} = shift if @_;\& if (defined $self\->{color}) { # not exists!\& return $self\->{color};\& } else {\& return $class\->{color};\& } \& } \&\& # accessor for "PopCount" class attribute\& # invoked as class method or object method\& # but uses object solely to locate meta\-object\& sub population {\& my $obclass = shift;\& my $class = ref($obclass) || $obclass;\& return $class\->{PopCount};\& } \&\& # instance destructor\& # invoked only as object method\& sub DESTROY {\& my $self = shift;\& my $class = ref $self;\& $class\->{PopCount}\-\-;\& }.Ve.PPHere are a couple of helper methods that might be convenient. They aren'taccessor methods at all. They're used to detect accessibility of dataattributes. The &is_translucent method determines whether a particularobject attribute is coming from the meta-object. The &has_attributemethod detects whether a class implements a particular property at all.It could also be used to distinguish undefined properties from non-existentones..PP.Vb 6\& # detect whether an object attribute is translucent\& # (typically?) invoked only as object method\& sub is_translucent {\& my($self, $attr) = @_;\& return !defined $self\->{$attr}; \& }\&\& # test for presence of attribute in class\& # invoked as class method or object method\& sub has_attribute {\& my($self, $attr) = @_;\& my $class = ref($self) || $self;\& return exists $class\->{$attr}; \& }.Ve.PPIf you prefer to install your accessors more generically, you can makeuse of the upper-case versus lower-case convention to register into thepackage appropriate methods cloned from generic closures..PP.Vb 10\& for my $datum (keys %{ +_\|_PACKAGE_\|_ }) {\& *$datum = ($datum =~ /^[A\-Z]/)\& ? sub { # install class accessor\& my $obclass = shift;\& my $class = ref($obclass) || $obclass;\& return $class\->{$datum};\& }\& : sub { # install translucent accessor\& my $self = shift;\& my $class = ref($self) || $self;\& unless (ref $self) {\& $class\->{$datum} = shift if @_;\& return $class\->{$datum}\& }\& $self\->{$datum} = shift if @_;\& return defined $self\->{$datum}\& ? $self \-> {$datum}\& : $class \-> {$datum}\& } \& }.Ve.PPTranslations of this closure-based approach into \*(C+, Java, and Pythonhave been left as exercises for the reader. Be sure to send us mail assoon as you're done..SH "Class Data as Lexical Variables".IX Header "Class Data as Lexical Variables".Sh "Privacy and Responsibility".IX Subsection "Privacy and Responsibility"Unlike conventions used by some Perl programmers, in the previousexamples, we didn't prefix the package variables used for class attributeswith an underscore, nor did we do so for the names of the hash keys usedfor instance attributes. You don't need little markers on data names tosuggest nominal privacy on attribute variables or hash keys, because theseare \fBalready\fR notionally private! Outsiders have no business whatsoeverplaying with anything within a class save through the mediated access ofits documented interface; in other words, through method invocations.And not even through just any method, either. Methods that begin withan underscore are traditionally considered off-limits outside the class.If outsiders skip the documented method interface to poke around theinternals of your class and end up breaking something, that's not yourfault\*(--it's theirs..PPPerl believes in individual responsibility rather than mandated control.Perl respects you enough to let you choose your own preferred level ofpain, or of pleasure. Perl believes that you are creative, intelligent,and capable of making your own decisions\*(--and fully expects you totake complete responsibility for your own actions. In a perfect world,these admonitions alone would suffice, and everyone would be intelligent,responsible, happy, and creative. And careful. One probably shouldn'tforget careful, and that's a good bit harder to expect. Even Einsteinwould take wrong turns by accident and end up lost in the wrong partof town..PPSome folks get the heebie-jeebies when they see package variableshanging out there for anyone to reach over and alter them. Some folkslive in constant fear that someone somewhere might do something wicked.The solution to that problem is simply to fire the wicked, of course.But unfortunately, it's not as simple as all that. These cautioustypes are also afraid that they or others will do something not somuch wicked as careless, whether by accident or out of desperation.If we fire everyone who ever gets careless, pretty soon there won't beanybody left to get any work done..PPWhether it's needless paranoia or sensible caution, this uneasiness canbe a problem for some people. We can take the edge off their discomfortby providing the option of storing class attributes as lexical variablesinstead of as package variables. The \fImy()\fR operator is the source ofall privacy in Perl, and it is a powerful form of privacy indeed..PPIt is widely perceived, and indeed has often been written, that Perlprovides no data hiding, that it affords the class designer no privacynor isolation, merely a rag-tag assortment of weak and unenforceablesocial conventions instead. This perception is demonstrably false andeasily disproven. In the next section, we show how to implement formsof privacy that are far stronger than those provided in nearly anyother object-oriented language..Sh "File-Scoped Lexicals".IX Subsection "File-Scoped Lexicals"A lexical variable is visible only through the end of its static scope.That means that the only code able to access that variable is coderesiding textually below the \fImy()\fR operator through the end of its blockif it has one, or through the end of the current file if it doesn't..PPStarting again with our simplest example given at the start of thisdocument, we replace \fIour()\fR variables with \fImy()\fR versions..PP.Vb 12\& package Some_Class;\& my($CData1, $CData2); # file scope, not in any package\& sub CData1 {\& shift; # XXX: ignore calling class/object\& $CData1 = shift if @_;\& return $CData1;\& } \& sub CData2 {\& shift; # XXX: ignore calling class/object\& $CData2 = shift if @_;\& return $CData2;\& }.Ve.PPSo much for that old \f(CW$Some_Class::CData1\fR package variable and its brethren!Those are gone now, replaced with lexicals. No one outside thescope can reach in and alter the class state without resorting to thedocumented interface. Not even subclasses or superclasses ofthis one have unmediated access to \f(CW$CData1\fR. They have to invoke the &CData1method against Some_Class or an instance thereof, just like anybody else..PPTo be scrupulously honest, that last statement assumes you haven't packedseveral classes together into the same file scope, nor strewn your classimplementation across several different files. Accessibility of thosevariables is based uniquely on the static file scope. It has nothing todo with the package. That means that code in a different file butthe same package (class) could not access those variables, yet code in thesame file but a different package (class) could. There are sound reasonswhy we usually suggest a one-to-one mapping between files and packagesand modules and classes. You don't have to stick to this suggestion ifyou really know what you're doing, but you're apt to confuse yourselfotherwise, especially at first..PPIf you'd like to aggregate your class attributes into one lexically scoped,composite structure, you're perfectly free to do so..PP.Vb 10
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -