From a25275c0c9488d0f50e2e64e5ca84ecb8fd32f7e Mon Sep 17 00:00:00 2001 From: Dave Rolsky Date: Thu, 7 Jul 2011 11:43:25 -0500 Subject: [PATCH] remove all old OO tutorials (and perlbot) --- pod/perlboot.pod | 884 -------------------------- pod/perlbot.pod | 535 ---------------- pod/perltooc.pod | 1342 --------------------------------------- pod/perltoot.pod | 1836 ------------------------------------------------------ 4 files changed, 4597 deletions(-) delete mode 100644 pod/perlboot.pod delete mode 100644 pod/perlbot.pod delete mode 100644 pod/perltooc.pod delete mode 100644 pod/perltoot.pod diff --git a/pod/perlboot.pod b/pod/perlboot.pod deleted file mode 100644 index 5aa6179..0000000 --- a/pod/perlboot.pod +++ /dev/null @@ -1,884 +0,0 @@ -=head1 NAME - -perlboot - Beginner's Object-Oriented Tutorial - -=head1 DESCRIPTION - -If you're not familiar with objects from other languages, some of the -other Perl object documentation may be a little daunting, such as -L, a basic reference in using objects, and L, which -introduces readers to the peculiarities of Perl's object system in a -tutorial way. - -So, let's take a different approach, presuming no prior object -experience. It helps if you know about subroutines (L), -references (L et. seq.), and packages (L), so become -familiar with those first if you haven't already. - -=head2 If we could talk to the animals... - -Let's let the animals talk for a moment: - - sub Cow::speak { - print "a Cow goes moooo!\n"; - } - sub Horse::speak { - print "a Horse goes neigh!\n"; - } - sub Sheep::speak { - print "a Sheep goes baaaah!\n"; - } - - Cow::speak; - Horse::speak; - Sheep::speak; - -This results in: - - a Cow goes moooo! - a Horse goes neigh! - a Sheep goes baaaah! - -Nothing spectacular here. Simple subroutines, albeit from separate -packages, and called using the full package name. So let's create -an entire pasture: - - # Cow::speak, Horse::speak, Sheep::speak as before - @pasture = qw(Cow Cow Horse Sheep Sheep); - foreach $animal (@pasture) { - &{$animal."::speak"}; - } - -This results in: - - a Cow goes moooo! - a Cow goes moooo! - a Horse goes neigh! - a Sheep goes baaaah! - a Sheep goes baaaah! - -Wow. That symbolic coderef de-referencing there is pretty nasty. -We're counting on C mode, certainly not recommended -for larger programs. And why was that necessary? Because the name of -the package seems to be inseparable from the name of the subroutine we -want to invoke within that package. - -Or is it? - -=head2 Introducing the method invocation arrow - -For now, let's say that C<< Class->method >> invokes subroutine -C in package C. (Here, "Class" is used in its -"category" meaning, not its "scholastic" meaning.) That's not -completely accurate, but we'll do this one step at a time. Now let's -use it like so: - - # Cow::speak, Horse::speak, Sheep::speak as before - Cow->speak; - Horse->speak; - Sheep->speak; - -And once again, this results in: - - a Cow goes moooo! - a Horse goes neigh! - a Sheep goes baaaah! - -That's not fun yet. Same number of characters, all constant, no -variables. But yet, the parts are separable now. Watch: - - $a = "Cow"; - $a->speak; # invokes Cow->speak - -Ahh! Now that the package name has been parted from the subroutine -name, we can use a variable package name. And this time, we've got -something that works even when C is enabled. - -=head2 Invoking a barnyard - -Let's take that new arrow invocation and put it back in the barnyard -example: - - sub Cow::speak { - print "a Cow goes moooo!\n"; - } - sub Horse::speak { - print "a Horse goes neigh!\n"; - } - sub Sheep::speak { - print "a Sheep goes baaaah!\n"; - } - - @pasture = qw(Cow Cow Horse Sheep Sheep); - foreach $animal (@pasture) { - $animal->speak; - } - -There! Now we have the animals all talking, and safely at that, -without the use of symbolic coderefs. - -But look at all that common code. Each of the C routines has a -similar structure: a C operator and a string that contains -common text, except for two of the words. It'd be nice if we could -factor out the commonality, in case we decide later to change it all -to C instead of C. - -And we actually have a way of doing that without much fuss, but we -have to hear a bit more about what the method invocation arrow is -actually doing for us. - -=head2 The extra parameter of method invocation - -The invocation of: - - Class->method(@args) - -attempts to invoke subroutine C as: - - Class::method("Class", @args); - -(If the subroutine can't be found, "inheritance" kicks in, but we'll -get to that later.) This means that we get the class name as the -first parameter (the only parameter, if no arguments are given). So -we can rewrite the C speaking subroutine as: - - sub Sheep::speak { - my $class = shift; - print "a $class goes baaaah!\n"; - } - -And the other two animals come out similarly: - - sub Cow::speak { - my $class = shift; - print "a $class goes moooo!\n"; - } - sub Horse::speak { - my $class = shift; - print "a $class goes neigh!\n"; - } - -In each case, C<$class> will get the value appropriate for that -subroutine. But once again, we have a lot of similar structure. Can -we factor that out even further? Yes, by calling another method in -the same class. - -=head2 Calling a second method to simplify things - -Let's call out from C to a helper method called C. -This method provides the constant text for the sound itself. - - { package Cow; - sub sound { "moooo" } - sub speak { - my $class = shift; - print "a $class goes ", $class->sound, "!\n"; - } - } - -Now, when we call C<< Cow->speak >>, we get a C<$class> of C in -C. This in turn selects the C<< Cow->sound >> method, which -returns C. But how different would this be for the C? - - { package Horse; - sub sound { "neigh" } - sub speak { - my $class = shift; - print "a $class goes ", $class->sound, "!\n"; - } - } - -Only the name of the package and the specific sound change. So can we -somehow share the definition for C between the Cow and the -Horse? Yes, with inheritance! - -=head2 Inheriting the windpipes - -We'll define a common subroutine package called C, with the -definition for C: - - { package Animal; - sub speak { - my $class = shift; - print "a $class goes ", $class->sound, "!\n"; - } - } - -Then, for each animal, we say it "inherits" from C, along -with the animal-specific sound: - - { package Cow; - @ISA = qw(Animal); - sub sound { "moooo" } - } - -Note the added C<@ISA> array (pronounced "is a"). We'll get to that in a minute. - -But what happens when we invoke C<< Cow->speak >> now? - -First, Perl constructs the argument list. In this case, it's just -C. Then Perl looks for C. But that's not there, so -Perl checks for the inheritance array C<@Cow::ISA>. It's there, -and contains the single name C. - -Perl next checks for C inside C instead, as in -C. And that's found, so Perl invokes that subroutine -with the already frozen argument list. - -Inside the C subroutine, C<$class> becomes C (the -first argument). So when we get to the step of invoking -C<< $class->sound >>, it'll be looking for C<< Cow->sound >>, which -gets it on the first try without looking at C<@ISA>. Success! - -=head2 A few notes about @ISA - -This magical C<@ISA> variable has declared that C "is a" C. -Note that it's an array, not a simple single value, because on rare -occasions, it makes sense to have more than one parent class searched -for the missing methods. - -If C also had an C<@ISA>, then we'd check there too. The -search is recursive, depth-first, left-to-right in each C<@ISA> by -default (see L for alternatives). Typically, each C<@ISA> has -only one element (multiple elements means multiple inheritance and -multiple headaches), so we get a nice tree of inheritance. - -When we turn on C, we'll get complaints on C<@ISA>, since -it's not a variable containing an explicit package name, nor is it a -lexical ("my") variable. We can't make it a lexical variable though -(it has to belong to the package to be found by the inheritance mechanism), -so there's a couple of straightforward ways to handle that. - -The easiest is to just spell the package name out: - - @Cow::ISA = qw(Animal); - -Or declare it as a package global variable: - - package Cow; - our @ISA = qw(Animal); - -Or allow it as an implicitly named package variable: - - package Cow; - use vars qw(@ISA); - @ISA = qw(Animal); - -If the C class comes from another (object-oriented) module, then -just employ C to specify that C should serve as the basis -for the C class: - - package Cow; - use base qw(Animal); - -Now that's pretty darn simple! - -=head2 Overriding the methods - -Let's add a mouse, which can barely be heard: - - # Animal package from before - { package Mouse; - @ISA = qw(Animal); - sub sound { "squeak" } - sub speak { - my $class = shift; - print "a $class goes ", $class->sound, "!\n"; - print "[but you can barely hear it!]\n"; - } - } - - Mouse->speak; - -which results in: - - a Mouse goes squeak! - [but you can barely hear it!] - -Here, C has its own speaking routine, so C<< Mouse->speak >> -doesn't immediately invoke C<< Animal->speak >>. This is known as -"overriding". In fact, we don't even need to say that a C is -an C at all, because all of the methods needed for C are -completely defined for C; this is known as "duck typing": -"If it walks like a duck and quacks like a duck, I would call it a duck" -(James Whitcomb). However, it would probably be beneficial to allow a -closer examination to conclude that a C is indeed an C, -so it is actually better to define C with C as its base -(that is, it is better to "derive C from C"). - -Moreover, this duplication of code could become a maintenance headache -(though code-reuse is not actually a good reason for inheritance; good -design practices dictate that a derived class should be usable wherever -its base class is usable, which might not be the outcome if code-reuse -is the sole criterion for inheritance. Just remember that a C -should always act like an C). - -So, let's make C an C! - -The obvious solution is to invoke C directly: - - # Animal package from before - { package Mouse; - @ISA = qw(Animal); - sub sound { "squeak" } - sub speak { - my $class = shift; - Animal::speak($class); - print "[but you can barely hear it!]\n"; - } - } - -Note that we're using C. If we were to invoke -C<< Animal->speak >> instead, the first parameter to C -would automatically be C<"Animal"> rather than C<"Mouse">, so that -the call to C<< $class->sound >> in C would become -C<< Animal->sound >> rather than C<< Mouse->sound >>. - -Also, without the method arrow C<< -> >>, it becomes necessary to specify -the first parameter to C ourselves, which is why C<$class> -is explicitly passed: C. - -However, invoking C directly is a mess: Firstly, it assumes -that the C method is a member of the C class; what if C -actually inherits C from its own base? Because we are no longer using -C<< -> >> to access C, the special method look up mechanism wouldn't be -used, so C wouldn't even be found! - -The second problem is more subtle: C is now hardwired into the subroutine -selection. Let's assume that C does exist. What happens when, -at a later time, someone expands the class hierarchy by having C -inherit from C instead of C. Unless the invocation of C -is also changed to an invocation of C, centuries worth of taxonomical -classification could be obliterated! - -What we have here is a fragile or leaky abstraction; it is the beginning of a -maintenance nightmare. What we need is the ability to search for the right -method wih as few assumptions as possible. - -=head2 Starting the search from a different place - -A I solution is to tell Perl where in the inheritance chain to begin searching -for C. This can be achieved with a modified version of the method arrow C<< -> >>: - - ClassName->FirstPlaceToLook::method - -So, the improved C class is: - - # same Animal as before - { package Mouse; - # same @ISA, &sound as before - sub speak { - my $class = shift; - $class->Animal::speak; - print "[but you can barely hear it!]\n"; - } - } - -Using this syntax, we start with C to find C, and then -use all of C's inheritance chain if it is not found immediately. -As usual, the first parameter to C would be C<$class>, so we no -longer need to pass C<$class> explicitly to C. - -But what about the second problem? We're still hardwiring C into -the method lookup. - -=head2 The SUPER way of doing things - -If C is replaced with the special placeholder C in that -invocation, then the contents of C's C<@ISA> are used for the -search, beginning with C<$ISA[0]>. So, all of the problems can be fixed -as follows: - - # same Animal as before - { package Mouse; - # same @ISA, &sound as before - sub speak { - my $class = shift; - $class->SUPER::speak; - print "[but you can barely hear it!]\n"; - } - } - -In general, C means look in the current package's C<@ISA> -for a class that implements C, and invoke the first one found. -The placeholder is called C, because many other languages refer -to base classes as "Iclasses", and Perl likes to be eclectic. - -Note that a call such as - - $class->SUPER::method; - -does I look in the C<@ISA> of C<$class> unless C<$class> happens to -be the current package. - -=head2 Let's review... - -So far, we've seen the method arrow syntax: - - Class->method(@args); - -or the equivalent: - - $a = "Class"; - $a->method(@args); - -which constructs an argument list of: - - ("Class", @args) - -and attempts to invoke: - - Class::method("Class", @args); - -However, if C is not found, then C<@Class::ISA> is examined -(recursively) to locate a class (a package) that does indeed contain C, -and that subroutine is invoked instead. - -Using this simple syntax, we have class methods, (multiple) inheritance, -overriding, and extending. Using just what we've seen so far, we've -been able to factor out common code (though that's never a good reason -for inheritance!), and provide a nice way to reuse implementations with -variations. - -Now, what about data? - -=head2 A horse is a horse, of course of course, or is it? - -Let's start with the code for the C class -and the C class: - - { package Animal; - sub speak { - my $class = shift; - print "a $class goes ", $class->sound, "!\n"; - } - } - { package Horse; - @ISA = qw(Animal); - sub sound { "neigh" } - } - -This lets us invoke C<< Horse->speak >> to ripple upward to -C, calling back to C to get the specific -sound, and the output of: - - a Horse goes neigh! - -But all of our Horse objects would have to be absolutely identical. -If we add a subroutine, all horses automatically share it. That's -great for making horses the same, but how do we capture the -distinctions of an individual horse? For example, suppose we want -to give our first horse a name. There's got to be a way to keep its -name separate from the other horses. - -That is to say, we want particular instances of C to have -different names. - -In Perl, any reference can be an "instance", so let's start with the -simplest reference that can hold a horse's name: a scalar reference. - - my $name = "Mr. Ed"; - my $horse = \$name; - -So, now C<$horse> is a reference to what will be the instance-specific -data (the name). The final step is to turn this reference into a real -instance of a C by using the special operator C: - - bless $horse, Horse; - -This operator stores information about the package named C into -the thing pointed at by the reference. At this point, we say -C<$horse> is an instance of C. That is, it's a specific -horse. The reference is otherwise unchanged, and can still be used -with traditional dereferencing operators. - -=head2 Invoking an instance method - -The method arrow can be used on instances, as well as classes (the names -of packages). So, let's get the sound that C<$horse> makes: - - my $noise = $horse->sound("some", "unnecessary", "args"); - -To invoke C, Perl first notes that C<$horse> is a blessed -reference (and thus an instance). It then constructs an argument -list, as per usual. - -Now for the fun part: Perl takes the class in which the instance was -blessed, in this case C, and uses that class to locate the -subroutine. In this case, C is found directly (without -using inheritance). In the end, it is as though our initial line were -written as follows: - - my $noise = Horse::sound($horse, "some", "unnecessary", "args"); - -Note that the first parameter here is still the instance, not the name -of the class as before. We'll get C as the return value, and -that'll end up as the C<$noise> variable above. - -If Horse::sound had not been found, we'd be wandering up the C<@Horse::ISA> -array, trying to find the method in one of the superclasses. The only -difference between a class method and an instance method is whether the -first parameter is an instance (a blessed reference) or a class name (a -string). - -=head2 Accessing the instance data - -Because we get the instance as the first parameter, we can now access -the instance-specific data. In this case, let's add a way to get at -the name: - - { package Horse; - @ISA = qw(Animal); - sub sound { "neigh" } - sub name { - my $self = shift; - $$self; - } - } - -Inside C, the C<@_> array contains: - - ($horse, "some", "unnecessary", "args") - -so the C stores C<$horse> into C<$self>. Then, C<$self> gets -de-referenced with C<$$self> as normal, yielding C<"Mr. Ed">. - -It's traditional to C the first parameter into a variable named -C<$self> for instance methods and into a variable named C<$class> for -class methods. - -Then, the following line: - - print $horse->name, " says ", $horse->sound, "\n"; - -outputs: - - Mr. Ed says neigh. - -=head2 How to build a horse - -Of course, if we constructed all of our horses by hand, we'd most -likely make mistakes from time to time. We're also violating one of -the properties of object-oriented programming, in that the "inside -guts" of a Horse are visible. That's good if you're a veterinarian, -but not if you just like to own horses. So, let's have the Horse -class handle the details inside a class method: - - { package Horse; - @ISA = qw(Animal); - sub sound { "neigh" } - sub name { - my $self = shift; # instance method, so use $self - $$self; - } - sub named { - my $class = shift; # class method, so use $class - my $name = shift; - bless \$name, $class; - } - } - -Now with the new C method, we can build a horse as follows: - - my $horse = Horse->named("Mr. Ed"); - -Notice we're back to a class method, so the two arguments to -C are C and C. The C operator -not only blesses C<\$name>, it also returns that reference. - -This C method is called a "constructor". - -We've called the constructor C here, so that it quickly denotes -the constructor's argument as the name for this particular C. -You can use different constructors with different names for different -ways of "giving birth" to the object (like maybe recording its -pedigree or date of birth). However, you'll find that most people -coming to Perl from more limited languages use a single constructor -named C, with various ways of interpreting the arguments to -C. Either style is fine, as long as you document your particular -way of giving birth to an object. (And you I going to do that, -right?) - -=head2 Inheriting the constructor - -But was there anything specific to C in that method? No. Therefore, -it's also the same recipe for building anything else that inherited from -C, so let's put C and C there: - - { package Animal; - sub speak { - my $class = shift; - print "a $class goes ", $class->sound, "!\n"; - } - sub name { - my $self = shift; - $$self; - } - sub named { - my $class = shift; - my $name = shift; - bless \$name, $class; - } - } - { package Horse; - @ISA = qw(Animal); - sub sound { "neigh" } - } - -Ahh, but what happens if we invoke C on an instance? - - my $horse = Horse->named("Mr. Ed"); - $horse->speak; - -We get a debugging value: - - a Horse=SCALAR(0xaca42ac) goes neigh! - -Why? Because the C routine is expecting a classname as -its first parameter, not an instance. When the instance is passed in, -we'll end up using a blessed scalar reference as a string, and that -shows up as we saw it just now. - -=head2 Making a method work with either classes or instances - -All we need is for a method to detect if it is being called on a class -or called on an instance. The most straightforward way is with the -C operator. This returns a string (the classname) when used on a -blessed reference, and an empty string when used on a string (like a -classname). Let's modify the C method first to notice the change: - - sub name { - my $either = shift; - ref $either ? $$either : "Any $either"; - } - -Here, the C operator comes in handy to select either the -dereference or a derived string. Now we can use this with either an -instance or a class. Note that I've changed the first parameter -holder to C<$either> to show that this is intended: - - my $horse = Horse->named("Mr. Ed"); - print Horse->name, "\n"; # prints "Any Horse\n" - print $horse->name, "\n"; # prints "Mr Ed.\n" - -and now we'll fix C to use this: - - sub speak { - my $either = shift; - print $either->name, " goes ", $either->sound, "\n"; - } - -And since C already worked with either a class or an instance, -we're done! - -=head2 Adding parameters to a method - -Let's train our animals to eat: - - { package Animal; - sub named { - my $class = shift; - my $name = shift; - bless \$name, $class; - } - sub name { - my $either = shift; - ref $either ? $$either : "Any $either"; - } - sub speak { - my $either = shift; - print $either->name, " goes ", $either->sound, "\n"; - } - sub eat { - my $either = shift; - my $food = shift; - print $either->name, " eats $food.\n"; - } - } - { package Horse; - @ISA = qw(Animal); - sub sound { "neigh" } - } - { package Sheep; - @ISA = qw(Animal); - sub sound { "baaaah" } - } - -And now try it out: - - my $horse = Horse->named("Mr. Ed"); - $horse->eat("hay"); - Sheep->eat("grass"); - -which prints: - - Mr. Ed eats hay. - Any Sheep eats grass. - -An instance method with parameters gets invoked with the instance, -and then the list of parameters. So that first invocation is like: - - Animal::eat($horse, "hay"); - -=head2 More interesting instances - -What if an instance needs more data? Most interesting instances are -made of many items, each of which can in turn be a reference or even -another object. The easiest way to store these is often in a hash. -The keys of the hash serve as the names of parts of the object (often -called "instance variables" or "member variables"), and the -corresponding values are, well, the values. - -But how do we turn the horse into a hash? Recall that an object was -any blessed reference. We can just as easily make it a blessed hash -reference as a blessed scalar reference, as long as everything that -looks at the reference is changed accordingly. - -Let's make a sheep that has a name and a color: - - my $bad = bless { Name => "Evil", Color => "black" }, Sheep; - -so C<< $bad->{Name} >> has C, and C<< $bad->{Color} >> has -C. But we want to make C<< $bad->name >> access the name, and -that's now messed up because it's expecting a scalar reference. Not -to worry, because that's pretty easy to fix up. - -One solution is to override C and C by -defining them anew in C, but then any methods added later to -C might still mess up, and we'd have to override all of those -too. Therefore, it's never a good idea to define the data layout in a -way that's different from the data layout of the base classes. In fact, -it's a good idea to use blessed hash references in all cases. Also, this -is why it's important to have constructors do the low-level work. So, -let's redefine C: - - ## in Animal - sub name { - my $either = shift; - ref $either ? $either->{Name} : "Any $either"; - } - sub named { - my $class = shift; - my $name = shift; - my $self = { Name => $name }; - bless $self, $class; - } - -Of course, we still need to override C in order to handle -constructing a C with a certain color: - - ## in Sheep - sub named { - my ($class, $name) = @_; - my $self = $class->SUPER::named(@_); - $$self{Color} = $class->default_color; - $self - } - -(Note that C<@_> contains the parameters to C.) - -What's this C? Well, if C has only the name, -we still need to set a color, so we'll have a class-specific default color. -For a sheep, we might define it as white: - - ## in Sheep - sub default_color { "white" } - -Now: - - my $sheep = Sheep->named("Bad"); - print $sheep->{Color}, "\n"; - -outputs: - - white - -Now, there's nothing particularly specific to C when it comes -to color, so let's remove C and implement C -to handle color instead: - - ## in Animal - sub named { - my ($class, $name) = @_; - my $self = { Name => $name, Color => $class->default_color }; - bless $self, $class; - } - -And then to keep from having to define C for each additional -class, we'll define a method that serves as the "default default" directly -in C: - - ## in Animal - sub default_color { "brown" } - -Of course, because C and C were the only methods that -referenced the "structure" of the object, the rest of the methods can -remain the same, so C still works as before. - -=head2 A horse of a different color - -But having all our horses be brown would be boring. So let's add a -method or two to get and set the color. - - ## in Animal - sub color { - $_[0]->{Color} - } - sub set_color { - $_[0]->{Color} = $_[1]; - } - -Note the alternate way of accessing the arguments: C<$_[0]> is used -in-place, rather than with a C. (This saves us a bit of time -for something that may be invoked frequently.) And now we can fix -that color for Mr. Ed: - - my $horse = Horse->named("Mr. Ed"); - $horse->set_color("black-and-white"); - print $horse->name, " is colored ", $horse->color, "\n"; - -which results in: - - Mr. Ed is colored black-and-white - -=head2 Summary - -So, now we have class methods, constructors, instance methods, instance -data, and even accessors. But that's still just the beginning of what -Perl has to offer. We haven't even begun to talk about accessors that -double as getters and setters, destructors, indirect object notation, -overloading, "isa" and "can" tests, the C class, and so on. -That's for the rest of the Perl documentation to cover. Hopefully, this -gets you started, though. - -=head1 SEE ALSO - -For more information, see L (for all the gritty details about -Perl objects, now that you've seen the basics), L (the -tutorial for those who already know objects), L (dealing -with class data), L (for some more tricks), and books such as -Damian Conway's excellent I. - -Some modules which might prove interesting are Class::Accessor, -Class::Class, Class::Contract, Class::Data::Inheritable, -Class::MethodMaker and Tie::SecureHash - -=head1 COPYRIGHT - -Copyright (c) 1999, 2000 by Randal L. Schwartz and Stonehenge -Consulting Services, Inc. - -Copyright (c) 2009 by Michael F. Witten. - -Permission is hereby granted to distribute this document intact with -the Perl distribution, and in accordance with the licenses of the Perl -distribution; derived documents must include this copyright notice -intact. - -Portions of this text have been derived from Perl Training materials -originally appearing in the I course taught by instructors for Stonehenge Consulting -Services, Inc. and used with permission. - -Portions of this text have been derived from materials originally -appearing in I and used with permission. diff --git a/pod/perlbot.pod b/pod/perlbot.pod deleted file mode 100644 index 305a695..0000000 --- a/pod/perlbot.pod +++ /dev/null @@ -1,535 +0,0 @@ -=head1 NAME - -perlbot - Bag o' Object Tricks (the BOT) - -=head1 DESCRIPTION - -The following collection of tricks and hints is intended to whet curious -appetites about such things as the use of instance variables and the -mechanics of object and class relationships. The reader is encouraged to -consult relevant textbooks for discussion of Object Oriented definitions and -methodology. This is not intended as a tutorial for object-oriented -programming or as a comprehensive guide to Perl's object oriented features, -nor should it be construed as a style guide. If you're looking for tutorials, -be sure to read L, L, and L. - -The Perl motto still holds: There's more than one way to do it. - -=head1 OO SCALING TIPS - -=over 5 - -=item 1 - -Do not attempt to verify the type of $self. That'll break if the class is -inherited, when the type of $self is valid but its package isn't what you -expect. See rule 5. - -=item 2 - -If an object-oriented (OO) or indirect-object (IO) syntax was used, then the -object is probably the correct type and there's no need to become paranoid -about it. Perl isn't a paranoid language anyway. If people subvert the OO -or IO syntax then they probably know what they're doing and you should let -them do it. See rule 1. - -=item 3 - -Use the two-argument form of bless(). Let a subclass use your constructor. -See L. - -=item 4 - -The subclass is allowed to know things about its immediate superclass, the -superclass is allowed to know nothing about a subclass. - -=item 5 - -Don't be trigger happy with inheritance. A "using", "containing", or -"delegation" relationship (some sort of aggregation, at least) is often more -appropriate. See L, L, -and L<"DELEGATION">. - -=item 6 - -The object is the namespace. Make package globals accessible via the -object. This will remove the guess work about the symbol's home package. -See L. - -=item 7 - -IO syntax is certainly less noisy, but it is also prone to ambiguities that -can cause difficult-to-find bugs. Allow people to use the sure-thing OO -syntax, even if you don't like it. - -=item 8 - -Do not use function-call syntax on a method. You're going to be bitten -someday. Someone might move that method into a superclass and your code -will be broken. On top of that you're feeding the paranoia in rule 2. - -=item 9 - -Don't assume you know the home package of a method. You're making it -difficult for someone to override that method. See L. - -=back - -=head1 INSTANCE VARIABLES - -An anonymous array or anonymous hash can be used to hold instance -variables. Named parameters are also demonstrated. - - package Foo; - - sub new { - my $type = shift; - my %params = @_; - my $self = {}; - $self->{'High'} = $params{'High'}; - $self->{'Low'} = $params{'Low'}; - bless $self, $type; - } - - - package Bar; - - sub new { - my $type = shift; - my %params = @_; - my $self = []; - $self->[0] = $params{'Left'}; - $self->[1] = $params{'Right'}; - bless $self, $type; - } - - package main; - - $a = Foo->new( 'High' => 42, 'Low' => 11 ); - print "High=$a->{'High'}\n"; - print "Low=$a->{'Low'}\n"; - - $b = Bar->new( 'Left' => 78, 'Right' => 40 ); - print "Left=$b->[0]\n"; - print "Right=$b->[1]\n"; - -=head1 SCALAR INSTANCE VARIABLES - -An anonymous scalar can be used when only one instance variable is needed. - - package Foo; - - sub new { - my $type = shift; - my $self; - $self = shift; - bless \$self, $type; - } - - package main; - - $a = Foo->new( 42 ); - print "a=$$a\n"; - - -=head1 INSTANCE VARIABLE INHERITANCE - -This example demonstrates how one might inherit instance variables from a -superclass for inclusion in the new class. This requires calling the -superclass's constructor and adding one's own instance variables to the new -object. - - package Bar; - - sub new { - my $type = shift; - my $self = {}; - $self->{'buz'} = 42; - bless $self, $type; - } - - package Foo; - @ISA = qw( Bar ); - - sub new { - my $type = shift; - my $self = Bar->new; - $self->{'biz'} = 11; - bless $self, $type; - } - - package main; - - $a = Foo->new; - print "buz = ", $a->{'buz'}, "\n"; - print "biz = ", $a->{'biz'}, "\n"; - - - -=head1 OBJECT RELATIONSHIPS - -The following demonstrates how one might implement "containing" and "using" -relationships between objects. - - package Bar; - - sub new { - my $type = shift; - my $self = {}; - $self->{'buz'} = 42; - bless $self, $type; - } - - package Foo; - - sub new { - my $type = shift; - my $self = {}; - $self->{'Bar'} = Bar->new; - $self->{'biz'} = 11; - bless $self, $type; - } - - package main; - - $a = Foo->new; - print "buz = ", $a->{'Bar'}->{'buz'}, "\n"; - print "biz = ", $a->{'biz'}, "\n"; - - - -=head1 OVERRIDING SUPERCLASS METHODS - -The following example demonstrates how to override a superclass method and -then call the overridden method. The B pseudo-class allows the -programmer to call an overridden superclass method without actually knowing -where that method is defined. - - package Buz; - sub goo { print "here's the goo\n" } - - package Bar; @ISA = qw( Buz ); - sub google { print "google here\n" } - - package Baz; - sub mumble { print "mumbling\n" } - - package Foo; - @ISA = qw( Bar Baz ); - - sub new { - my $type = shift; - bless [], $type; - } - sub grr { print "grumble\n" } - sub goo { - my $self = shift; - $self->SUPER::goo(); - } - sub mumble { - my $self = shift; - $self->SUPER::mumble(); - } - sub google { - my $self = shift; - $self->SUPER::google(); - } - - package main; - - $foo = Foo->new; - $foo->mumble; - $foo->grr; - $foo->goo; - $foo->google; - -Note that C refers to the superclasses of the current package -(C), not to the superclasses of C<$self>. - - -=head1 USING RELATIONSHIP WITH SDBM - -This example demonstrates an interface for the SDBM class. This creates a -"using" relationship between the SDBM class and the new class Mydbm. - - package Mydbm; - - require SDBM_File; - require Tie::Hash; - @ISA = qw( Tie::Hash ); - - sub TIEHASH { - my $type = shift; - my $ref = SDBM_File->new(@_); - bless {'dbm' => $ref}, $type; - } - sub FETCH { - my $self = shift; - my $ref = $self->{'dbm'}; - $ref->FETCH(@_); - } - sub STORE { - my $self = shift; - if (defined $_[0]){ - my $ref = $self->{'dbm'}; - $ref->STORE(@_); - } else { - die "Cannot STORE an undefined key in Mydbm\n"; - } - } - - package main; - use Fcntl qw( O_RDWR O_CREAT ); - - tie %foo, "Mydbm", "Sdbm", O_RDWR|O_CREAT, 0640; - $foo{'bar'} = 123; - print "foo-bar = $foo{'bar'}\n"; - - tie %bar, "Mydbm", "Sdbm2", O_RDWR|O_CREAT, 0640; - $bar{'Cathy'} = 456; - print "bar-Cathy = $bar{'Cathy'}\n"; - -=head1 THINKING OF CODE REUSE - -One strength of Object-Oriented languages is the ease with which old code -can use new code. The following examples will demonstrate first how one can -hinder code reuse and then how one can promote code reuse. - -This first example illustrates a class which uses a fully-qualified method -call to access the "private" method BAZ(). The second example will show -that it is impossible to override the BAZ() method. - - package FOO; - - sub new { - my $type = shift; - bless {}, $type; - } - sub bar { - my $self = shift; - $self->FOO::private::BAZ; - } - - package FOO::private; - - sub BAZ { - print "in BAZ\n"; - } - - package main; - - $a = FOO->new; - $a->bar; - -Now we try to override the BAZ() method. We would like FOO::bar() to call -GOOP::BAZ(), but this cannot happen because FOO::bar() explicitly calls -FOO::private::BAZ(). - - package FOO; - - sub new { - my $type = shift; - bless {}, $type; - } - sub bar { - my $self = shift; - $self->FOO::private::BAZ; - } - - package FOO::private; - - sub BAZ { - print "in BAZ\n"; - } - - package GOOP; - @ISA = qw( FOO ); - sub new { - my $type = shift; - bless {}, $type; - } - - sub BAZ { - print "in GOOP::BAZ\n"; - } - - package main; - - $a = GOOP->new; - $a->bar; - -To create reusable code we must modify class FOO, flattening class -FOO::private. The next example shows a reusable class FOO which allows the -method GOOP::BAZ() to be used in place of FOO::BAZ(). - - package FOO; - - sub new { - my $type = shift; - bless {}, $type; - } - sub bar { - my $self = shift; - $self->BAZ; - } - - sub BAZ { - print "in BAZ\n"; - } - - package GOOP; - @ISA = qw( FOO ); - - sub new { - my $type = shift; - bless {}, $type; - } - sub BAZ { - print "in GOOP::BAZ\n"; - } - - package main; - - $a = GOOP->new; - $a->bar; - -=head1 CLASS CONTEXT AND THE OBJECT - -Use the object to solve package and class context problems. Everything a -method needs should be available via the object or should be passed as a -parameter to the method. - -A class will sometimes have static or global data to be used by the -methods. A subclass may want to override that data and replace it with new -data. When this happens the superclass may not know how to find the new -copy of the data. - -This problem can be solved by using the object to define the context of the -method. Let the method look in the object for a reference to the data. The -alternative is to force the method to go hunting for the data ("Is it in my -class, or in a subclass? Which subclass?"), and this can be inconvenient -and will lead to hackery. It is better just to let the object tell the -method where that data is located. - - package Bar; - - %fizzle = ( 'Password' => 'XYZZY' ); - - sub new { - my $type = shift; - my $self = {}; - $self->{'fizzle'} = \%fizzle; - bless $self, $type; - } - - sub enter { - my $self = shift; - - # Don't try to guess if we should use %Bar::fizzle - # or %Foo::fizzle. The object already knows which - # we should use, so just ask it. - # - my $fizzle = $self->{'fizzle'}; - - print "The word is ", $fizzle->{'Password'}, "\n"; - } - - package Foo; - @ISA = qw( Bar ); - - %fizzle = ( 'Password' => 'Rumple' ); - - sub new { - my $type = shift; - my $self = Bar->new; - $self->{'fizzle'} = \%fizzle; - bless $self, $type; - } - - package main; - - $a = Bar->new; - $b = Foo->new; - $a->enter; - $b->enter; - -=head1 INHERITING A CONSTRUCTOR - -An inheritable constructor should use the second form of bless() which allows -blessing directly into a specified class. Notice in this example that the -object will be a BAR not a FOO, even though the constructor is in class FOO. - - package FOO; - - sub new { - my $type = shift; - my $self = {}; - bless $self, $type; - } - - sub baz { - print "in FOO::baz()\n"; - } - - package BAR; - @ISA = qw(FOO); - - sub baz { - print "in BAR::baz()\n"; - } - - package main; - - $a = BAR->new; - $a->baz; - -=head1 DELEGATION - -Some classes, such as SDBM_File, cannot be effectively subclassed because -they create foreign objects. Such a class can be extended with some sort of -aggregation technique such as the "using" relationship mentioned earlier or -by delegation. - -The following example demonstrates delegation using an AUTOLOAD() function to -perform message-forwarding. This will allow the Mydbm object to behave -exactly like an SDBM_File object. The Mydbm class could now extend the -behavior by adding custom FETCH() and STORE() methods, if this is desired. - - package Mydbm; - - require SDBM_File; - require Tie::Hash; - @ISA = qw(Tie::Hash); - - sub TIEHASH { - my $type = shift; - my $ref = SDBM_File->new(@_); - bless {'delegate' => $ref}; - } - - sub AUTOLOAD { - my $self = shift; - - # The Perl interpreter places the name of the - # message in a variable called $AUTOLOAD. - - # DESTROY messages should never be propagated. - return if $AUTOLOAD =~ /::DESTROY$/; - - # Remove the package name. - $AUTOLOAD =~ s/^Mydbm:://; - - # Pass the message to the delegate. - $self->{'delegate'}->$AUTOLOAD(@_); - } - - package main; - use Fcntl qw( O_RDWR O_CREAT ); - - tie %foo, "Mydbm", "adbm", O_RDWR|O_CREAT, 0640; - $foo{'bar'} = 123; - print "foo-bar = $foo{'bar'}\n"; - -=head1 SEE ALSO - -L, L, L. diff --git a/pod/perltooc.pod b/pod/perltooc.pod deleted file mode 100644 index 06f697c..0000000 --- a/pod/perltooc.pod +++ /dev/null @@ -1,1342 +0,0 @@ -=head1 NAME - -perltooc - Tom's OO Tutorial for Class Data in Perl - -=head1 DESCRIPTION - -When designing an object class, you are sometimes faced with the situation -of wanting common state shared by all objects of that class. -Such I act somewhat like global variables for the entire -class, but unlike program-wide globals, class attributes have meaning only to -the class itself. - -Here are a few examples where class attributes might come in handy: - -=over 4 - -=item * - -to keep a count of the objects you've created, or how many are -still extant. - -=item * - -to extract the name or file descriptor for a logfile used by a debugging -method. - -=item * - -to access collective data, like the total amount of cash dispensed by -all ATMs in a network in a given day. - -=item * - -to access the last object created by a class, or the most accessed object, -or to retrieve a list of all objects. - -=back - -Unlike a true global, class attributes should not be accessed directly. -Instead, their state should be inspected, and perhaps altered, only -through the mediated access of I. These class attributes -accessor methods are similar in spirit and function to accessors used -to manipulate the state of instance attributes on an object. They provide a -clear firewall between interface and implementation. - -You should allow access to class attributes through either the class -name or any object of that class. If we assume that $an_object is of -type Some_Class, and the &Some_Class::population_count method accesses -class attributes, then these two invocations should both be possible, -and almost certainly equivalent. - - Some_Class->population_count() - $an_object->population_count() - -The question is, where do you store the state which that method accesses? -Unlike more restrictive languages like C++, where these are called -static data members, Perl provides no syntactic mechanism to declare -class attributes, any more than it provides a syntactic mechanism to -declare instance attributes. Perl provides the developer with a broad -set of powerful but flexible features that can be uniquely crafted to -the particular demands of the situation. - -A class in Perl is typically implemented in a module. A module consists -of two complementary feature sets: a package for interfacing with the -outside world, and a lexical file scope for privacy. Either of these -two mechanisms can be used to implement class attributes. That means you -get to decide whether to put your class attributes in package variables -or to put them in lexical variables. - -And those aren't the only decisions to make. If you choose to use package -variables, you can make your class attribute accessor methods either ignorant -of inheritance or sensitive to it. If you choose lexical variables, -you can elect to permit access to them from anywhere in the entire file -scope, or you can limit direct data access exclusively to the methods -implementing those attributes. - -=head1 Class Data in a Can - -One of the easiest ways to solve a hard problem is to let someone else -do it for you! In this case, Class::Data::Inheritable (available on a -CPAN near you) offers a canned solution to the class data problem -using closures. So before you wade into this document, consider -having a look at that module. - - -=head1 Class Data as Package Variables - -Because a class in Perl is really just a package, using package variables -to hold class attributes is the most natural choice. This makes it simple -for each class to have its own class attributes. Let's say you have a class -called Some_Class that needs a couple of different attributes that you'd -like to be global to the entire class. The simplest thing to do is to -use package variables like $Some_Class::CData1 and $Some_Class::CData2 -to hold these attributes. But we certainly don't want to encourage -outsiders to touch those data directly, so we provide methods -to mediate access. - -In the accessor methods below, we'll for now just ignore the first -argument--that part to the left of the arrow on method invocation, which -is either a class name or an object reference. - - package Some_Class; - sub CData1 { - shift; # XXX: ignore calling class/object - $Some_Class::CData1 = shift if @_; - return $Some_Class::CData1; - } - sub CData2 { - shift; # XXX: ignore calling class/object - $Some_Class::CData2 = shift if @_; - return $Some_Class::CData2; - } - -This technique is highly legible and should be completely straightforward -to even the novice Perl programmer. By fully qualifying the package -variables, they stand out clearly when reading the code. Unfortunately, -if you misspell one of these, you've introduced an error that's hard -to catch. It's also somewhat disconcerting to see the class name itself -hard-coded in so many places. - -Both these problems can be easily fixed. Just add the C -pragma, then pre-declare your package variables. (The C operator -will be new in 5.6, and will work for package globals just like C -works for scoped lexicals.) - - package Some_Class; - use strict; - our($CData1, $CData2); # our() is new to perl5.6 - 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; - } - - -As with any other global variable, some programmers prefer to start their -package variables with capital letters. This helps clarity somewhat, but -by no longer fully qualifying the package variables, their significance -can be lost when reading the code. You can fix this easily enough by -choosing better names than were used here. - -=head2 Putting All Your Eggs in One Basket - -Just as the mindless enumeration of accessor methods for instance attributes -grows tedious after the first few (see L), so too does the -repetition begin to grate when listing out accessor methods for class -data. Repetition runs counter to the primary virtue of a programmer: -Laziness, here manifesting as that innate urge every programmer feels -to factor out duplicate code whenever possible. - -Here's what to do. First, make just one hash to hold all class attributes. - - package Some_Class; - use strict; - our %ClassData = ( # our() is new to perl5.6 - CData1 => "", - CData2 => "", - ); - -Using closures (see L) and direct access to the package symbol -table (see L), now clone an accessor method for each key in -the %ClassData hash. Each of these methods is used to fetch or store -values to the specific, named class attribute. - - for my $datum (keys %ClassData) { - no strict "refs"; # to register new methods in package - *$datum = sub { - shift; # XXX: ignore calling class/object - $ClassData{$datum} = shift if @_; - return $ClassData{$datum}; - } - } - -It's true that you could work out a solution employing an &AUTOLOAD -method, but this approach is unlikely to prove satisfactory. Your -function would have to distinguish between class attributes and object -attributes; it could interfere with inheritance; and it would have to -careful about DESTROY. Such complexity is uncalled for in most cases, -and certainly in this one. - -You may wonder why we're rescinding strict refs for the loop. We're -manipulating the package's symbol table to introduce new function names -using symbolic references (indirect naming), which the strict pragma -would otherwise forbid. Normally, symbolic references are a dodgy -notion at best. This isn't just because they can be used accidentally -when you aren't meaning to. It's also because for most uses -to which beginning Perl programmers attempt to put symbolic references, -we have much better approaches, like nested hashes or hashes of arrays. -But there's nothing wrong with using symbolic references to manipulate -something that is meaningful only from the perspective of the package -symbol table, like method names or package variables. In other -words, when you want to refer to the symbol table, use symbol references. - -Clustering all the class attributes in one place has several advantages. -They're easy to spot, initialize, and change. The aggregation also -makes them convenient to access externally, such as from a debugger -or a persistence package. The only possible problem is that we don't -automatically know the name of each class's class object, should it have -one. This issue is addressed below in L<"The Eponymous Meta-Object">. - -=head2 Inheritance Concerns - -Suppose you have an instance of a derived class, and you access class -data using an inherited method call. Should that end up referring -to the base class's attributes, or to those in the derived class? -How would it work in the earlier examples? The derived class inherits -all the base class's methods, including those that access class attributes. -But what package are the class attributes stored in? - -The answer is that, as written, class attributes are stored in the package into -which those methods were compiled. When you invoke the &CData1 method -on the name of the derived class or on one of that class's objects, the -version shown above is still run, so you'll access $Some_Class::CData1--or -in the method cloning version, C<$Some_Class::ClassData{CData1}>. - -Think of these class methods as executing in the context of their base -class, not in that of their derived class. Sometimes this is exactly -what you want. If Feline subclasses Carnivore, then the population of -Carnivores in the world should go up when a new Feline is born. -But what if you wanted to figure out how many Felines you have apart -from Carnivores? The current approach doesn't support that. - -You'll have to decide on a case-by-case basis whether it makes any sense -for class attributes to be package-relative. If you want it to be so, -then stop ignoring the first argument to the function. Either it will -be a package name if the method was invoked directly on a class name, -or else it will be an object reference if the method was invoked on an -object reference. In the latter case, the ref() function provides the -class of that object. - - package Some_Class; - sub CData1 { - my $obclass = shift; - my $class = ref($obclass) || $obclass; - my $varname = $class . "::CData1"; - no strict "refs"; # to access package data symbolically - $$varname = shift if @_; - return $$varname; - } - -And then do likewise for all other class attributes (such as CData2, -etc.) that you wish to access as package variables in the invoking package -instead of the compiling package as we had previously. - -Once again we temporarily disable the strict references ban, because -otherwise we couldn't use the fully-qualified symbolic name for -the package global. This is perfectly reasonable: since all package -variables by definition live in a package, there's nothing wrong with -accessing them via that package's symbol table. That's what it's there -for (well, somewhat). - -What about just using a single hash for everything and then cloning -methods? What would that look like? The only difference would be the -closure used to produce new method entries for the class's symbol table. - - no strict "refs"; - *$datum = sub { - my $obclass = shift; - my $class = ref($obclass) || $obclass; - my $varname = $class . "::ClassData"; - $varname->{$datum} = shift if @_; - return $varname->{$datum}; - } - -=head2 The Eponymous Meta-Object - -It could be argued that the %ClassData hash in the previous example is -neither the most imaginative nor the most intuitive of names. Is there -something else that might make more sense, be more useful, or both? - -As it happens, yes, there is. For the "class meta-object", we'll use -a package variable of the same name as the package itself. Within the -scope of a package Some_Class declaration, we'll use the eponymously -named hash %Some_Class as that class's meta-object. (Using an eponymously -named hash is somewhat reminiscent of classes that name their constructors -eponymously in the Python or C++ fashion. That is, class Some_Class would -use &Some_Class::Some_Class as a constructor, probably even exporting that -name as well. The StrNum class in Recipe 13.14 in I -does this, if you're looking for an example.) - -This predictable approach has many benefits, including having a well-known -identifier to aid in debugging, transparent persistence, -or checkpointing. It's also the obvious name for monadic classes and -translucent attributes, discussed later. - -Here's an example of such a class. Notice how the name of the -hash storing the meta-object is the same as the name of the package -used to implement the class. - - package Some_Class; - use strict; - - # create class meta-object using that most perfect of names - our %Some_Class = ( # our() is new to perl5.6 - CData1 => "", - CData2 => "", - ); - - # this accessor is calling-package-relative - sub CData1 { - my $obclass = shift; - my $class = ref($obclass) || $obclass; - no strict "refs"; # to access eponymous meta-object - $class->{CData1} = shift if @_; - return $class->{CData1}; - } - - # but this accessor is not - sub CData2 { - shift; # XXX: ignore calling class/object - no strict "refs"; # to access eponymous meta-object - __PACKAGE__ -> {CData2} = shift if @_; - return __PACKAGE__ -> {CData2}; - } - -In the second accessor method, the __PACKAGE__ notation was used for -two reasons. First, to avoid hardcoding the literal package name -in the code in case we later want to change that name. Second, to -clarify to the reader that what matters here is the package currently -being compiled into, not the package of the invoking object or class. -If the long sequence of non-alphabetic characters bothers you, you can -always put the __PACKAGE__ in a variable first. - - sub CData2 { - shift; # XXX: ignore calling class/object - no strict "refs"; # to access eponymous meta-object - my $class = __PACKAGE__; - $class->{CData2} = shift if @_; - return $class->{CData2}; - } - -Even though we're using symbolic references for good not evil, some -folks tend to become unnerved when they see so many places with strict -ref checking disabled. Given a symbolic reference, you can always -produce a real reference (the reverse is not true, though). So we'll -create a subroutine that does this conversion for us. If invoked as a -function of no arguments, it returns a reference to the compiling class's -eponymous hash. Invoked as a class method, it returns a reference to -the eponymous hash of its caller. And when invoked as an object method, -this function returns a reference to the eponymous hash for whatever -class the object belongs to. - - package Some_Class; - use strict; - - our %Some_Class = ( # our() is new to perl5.6 - CData1 => "", - CData2 => "", - ); - - # tri-natured: function, class method, or object method - sub _classobj { - my $obclass = shift || __PACKAGE__; - my $class = ref($obclass) || $obclass; - no strict "refs"; # to convert sym ref to real one - return \%$class; - } - - for my $datum (keys %{ _classobj() } ) { - # turn off strict refs so that we can - # register a method in the symbol table - no strict "refs"; - *$datum = sub { - use strict "refs"; - my $self = shift->_classobj(); - $self->{$datum} = shift if @_; - return $self->{$datum}; - } - } - -=head2 Indirect References to Class Data - -A reasonably common strategy for handling class attributes is to store -a reference to each package variable on the object itself. This is -a strategy you've probably seen before, such as in L and -L, but there may be variations in the example below that you -haven't thought of before. - - package Some_Class; - our($CData1, $CData2); # our() is new to perl5.6 - - sub new { - my $obclass = shift; - return bless my $self = { - ObData1 => "", - ObData2 => "", - CData1 => \$CData1, - CData2 => \$CData2, - } => (ref $obclass || $obclass); - } - - sub ObData1 { - my $self = shift; - $self->{ObData1} = shift if @_; - return $self->{ObData1}; - } - - sub ObData2 { - my $self = shift; - $self->{ObData2} = shift if @_; - return $self->{ObData2}; - } - - sub CData1 { - my $self = shift; - my $dataref = ref $self - ? $self->{CData1} - : \$CData1; - $$dataref = shift if @_; - return $$dataref; - } - - sub CData2 { - my $self = shift; - my $dataref = ref $self - ? $self->{CData2} - : \$CData2; - $$dataref = shift if @_; - return $$dataref; - } - -As written above, a derived class will inherit these methods, which -will consequently access package variables in the base class's package. -This is not necessarily expected behavior in all circumstances. Here's an -example that uses a variable meta-object, taking care to access the -proper package's data. - - package Some_Class; - use strict; - - our %Some_Class = ( # our() is new to perl5.6 - CData1 => "", - CData2 => "", - ); - - sub _classobj { - my $self = shift; - my $class = ref($self) || $self; - no strict "refs"; - # get (hard) ref to eponymous meta-object - return \%$class; - } - - sub new { - my $obclass = shift; - my $classobj = $obclass->_classobj(); - bless my $self = { - ObData1 => "", - ObData2 => "", - CData1 => \$classobj->{CData1}, - CData2 => \$classobj->{CData2}, - } => (ref $obclass || $obclass); - return $self; - } - - sub ObData1 { - my $self = shift; - $self->{ObData1} = shift if @_; - return $self->{ObData1}; - } - - sub ObData2 { - my $self = shift; - $self->{ObData2} = shift if @_; - return $self->{ObData2}; - } - - sub CData1 { - my $self = shift; - $self = $self->_classobj() unless ref $self; - my $dataref = $self->{CData1}; - $$dataref = shift if @_; - return $$dataref; - } - - sub CData2 { - my $self = shift; - $self = $self->_classobj() unless ref $self; - my $dataref = $self->{CData2}; - $$dataref = shift if @_; - return $$dataref; - } - -Not only are we now strict refs clean, using an eponymous meta-object -seems to make the code cleaner. Unlike the previous version, this one -does something interesting in the face of inheritance: it accesses the -class meta-object in the invoking class instead of the one into which -the method was initially compiled. - -You can easily access data in the class meta-object, making -it easy to dump the complete class state using an external mechanism such -as when debugging or implementing a persistent class. This works because -the class meta-object is a package variable, has a well-known name, and -clusters all its data together. (Transparent persistence -is not always feasible, but it's certainly an appealing idea.) - -There's still no check that object accessor methods have not been -invoked on a class name. If strict ref checking is enabled, you'd -blow up. If not, then you get the eponymous meta-object. What you do -with--or about--this is up to you. The next two sections demonstrate -innovative uses for this powerful feature. - -=head2 Monadic Classes - -Some of the standard modules shipped with Perl provide class interfaces -without any attribute methods whatsoever. The most commonly used module -not numbered amongst the pragmata, the Exporter module, is a class with -neither constructors nor attributes. Its job is simply to provide a -standard interface for modules wishing to export part of their namespace -into that of their caller. Modules use the Exporter's &import method by -setting their inheritance list in their package's @ISA array to mention -"Exporter". But class Exporter provides no constructor, so you can't -have several instances of the class. In fact, you can't have any--it -just doesn't make any sense. All you get is its methods. Its interface -contains no statefulness, so state data is wholly superfluous. - -Another sort of class that pops up from time to time is one that supports -a unique instance. Such classes are called I, or less -formally, I or I. - -If a class is monadic, where do you store its state, that is, -its attributes? How do you make sure that there's never more than -one instance? While you could merely use a slew of package variables, -it's a lot cleaner to use the eponymously named hash. Here's a complete -example of a monadic class: - - package Cosmos; - %Cosmos = (); - - # accessor method for "name" attribute - sub name { - my $self = shift; - $self->{name} = shift if @_; - return $self->{name}; - } - - # read-only accessor method for "birthday" attribute - sub birthday { - my $self = shift; - die "can't reset birthday" if @_; # XXX: croak() is better - return $self->{birthday}; - } - - # accessor method for "stars" attribute - sub stars { - my $self = shift; - $self->{stars} = shift if @_; - return $self->{stars}; - } - - # oh my - one of our stars just went out! - sub supernova { - my $self = shift; - my $count = $self->stars(); - $self->stars($count - 1) if $count > 0; - } - - # constructor/initializer method - fix by reboot - sub bigbang { - my $self = shift; - %$self = ( - name => "the world according to tchrist", - birthday => time(), - stars => 0, - ); - return $self; # yes, it's probably a class. SURPRISE! - } - - # After the class is compiled, but before any use or require - # returns, we start off the universe with a bang. - __PACKAGE__ -> bigbang(); - -Hold on, that doesn't look like anything special. Those attribute -accessors look no different than they would if this were a regular class -instead of a monadic one. The crux of the matter is there's nothing -that says that $self must hold a reference to a blessed object. It merely -has to be something you can invoke methods on. Here the package name -itself, Cosmos, works as an object. Look at the &supernova method. Is that -a class method or an object method? The answer is that static analysis -cannot reveal the answer. Perl doesn't care, and neither should you. -In the three attribute methods, C<%$self> is really accessing the %Cosmos -package variable. - -If like Stephen Hawking, you posit the existence of multiple, sequential, -and unrelated universes, then you can invoke the &bigbang method yourself -at any time to start everything all over again. You might think of -&bigbang as more of an initializer than a constructor, since the function -doesn't allocate new memory; it only initializes what's already there. -But like any other constructor, it does return a scalar value to use -for later method invocations. - -Imagine that some day in the future, you decide that one universe just -isn't enough. You could write a new class from scratch, but you already -have an existing class that does what you want--except that it's monadic, -and you want more than just one cosmos. - -That's what code reuse via subclassing is all about. Look how short -the new code is: - - package Multiverse; - use Cosmos; - @ISA = qw(Cosmos); - - sub new { - my $protoverse = shift; - my $class = ref($protoverse) || $protoverse; - my $self = {}; - return bless($self, $class)->bigbang(); - } - 1; - -Because we were careful to be good little creators when we designed our -Cosmos class, we can now reuse it without touching a single line of code -when it comes time to write our Multiverse class. The same code that -worked when invoked as a class method continues to work perfectly well -when invoked against separate instances of a derived class. - -The astonishing thing about the Cosmos class above is that the value -returned by the &bigbang "constructor" is not a reference to a blessed -object at all. It's just the class's own name. A class name is, for -virtually all intents and purposes, a perfectly acceptable object. -It has state, behavior, and identity, the three crucial components -of an object system. It even manifests inheritance, polymorphism, -and encapsulation. And what more can you ask of an object? - -To understand object orientation in Perl, it's important to recognize the -unification of what other programming languages might think of as class -methods and object methods into just plain methods. "Class methods" -and "object methods" are distinct only in the compartmentalizing mind -of the Perl programmer, not in the Perl language itself. - -Along those same lines, a constructor is nothing special either, which -is one reason why Perl has no pre-ordained name for them. "Constructor" -is just an informal term loosely used to describe a method that returns -a scalar value that you can make further method calls against. So long -as 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. - -You can have as many--or as few--constructors as you want, and you can -name them whatever you care to. Blindly and obediently using new() -for each and every constructor you ever write is to speak Perl with -such 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 new(), or that a constructor be -used solely as a class method and not an object method. - -The next section shows how useful it can be to further distance ourselves -from any formal distinction between class method calls and object method -calls, both in constructors and in accessor methods. - -=head2 Translucent Attributes - -A package's eponymous hash can be used for more than just containing -per-class, global state data. It can also serve as a sort of template -containing default settings for object attributes. These default -settings can then be used in constructors for initialization of a -particular object. The class's eponymous hash can also be used to -implement I. A translucent attribute is one -that has a class-wide default. Each object can set its own value for the -attribute, in which case C<< $object->attribute() >> returns that value. -But if no value has been set, then C<< $object->attribute() >> returns -the class-wide default. - -We'll apply something of a copy-on-write approach to these translucent -attributes. If you're just fetching values from them, you get -translucency. But if you store a new value to them, that new value is -set on the current object. On the other hand, if you use the class as -an object and store the attribute value directly on the class, then the -meta-object's value changes, and later fetch operations on objects with -uninitialized values for those attributes will retrieve the meta-object's -new values. Objects with their own initialized values, however, won't -see any change. - -Let's look at some concrete examples of using these properties before we -show how to implement them. Suppose that a class named Some_Class -had a translucent data attribute called "color". First you set the color -in the meta-object, then you create three objects using a constructor -that happens to be named &spawn. - - use Vermin; - Vermin->color("vermilion"); - - $ob1 = Vermin->spawn(); # so that's where Jedi come from - $ob2 = Vermin->spawn(); - $ob3 = Vermin->spawn(); - - print $obj3->color(); # prints "vermilion" - -Each of these objects' colors is now "vermilion", because that's the -meta-object's value for that attribute, and these objects do not have -individual color values set. - -Changing the attribute on one object has no effect on other objects -previously created. - - $ob3->color("chartreuse"); - print $ob3->color(); # prints "chartreuse" - print $ob1->color(); # prints "vermilion", translucently - -If you now use $ob3 to spawn off another object, the new object will -take the color its parent held, which now happens to be "chartreuse". -That's because the constructor uses the invoking object as its template -for initializing attributes. When that invoking object is the -class 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. - - $ob4 = $ob3->spawn(); # $ob3 now template, not %Vermin - print $ob4->color(); # prints "chartreuse" - -Any actual values set on the template object will be copied to the -new object. But attributes undefined in the template object, being -translucent, will remain undefined and consequently translucent in the -new one as well. - -Now let's change the color attribute on the entire class: - - Vermin->color("azure"); - print $ob1->color(); # prints "azure" - print $ob2->color(); # prints "azure" - print $ob3->color(); # prints "chartreuse" - print $ob4->color(); # prints "chartreuse" - -That color change took effect only in the first pair of objects, which -were still translucently accessing the meta-object's values. The second -pair had per-object initialized colors, and so didn't change. - -One important question remains. Changes to the meta-object are reflected -in translucent attributes in the entire class, but what about -changes to discrete objects? If you change the color of $ob3, does the -value of $ob4 see that change? Or vice-versa. If you change the color -of $ob4, does then the value of $ob3 shift? - - $ob3->color("amethyst"); - print $ob3->color(); # prints "amethyst" - print $ob4->color(); # hmm: "chartreuse" or "amethyst"? - -While one could argue that in certain rare cases it should, let's not -do that. Good taste aside, we want the answer to the question posed in -the comment above to be "chartreuse", not "amethyst". So we'll treat -these attributes similar to the way process attributes like environment -variables, user and group IDs, or the current working directory are -treated across a fork(). You can change only yourself, but you will see -those changes reflected in your unspawned children. Changes to one object -will propagate neither up to the parent nor down to any existing child objects. -Those objects made later, however, will see the changes. - -If you have an object with an actual attribute value, and you want to -make 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 -C as its argument, that attribute returns to translucency. - - $ob4->color(undef); # back to "azure" - -Here's a complete implementation of Vermin as described above. - - package Vermin; - - # here's 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}--; - } - -Here are a couple of helper methods that might be convenient. They aren't -accessor methods at all. They're used to detect accessibility of data -attributes. The &is_translucent method determines whether a particular -object attribute is coming from the meta-object. The &has_attribute -method detects whether a class implements a particular property at all. -It could also be used to distinguish undefined properties from non-existent -ones. - - # 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}; - } - -If you prefer to install your accessors more generically, you can make -use of the upper-case versus lower-case convention to register into the -package appropriate methods cloned from generic closures. - - 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} - } - } - -Translations of this closure-based approach into C++, Java, and Python -have been left as exercises for the reader. Be sure to send us mail as -soon as you're done. - -=head1 Class Data as Lexical Variables - -=head2 Privacy and Responsibility - -Unlike conventions used by some Perl programmers, in the previous -examples, we didn't prefix the package variables used for class attributes -with an underscore, nor did we do so for the names of the hash keys used -for instance attributes. You don't need little markers on data names to -suggest nominal privacy on attribute variables or hash keys, because these -are B notionally private! Outsiders have no business whatsoever -playing with anything within a class save through the mediated access of -its documented interface; in other words, through method invocations. -And not even through just any method, either. Methods that begin with -an underscore are traditionally considered off-limits outside the class. -If outsiders skip the documented method interface to poke around the -internals of your class and end up breaking something, that's not your -fault--it's theirs. - -Perl believes in individual responsibility rather than mandated control. -Perl respects you enough to let you choose your own preferred level of -pain, or of pleasure. Perl believes that you are creative, intelligent, -and capable of making your own decisions--and fully expects you to -take 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't -forget careful, and that's a good bit harder to expect. Even Einstein -would take wrong turns by accident and end up lost in the wrong part -of town. - -Some folks get the heebie-jeebies when they see package variables -hanging out there for anyone to reach over and alter them. Some folks -live 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 cautious -types are also afraid that they or others will do something not so -much wicked as careless, whether by accident or out of desperation. -If we fire everyone who ever gets careless, pretty soon there won't be -anybody left to get any work done. - -Whether it's needless paranoia or sensible caution, this uneasiness can -be a problem for some people. We can take the edge off their discomfort -by providing the option of storing class attributes as lexical variables -instead of as package variables. The my() operator is the source of -all privacy in Perl, and it is a powerful form of privacy indeed. - -It is widely perceived, and indeed has often been written, that Perl -provides no data hiding, that it affords the class designer no privacy -nor isolation, merely a rag-tag assortment of weak and unenforceable -social conventions instead. This perception is demonstrably false and -easily disproven. In the next section, we show how to implement forms -of privacy that are far stronger than those provided in nearly any -other object-oriented language. - -=head2 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 code -residing textually below the my() operator through the end of its block -if it has one, or through the end of the current file if it doesn't. - -Starting again with our simplest example given at the start of this -document, we replace our() variables with my() versions. - - 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; - } - -So much for that old $Some_Class::CData1 package variable and its brethren! -Those are gone now, replaced with lexicals. No one outside the -scope can reach in and alter the class state without resorting to the -documented interface. Not even subclasses or superclasses of -this one have unmediated access to $CData1. They have to invoke the &CData1 -method against Some_Class or an instance thereof, just like anybody else. - -To be scrupulously honest, that last statement assumes you haven't packed -several classes together into the same file scope, nor strewn your class -implementation across several different files. Accessibility of those -variables is based uniquely on the static file scope. It has nothing to -do with the package. That means that code in a different file but -the same package (class) could not access those variables, yet code in the -same file but a different package (class) could. There are sound reasons -why we usually suggest a one-to-one mapping between files and packages -and modules and classes. You don't have to stick to this suggestion if -you really know what you're doing, but you're apt to confuse yourself -otherwise, especially at first. - -If you'd like to aggregate your class attributes into one lexically scoped, -composite structure, you're perfectly free to do so. - - package Some_Class; - my %ClassData = ( - CData1 => "", - CData2 => "", - ); - sub CData1 { - shift; # XXX: ignore calling class/object - $ClassData{CData1} = shift if @_; - return $ClassData{CData1}; - } - sub CData2 { - shift; # XXX: ignore calling class/object - $ClassData{CData2} = shift if @_; - return $ClassData{CData2}; - } - -To make this more scalable as other class attributes are added, we can -again register closures into the package symbol table to create accessor -methods for them. - - package Some_Class; - my %ClassData = ( - CData1 => "", - CData2 => "", - ); - for my $datum (keys %ClassData) { - no strict "refs"; - *$datum = sub { - shift; # XXX: ignore calling class/object - $ClassData{$datum} = shift if @_; - return $ClassData{$datum}; - }; - } - -Requiring even your own class to use accessor methods like anybody else is -probably a good thing. But demanding and expecting that everyone else, -be they subclass or superclass, friend or foe, will all come to your -object through mediation is more than just a good idea. It's absolutely -critical to the model. Let there be in your mind no such thing as -"public" data, nor even "protected" data, which is a seductive but -ultimately destructive notion. Both will come back to bite at you. -That's because as soon as you take that first step out of the solid -position in which all state is considered completely private, save from the -perspective of its own accessor methods, you have violated the envelope. -And, having pierced that encapsulating envelope, you shall doubtless -someday pay the price when future changes in the implementation break -unrelated code. Considering that avoiding this infelicitous outcome was -precisely why you consented to suffer the slings and arrows of obsequious -abstraction by turning to object orientation in the first place, such -breakage seems unfortunate in the extreme. - -=head2 More Inheritance Concerns - -Suppose that Some_Class were used as a base class from which to derive -Another_Class. If you invoke a &CData method on the derived class or -on an object of that class, what do you get? Would the derived class -have its own state, or would it piggyback on its base class's versions -of the class attributes? - -The answer is that under the scheme outlined above, the derived class -would B have its own state data. As before, whether you consider -this a good thing or a bad one depends on the semantics of the classes -involved. - -The cleanest, sanest, simplest way to address per-class state in a -lexical is for the derived class to override its base class's version -of the method that accesses the class attributes. Since the actual method -called is the one in the object's derived class if this exists, you -automatically get per-class state this way. Any urge to provide an -unadvertised method to sneak out a reference to the %ClassData hash -should be strenuously resisted. - -As with any other overridden method, the implementation in the -derived class always has the option of invoking its base class's -version of the method in addition to its own. Here's an example: - - package Another_Class; - @ISA = qw(Some_Class); - - my %ClassData = ( - CData1 => "", - ); - - sub CData1 { - my($self, $newvalue) = @_; - if (@_ > 1) { - # set locally first - $ClassData{CData1} = $newvalue; - - # then pass the buck up to the first - # overridden version, if there is one - if ($self->can("SUPER::CData1")) { - $self->SUPER::CData1($newvalue); - } - } - return $ClassData{CData1}; - } - -Those dabbling in multiple inheritance might be concerned -about there being more than one override. - - for my $parent (@ISA) { - my $methname = $parent . "::CData1"; - if ($self->can($methname)) { - $self->$methname($newvalue); - } - } - -Because the &UNIVERSAL::can method returns a reference -to the function directly, you can use this directly -for a significant performance improvement: - - for my $parent (@ISA) { - if (my $coderef = $self->can($parent . "::CData1")) { - $self->$coderef($newvalue); - } - } - -If you override C in your own classes, be sure to return the -reference appropriately. - -=head2 Locking the Door and Throwing Away the Key - -As currently implemented, any code within the same scope as the -file-scoped lexical %ClassData can alter that hash directly. Is that -ok? Is it acceptable or even desirable to allow other parts of the -implementation of this class to access class attributes directly? - -That depends on how careful you want to be. Think back to the Cosmos -class. If the &supernova method had directly altered $Cosmos::Stars or -C<$Cosmos::Cosmos{stars}>, then we wouldn't have been able to reuse the -class when it came to inventing a Multiverse. So letting even the class -itself access its own class attributes without the mediating intervention of -properly designed accessor methods is probably not a good idea after all. - -Restricting access to class attributes from the class itself is usually -not enforceable even in strongly object-oriented languages. But in Perl, -you can. - -Here's one way: - - package Some_Class; - - { # scope for hiding $CData1 - my $CData1; - sub CData1 { - shift; # XXX: unused - $CData1 = shift if @_; - return $CData1; - } - } - - { # scope for hiding $CData2 - my $CData2; - sub CData2 { - shift; # XXX: unused - $CData2 = shift if @_; - return $CData2; - } - } - -No one--absolutely no one--is allowed to read or write the class -attributes without the mediation of the managing accessor method, since -only that method has access to the lexical variable it's managing. -This use of mediated access to class attributes is a form of privacy far -stronger than most OO languages provide. - -The repetition of code used to create per-datum accessor methods chafes -at our Laziness, so we'll again use closures to create similar -methods. - - package Some_Class; - - { # scope for ultra-private meta-object for class attributes - my %ClassData = ( - CData1 => "", - CData2 => "", - ); - - for my $datum (keys %ClassData ) { - no strict "refs"; - *$datum = sub { - use strict "refs"; - my ($self, $newvalue) = @_; - $ClassData{$datum} = $newvalue if @_ > 1; - return $ClassData{$datum}; - } - } - - } - -The closure above can be modified to take inheritance into account using -the &UNIVERSAL::can method and SUPER as shown previously. - -=head2 Translucency Revisited - -The Vermin class demonstrates translucency using a package variable, -eponymously named %Vermin, as its meta-object. If you prefer to -use absolutely no package variables beyond those necessary to appease -inheritance or possibly the Exporter, this strategy is closed to you. -That's too bad, because translucent attributes are an appealing -technique, so it would be valuable to devise an implementation using -only lexicals. - -There's a second reason why you might wish to avoid the eponymous -package hash. If you use class names with double-colons in them, you -would end up poking around somewhere you might not have meant to poke. - - package Vermin; - $class = "Vermin"; - $class->{PopCount}++; - # accesses $Vermin::Vermin{PopCount} - - package Vermin::Noxious; - $class = "Vermin::Noxious"; - $class->{PopCount}++; - # accesses $Vermin::Noxious{PopCount} - -In the first case, because the class name had no double-colons, we got -the hash in the current package. But in the second case, instead of -getting some hash in the current package, we got the hash %Noxious in -the Vermin package. (The noxious vermin just invaded another package and -sprayed their data around it. :-) Perl doesn't support relative packages -in its naming conventions, so any double-colons trigger a fully-qualified -lookup instead of just looking in the current package. - -In practice, it is unlikely that the Vermin class had an existing -package variable named %Noxious that you just blew away. If you're -still mistrustful, you could always stake out your own territory -where you know the rules, such as using Eponymous::Vermin::Noxious or -Hieronymus::Vermin::Boschious or Leave_Me_Alone::Vermin::Noxious as class -names instead. Sure, it's in theory possible that someone else has -a class named Eponymous::Vermin with its own %Noxious hash, but this -kind of thing is always true. There's no arbiter of package names. -It's always the case that globals like @Cwd::ISA would collide if more -than one class uses the same Cwd package. - -If this still leaves you with an uncomfortable twinge of paranoia, -we have another solution for you. There's nothing that says that you -have to have a package variable to hold a class meta-object, either for -monadic classes or for translucent attributes. Just code up the methods -so that they access a lexical instead. - -Here's another implementation of the Vermin class with semantics identical -to those given previously, but this time using no package variables. - - package Vermin; - - - # Here's the class meta-object, eponymously named. - # It holds all class data, and also all instance data - # so the latter can be used for both initialization - # and translucency. it's a template. - my %ClassData = ( - 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); - $ClassData{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; - - # handle class invocation - unless (ref $self) { - $ClassData{color} = shift if @_; - return $ClassData{color} - } - - # handle object invocation - $self->{color} = shift if @_; - if (defined $self->{color}) { # not exists! - return $self->{color}; - } else { - return $ClassData{color}; - } - } - - # class attribute accessor for "PopCount" attribute - # invoked as class method or object method - sub population { - return $ClassData{PopCount}; - } - - # instance destructor; invoked only as object method - sub DESTROY { - $ClassData{PopCount}--; - } - - # detect whether an object attribute is translucent - # (typically?) invoked only as object method - sub is_translucent { - my($self, $attr) = @_; - $self = \%ClassData if !ref $self; - return !defined $self->{$attr}; - } - - # test for presence of attribute in class - # invoked as class method or object method - sub has_attribute { - my($self, $attr) = @_; - return exists $ClassData{$attr}; - } - -=head1 NOTES - -Inheritance is a powerful but subtle device, best used only after careful -forethought and design. Aggregation instead of inheritance is often a -better approach. - -You can't use file-scoped lexicals in conjunction with the SelfLoader -or the AutoLoader, because they alter the lexical scope in which the -module's methods wind up getting compiled. - -The usual mealy-mouthed package-munging doubtless applies to setting -up names of object attributes. For example, C<< $self->{ObData1} >> -should probably be C<< $self->{ __PACKAGE__ . "_ObData1" } >>, but that -would just confuse the examples. - -=head1 SEE ALSO - -L, L, L, and L. - -The Tie::SecureHash and Class::Data::Inheritable modules from CPAN are -worth checking out. - -=head1 AUTHOR AND COPYRIGHT - -Copyright (c) 1999 Tom Christiansen. -All rights reserved. - -This documentation is free; you can redistribute it and/or modify it -under the same terms as Perl itself. - -Irrespective of its distribution, all code examples in this file -are hereby placed into the public domain. You are permitted and -encouraged to use this code in your own programs for fun -or for profit as you see fit. A simple comment in the code giving -credit would be courteous but is not required. - -=head1 ACKNOWLEDGEMENTS - -Russ Allbery, Jon Orwant, Randy Ray, Larry Rosler, Nat Torkington, -and Stephen Warren all contributed suggestions and corrections to this -piece. Thanks especially to Damian Conway for his ideas and feedback, -and without whose indirect prodding I might never have taken the time -to show others how much Perl has to offer in the way of objects once -you start thinking outside the tiny little box that today's "popular" -object-oriented languages enforce. - -=head1 HISTORY - -Last edit: Sun Feb 4 20:50:28 EST 2001 diff --git a/pod/perltoot.pod b/pod/perltoot.pod deleted file mode 100644 index 0d8648b..0000000 --- a/pod/perltoot.pod +++ /dev/null @@ -1,1836 +0,0 @@ -=head1 NAME - -perltoot - Tom's object-oriented tutorial for perl - -=head1 DESCRIPTION - -Object-oriented programming is a big seller these days. Some managers -would rather have objects than sliced bread. Why is that? What's so -special about an object? Just what I an object anyway? - -An object is nothing but a way of tucking away complex behaviours into -a neat little easy-to-use bundle. (This is what professors call -abstraction.) Smart people who have nothing to do but sit around for -weeks on end figuring out really hard problems make these nifty -objects that even regular people can use. (This is what professors call -software reuse.) Users (well, programmers) can play with this little -bundle all they want, but they aren't to open it up and mess with the -insides. Just like an expensive piece of hardware, the contract says -that you void the warranty if you muck with the cover. So don't do that. - -The heart of objects is the class, a protected little private namespace -full of data and functions. A class is a set of related routines that -addresses some problem area. You can think of it as a user-defined type. -The Perl package mechanism, also used for more traditional modules, -is used for class modules as well. Objects "live" in a class, meaning -that they belong to some package. - -More often than not, the class provides the user with little bundles. -These bundles are objects. They know whose class they belong to, -and how to behave. Users ask the class to do something, like "give -me an object." Or they can ask one of these objects to do something. -Asking a class to do something for you is calling a I. -Asking an object to do something for you is calling an I. -Asking either a class (usually) or an object (sometimes) to give you -back an object is calling a I, which is just a -kind of method. - -That's all well and good, but how is an object different from any other -Perl data type? Just what is an object I; that is, what's its -fundamental type? The answer to the first question is easy. An object -is different from any other data type in Perl in one and only one way: -you may dereference it using not merely string or numeric subscripts -as with simple arrays and hashes, but with named subroutine calls. -In a word, with I. - -The answer to the second question is that it's a reference, and not just -any reference, mind you, but one whose referent has been I()ed -into a particular class (read: package). What kind of reference? Well, -the answer to that one is a bit less concrete. That's because in Perl -the designer of the class can employ any sort of reference they'd like -as the underlying intrinsic data type. It could be a scalar, an array, -or a hash reference. It could even be a code reference. But because -of its inherent flexibility, an object is usually a hash reference. - -=head1 Creating a Class - -Before you create a class, you need to decide what to name it. That's -because the class (package) name governs the name of the file used to -house it, just as with regular modules. Then, that class (package) -should provide one or more ways to generate objects. Finally, it should -provide mechanisms to allow users of its objects to indirectly manipulate -these objects from a distance. - -For example, let's make a simple Person class module. It gets stored in -the file Person.pm. If it were called a Happy::Person class, it would -be stored in the file Happy/Person.pm, and its package would become -Happy::Person instead of just Person. (On a personal computer not -running Unix or Plan 9, but something like Mac OS or VMS, the directory -separator may be different, but the principle is the same.) Do not assume -any formal relationship between modules based on their directory names. -This is merely a grouping convenience, and has no effect on inheritance, -variable accessibility, or anything else. - -For this module we aren't going to use Exporter, because we're -a well-behaved class module that doesn't export anything at all. -In order to manufacture objects, a class needs to have a I. A constructor gives you back not just a regular data type, -but a brand-new object in that class. This magic is taken care of by -the bless() function, whose sole purpose is to enable its referent to -be used as an object. Remember: being an object really means nothing -more than that methods may now be called against it. - -While a constructor may be named anything you'd like, most Perl -programmers seem to like to call theirs new(). However, new() is not -a reserved word, and a class is under no obligation to supply such. -Some programmers have also been known to use a function with -the same name as the class as the constructor. - -=head2 Object Representation - -By far the most common mechanism used in Perl to represent a Pascal -record, a C struct, or a C++ class is an anonymous hash. That's because a -hash has an arbitrary number of data fields, each conveniently accessed by -an arbitrary name of your own devising. - -If you were just doing a simple -struct-like emulation, you would likely go about it something like this: - - $rec = { - name => "Jason", - age => 23, - peers => [ "Norbert", "Rhys", "Phineas"], - }; - -If you felt like it, you could add a bit of visual distinction -by up-casing the hash keys: - - $rec = { - NAME => "Jason", - AGE => 23, - PEERS => [ "Norbert", "Rhys", "Phineas"], - }; - -And so you could get at C<< $rec->{NAME} >> to find "Jason", or -C<< @{ $rec->{PEERS} } >> to get at "Norbert", "Rhys", and "Phineas". -(Have you ever noticed how many 23-year-old programmers seem to -be named "Jason" these days? :-) - -This same model is often used for classes, although it is not considered -the pinnacle of programming propriety for folks from outside the -class to come waltzing into an object, brazenly accessing its data -members directly. Generally speaking, an object should be considered -an opaque cookie that you use I to access. Visually, -methods look like you're dereffing a reference using a function name -instead of brackets or braces. - -=head2 Class Interface - -Some languages provide a formal syntactic interface to a class's methods, -but Perl does not. It relies on you to read the documentation of each -class. If you try to call an undefined method on an object, Perl won't -complain, but the program will trigger an exception while it's running. -Likewise, if you call a method expecting a prime number as its argument -with a non-prime one instead, you can't expect the compiler to catch this. -(Well, you can expect it all you like, but it's not going to happen.) - -Let's suppose you have a well-educated user of your Person class, -someone who has read the docs that explain the prescribed -interface. Here's how they might use the Person class: - - use Person; - - $him = Person->new(); - $him->name("Jason"); - $him->age(23); - $him->peers( "Norbert", "Rhys", "Phineas" ); - - push @All_Recs, $him; # save object in array for later - - printf "%s is %d years old.\n", $him->name, $him->age; - print "His peers are: ", join(", ", $him->peers), "\n"; - - printf "Last rec's name is %s\n", $All_Recs[-1]->name; - -As you can see, the user of the class doesn't know (or at least, has no -business paying attention to the fact) that the object has one particular -implementation or another. The interface to the class and its objects -is exclusively via methods, and that's all the user of the class should -ever play with. - -=head2 Constructors and Instance Methods - -Still, I has to know what's in the object. And that someone is -the class. It implements methods that the programmer uses to access -the object. Here's how to implement the Person class using the standard -hash-ref-as-an-object idiom. We'll make a class method called new() to -act as the constructor, and three object methods called name(), age(), and -peers() to get at per-object data hidden away in our anonymous hash. - - package Person; - use strict; - - ################################################## - ## the object constructor (simplistic version) ## - ################################################## - sub new { - my $self = {}; - $self->{NAME} = undef; - $self->{AGE} = undef; - $self->{PEERS} = []; - bless($self); # but see below - return $self; - } - - ############################################## - ## methods to access per-object data ## - ## ## - ## With args, they set the value. Without ## - ## any, they only retrieve it/them. ## - ############################################## - - 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 succeeds - -We've created three methods to access an object's data, name(), age(), -and peers(). These are all substantially similar. If called with an -argument, they set the appropriate field; otherwise they return the -value held by that field, meaning the value of that hash key. - -=head2 Planning for the Future: Better Constructors - -Even though at this point you may not even know what it means, someday -you're going to worry about inheritance. (You can safely ignore this -for now and worry about it later if you'd like.) To ensure that this -all works out smoothly, you must use the double-argument form of bless(). -The second argument is the class into which the referent will be blessed. -By not assuming our own class as the default second argument and instead -using the class passed into us, we make our constructor inheritable. - - sub new { - my $class = shift; - my $self = {}; - $self->{NAME} = undef; - $self->{AGE} = undef; - $self->{PEERS} = []; - bless ($self, $class); - return $self; - } - -That's about all there is for constructors. These methods bring objects -to life, returning neat little opaque bundles to the user to be used in -subsequent method calls. - -=head2 Destructors - -Every story has a beginning and an end. The beginning of the object's -story is its constructor, explicitly called when the object comes into -existence. But the ending of its story is the I, a method -implicitly called when an object leaves this life. Any per-object -clean-up code is placed in the destructor, which must (in Perl) be called -DESTROY. - -If constructors can have arbitrary names, then why not destructors? -Because while a constructor is explicitly called, a destructor is not. -Destruction happens automatically via Perl's garbage collection (GC) -system, which is a quick but somewhat lazy reference-based GC system. -To know what to call, Perl insists that the destructor be named DESTROY. -Perl's notion of the right time to call a destructor is not well-defined -currently, which is why your destructors should not rely on when they are -called. - -Why is DESTROY in all caps? Perl on occasion uses purely uppercase -function names as a convention to indicate that the function will -be automatically called by Perl in some way. Others that are called -implicitly include BEGIN, END, AUTOLOAD, plus all methods used by -tied objects, described in L. - -In really good object-oriented programming languages, the user doesn't -care when the destructor is called. It just happens when it's supposed -to. In low-level languages without any GC at all, there's no way to -depend on this happening at the right time, so the programmer must -explicitly call the destructor to clean up memory and state, crossing -their fingers that it's the right time to do so. Unlike C++, an -object destructor is nearly never needed in Perl, and even when it is, -explicit invocation is uncalled for. In the case of our Person class, -we don't need a destructor because Perl takes care of simple matters -like memory deallocation. - -The only situation where Perl's reference-based GC won't work is -when there's a circularity in the data structure, such as: - - $this->{WHATEVER} = $this; - -In that case, you must delete the self-reference manually if you expect -your program not to leak memory. While admittedly error-prone, this is -the best we can do right now. Nonetheless, rest assured that when your -program is finished, its objects' destructors are all duly called. -So you are guaranteed that an object I gets properly -destroyed, except in the unique case of a program that never exits. -(If you're running Perl embedded in another application, this full GC -pass happens a bit more frequently--whenever a thread shuts down.) - -=head2 Other Object Methods - -The methods we've talked about so far have either been constructors or -else simple "data methods", interfaces to data stored in the object. -These are a bit like an object's data members in the C++ world, except -that strangers don't access them as data. Instead, they should only -access the object's data indirectly via its methods. This is an -important rule: in Perl, access to an object's data should I -be made through methods. - -Perl doesn't impose restrictions on who gets to use which methods. -The public-versus-private distinction is by convention, not syntax. -(Well, unless you use the Alias module described below in -L.) Occasionally you'll see method names beginning or ending -with an underscore or two. This marking is a convention indicating -that the methods are private to that class alone and sometimes to its -closest acquaintances, its immediate subclasses. But this distinction -is not enforced by Perl itself. It's up to the programmer to behave. - -There's no reason to limit methods to those that simply access data. -Methods can do anything at all. The key point is that they're invoked -against an object or a class. Let's say we'd like object methods that -do more than fetch or set one particular field. - - sub exclaim { - my $self = shift; - return sprintf "Hi, I'm %s, age %d, working with %s", - $self->{NAME}, $self->{AGE}, join(", ", @{$self->{PEERS}}); - } - -Or maybe even one like this: - - sub happy_birthday { - my $self = shift; - return ++$self->{AGE}; - } - -Some might argue that one should go at these this way: - - sub exclaim { - my $self = shift; - return sprintf "Hi, I'm %s, age %d, working with %s", - $self->name, $self->age, join(", ", $self->peers); - } - - sub happy_birthday { - my $self = shift; - return $self->age( $self->age() + 1 ); - } - -But since these methods are all executing in the class itself, this -may not be critical. There are tradeoffs to be made. Using direct -hash access is faster (about an order of magnitude faster, in fact), and -it's more convenient when you want to interpolate in strings. But using -methods (the external interface) internally shields not just the users of -your class but even you yourself from changes in your data representation. - -=head1 Class Data - -What about "class data", data items common to each object in a class? -What would you want that for? Well, in your Person class, you might -like to keep track of the total people alive. How do you implement that? - -You I make it a global variable called $Person::Census. But about -only reason you'd do that would be if you I people to be able to -get at your class data directly. They could just say $Person::Census -and play around with it. Maybe this is ok in your design scheme. -You might even conceivably want to make it an exported variable. To be -exportable, a variable must be a (package) global. If this were a -traditional module rather than an object-oriented one, you might do that. - -While this approach is expected in most traditional modules, it's -generally considered rather poor form in most object modules. In an -object module, you should set up a protective veil to separate interface -from implementation. So provide a class method to access class data -just as you provide object methods to access object data. - -So, you I still keep $Census as a package global and rely upon -others to honor the contract of the module and therefore not play around -with its implementation. You could even be supertricky and make $Census a -tied object as described in L, thereby intercepting all accesses. - -But more often than not, you just want to make your class data a -file-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 which -it was declared is done (in this case the whole file being required or -used), Perl's deep binding of lexical variables guarantees that the -variable will not be deallocated, remaining accessible to functions -declared within that scope. This doesn't work with global variables -given temporary values via local(), though. - -Irrespective of whether you leave $Census a package global or make -it instead a file-scoped lexical, you should make these -changes to your Person::new() constructor: - - sub new { - my $class = shift; - 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 that -when Person is destroyed, the $Census goes down. Here's how -this could be done: - - sub DESTROY { --$Census } - -Notice how there's no memory to deallocate in the destructor? That's -something that Perl takes care of for you all by itself. - -Alternatively, you could use the Class::Data::Inheritable module from -CPAN. - - -=head2 Accessing Class Data - -It turns out that this is not really a good way to go about handling -class data. A good scalable rule is that I. Otherwise you aren't building a -scalable, inheritable class. The object must be the rendezvous point -for all operations, especially from an object method. The globals -(class data) would in some sense be in the "wrong" package in your -derived classes. In Perl, methods execute in the context of the class -they were defined in, I that of the object that triggered them. -Therefore, namespace visibility of package globals in methods is unrelated -to 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 those -objects are destroyed, the original $Census variable will be altered, -not the one in the new class's package namespace. Perhaps this is what -you want, but probably it isn't. - -Here's how to fix this. We'll store a reference to the data in the -value accessed by the hash key "_CENSUS". Why the underscore? Well, -mostly because an initial underscore already conveys strong feelings -of magicalness to a C programmer. It's really just a mnemonic device -to remind ourselves that this field is special and not to be used as -a public data member in the same way that NAME, AGE, and PEERS are. -(Because we've been developing this code under the strict pragma, prior -to perl version 5.004 we'll have to quote the field name.) - - sub new { - my $class = shift; - 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 Methods - -It'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 pull -in the standard Carp module to emit our warnings and fatal messages. -That way messages will come out with the caller's filename and -line number instead of our own; if we wanted them to be from our own -perspective, 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. That -way you could call both of these: - - Person->debug(1); # entire class - $him->debug(1); # just this object - -To do so, we need our debugging method to be a "bimodal" one, one that -works on both classes I 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) inherits -methods from this Person base class? Then C<< Employee->debug() >>, when called -as a class method, manipulates $Person::Debugging not $Employee::Debugging. - -=head2 Class Destructors - -The object destructor handles the death of each distinct object. But sometimes -you want a bit of cleanup when the entire class is shut down, which -currently only happens when the program exits. To make such a -I, create a function in that class's package named -END. This works just like the END function in traditional modules, -meaning that it gets called whenever your program exits unless it execs -or 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) are -be called in the opposite order that they were loaded in (LIFO order). - -=head2 Documenting the Interface - -And there you have it: we've just shown you the I of this -Person class. Its I would be its documentation. Usually this -means putting it in pod ("plain old documentation") format right there -in the same file. In our Person example, we would place the following -docs anywhere in the Person.pm file. Even though it looks mostly like -code, it's not. It's embedded documentation such as would be used by -the pod2man, pod2html, or pod2text programs. The Perl compiler ignores -pods entirely, just as the translators ignore code. Here's an example of -some 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 private -little shiny bits that were safely locked up behind the interface contract -has voided the warranty, and you shouldn't worry about their fate. - -=head1 Aggregation - -Suppose 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), plus -nicknames and titles. If users of your Person class have been properly -accessing it through its documented interface, then you can easily change -the underlying implementation. If they haven't, then they lose and -it's their fault for breaking the contract and voiding their warranty. - -To do this, we'll make another class, this one called Fullname. What's -the Fullname class look like? To answer that question, you have to -first 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 supports -a full name field this way: - - sub new { - my $class = shift; - 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 technique -of using a hash reference to hold data fields, and methods -by the appropriate name to access them: - - package Fullname; - use strict; - - sub new { - my $class = shift; - my $self = { - TITLE => undef, - CHRISTIAN => undef, - SURNAME => undef, - NICK => undef, - }; - bless ($self, $class); - return $self; - } - - sub christian { - my $self = shift; - if (@_) { $self->{CHRISTIAN} = shift } - return $self->{CHRISTIAN}; - } - - sub surname { - my $self = shift; - if (@_) { $self->{SURNAME} = shift } - return $self->{SURNAME}; - } - - sub nickname { - my $self = shift; - if (@_) { $self->{NICK} = shift } - return $self->{NICK}; - } - - sub title { - my $self = shift; - if (@_) { $self->{TITLE} = shift } - return $self->{TITLE}; - } - - sub as_string { - my $self = shift; - my $name = join(" ", @$self{'CHRISTIAN', 'SURNAME'}); - if ($self->{TITLE}) { - $name = $self->{TITLE} . " " . $name; - } - return $name; - } - - 1; - -Finally, here's the test program: - - #!/usr/bin/perl -w - use strict; - use Person; - sub END { show_census() } - - sub show_census () { - printf "Current population: %d\n", Person->population; - } - - Person->debug(1); - - show_census(); - - my $him = Person->new(); - - $him->fullname->christian("Thomas"); - $him->fullname->surname("Aquinas"); - $him->fullname->nickname("Tommy"); - $him->fullname->title("St"); - $him->age(1); - - printf "%s is really %s.\n", $him->name, $him->fullname->as_string; - printf "%s's age: %d.\n", $him->name, $him->age; - $him->happy_birthday; - printf "%s's age: %d.\n", $him->name, $him->age; - - show_census(); - -=head1 Inheritance - -Object-oriented programming systems all support some notion of -inheritance. Inheritance means allowing one class to piggy-back on -top of another one so you don't have to write the same code again and -again. It's about software reuse, and therefore related to Laziness, -the principal virtue of a programmer. (The import/export mechanisms in -traditional modules are also a form of code reuse, but a simpler one than -the true inheritance that you find in object modules.) - -Sometimes the syntax of inheritance is built into the core of the -language, and sometimes it's not. Perl has no special syntax for -specifying the class (or classes) to inherit from. Instead, it's all -strictly in the semantics. Each package can have a variable called @ISA, -which governs (method) inheritance. If you try to call a method on an -object or class, and that method is not found in that object's package, -Perl then looks to @ISA for other packages to go looking through in -search of the missing method. - -Like the special per-package variables recognized by Exporter (such as -@EXPORT, @EXPORT_OK, @EXPORT_FAIL, %EXPORT_TAGS, and $VERSION), the @ISA -array I be a package-scoped global and not a file-scoped lexical -created via my(). Most classes have just one item in their @ISA array. -In this case, we have what's called "single inheritance", or SI for short. - -Consider this class: - - package Employee; - use Person; - @ISA = ("Person"); - 1; - -Not a lot to it, eh? All it's doing so far is loading in another -class and stating that this one will inherit methods from that -other class if need be. We have given it none of its own methods. -We rely upon an Employee to behave just like a Person. - -Setting up an empty class like this is called the "empty subclass test"; -that is, making a derived class that does nothing but inherit from a -base class. If the original base class has been designed properly, -then the new derived class can be used as a drop-in replacement for the -old one. This means you should be able to write a program like this: - - use Employee; - my $empl = Employee->new(); - $empl->name("Jason"); - $empl->age(23); - printf "%s is age %d.\n", $empl->name, $empl->age; - -By proper design, we mean always using the two-argument form of bless(), -avoiding direct access of global data, and not exporting anything. If you -look back at the Person::new() function we defined above, we were careful -to do that. There's a bit of package data used in the constructor, -but the reference to this is stored on the object itself and all other -methods access package data via that reference, so we should be ok. - -What do we mean by the Person::new() function? Isn't that actually -a method? Well, in principle, yes. A method is just a function that -expects as its first argument a class name (package) or object -(blessed reference). Person::new() is the function that both the -C<< Person->new() >> method and the C<< Employee->new() >> method end -up calling. Understand that while a method call looks a lot like a -function call, they aren't really quite the same, and if you treat them -as the same, you'll very soon be left with nothing but broken programs. -First, the actual underlying calling conventions are different: method -calls get an extra argument. Second, function calls don't do inheritance, -but methods do. - - Method Call Resulting Function Call - ----------- ------------------------ - Person->new() Person::new("Person") - Employee->new() Person::new("Employee") - -So don't use function calls when you mean to call a method. - -If an employee is just a Person, that's not all too very interesting. -So let's add some other methods. We'll give our employee -data fields to access their salary, their employee ID, and their -start date. - -If you're getting a little tired of creating all these nearly identical -methods just to get at the object's data, do not despair. Later, -we'll describe several different convenience mechanisms for shortening -this up. Meanwhile, here's the straight-forward way: - - sub salary { - my $self = shift; - if (@_) { $self->{SALARY} = shift } - return $self->{SALARY}; - } - - sub id_number { - my $self = shift; - if (@_) { $self->{ID} = shift } - return $self->{ID}; - } - - sub start_date { - my $self = shift; - if (@_) { $self->{START_DATE} = shift } - return $self->{START_DATE}; - } - -=head2 Overridden Methods - -What happens when both a derived class and its base class have the same -method defined? Well, then you get the derived class's version of that -method. For example, let's say that we want the peers() method called on -an employee to act a bit differently. Instead of just returning the list -of peer names, let's return slightly different strings. So doing this: - - $empl->peers("Peter", "Paul", "Mary"); - printf "His peers are: %s\n", join(", ", $empl->peers); - -will produce: - - His peers are: PEON=PETER, PEON=PAUL, PEON=MARY - -To do this, merely add this definition into the Employee.pm file: - - sub peers { - my $self = shift; - if (@_) { @{ $self->{PEERS} } = @_ } - return map { "PEON=\U$_" } @{ $self->{PEERS} }; - } - -There, we've just demonstrated the high-falutin' concept known in certain -circles as I. We've taken on the form and behaviour of -an existing object, and then we've altered it to suit our own purposes. -This is a form of Laziness. (Getting polymorphed is also what happens -when the wizard decides you'd look better as a frog.) - -Every now and then you'll want to have a method call trigger both its -derived class (also known as "subclass") version as well as its base class -(also known as "superclass") version. In practice, constructors and -destructors are likely to want to do this, and it probably also makes -sense in the debug() method we showed previously. - -To do this, add this to Employee.pm: - - use Carp; - my $Debugging = 0; - - sub debug { - my $self = shift; - confess "usage: thing->debug(level)" unless @_ == 1; - my $level = shift; - if (ref($self)) { - $self->{"_DEBUG"} = $level; - } else { - $Debugging = $level; # whole class - } - Person::debug($self, $Debugging); # don't really do this - } - -As you see, we turn around and call the Person package's debug() function. -But this is far too fragile for good design. What if Person doesn't -have a debug() function, but is inheriting I debug() method -from elsewhere? It would have been slightly better to say - - Person->debug($Debugging); - -But even that's got too much hard-coded. It's somewhat better to say - - $self->Person::debug($Debugging); - -Which is a funny way to say to start looking for a debug() method up -in Person. This strategy is more often seen on overridden object methods -than on overridden class methods. - -There is still something a bit off here. We've hard-coded our -superclass's name. This in particular is bad if you change which classes -you inherit from, or add others. Fortunately, the pseudoclass SUPER -comes to the rescue here. - - $self->SUPER::debug($Debugging); - -This way it starts looking in my class's @ISA. This only makes sense -from I a method call, though. Don't try to access anything -in SUPER:: from anywhere else, because it doesn't exist outside -an overridden method call. Note that C refers to the superclass of -the current package, I to the superclass of C<$self>. - -Things are getting a bit complicated here. Have we done anything -we shouldn't? As before, one way to test whether we're designing -a decent class is via the empty subclass test. Since we already have -an Employee class that we're trying to check, we'd better get a new -empty subclass that can derive from Employee. Here's one: - - package Boss; - use Employee; # :-) - @ISA = qw(Employee); - -And here's the test program: - - #!/usr/bin/perl -w - use strict; - use Boss; - Boss->debug(1); - - my $boss = Boss->new(); - - $boss->fullname->title("Don"); - $boss->fullname->surname("Pichon Alvarez"); - $boss->fullname->christian("Federico Jesus"); - $boss->fullname->nickname("Fred"); - - $boss->age(47); - $boss->peers("Frank", "Felipe", "Faust"); - - printf "%s is age %d.\n", $boss->fullname->as_string, $boss->age; - printf "His peers are: %s\n", join(", ", $boss->peers); - -Running it, we see that we're still ok. If you'd like to dump out your -object in a nice format, somewhat like the way the 'x' command works in -the debugger, you could use the Data::Dumper module from CPAN this way: - - use Data::Dumper; - print "Here's the boss:\n"; - print Dumper($boss); - -Which shows us something like this: - - Here's the boss: - $VAR1 = bless( { - _CENSUS => \1, - FULLNAME => bless( { - TITLE => 'Don', - SURNAME => 'Pichon Alvarez', - NICK => 'Fred', - CHRISTIAN => 'Federico Jesus' - }, 'Fullname' ), - AGE => 47, - PEERS => [ - 'Frank', - 'Felipe', - 'Faust' - ] - }, 'Boss' ); - -Hm.... something's missing there. What about the salary, start date, -and ID fields? Well, we never set them to anything, even undef, so they -don't show up in the hash's keys. The Employee class has no new() method -of its own, and the new() method in Person doesn't know about Employees. -(Nor should it: proper OO design dictates that a subclass be allowed to -know about its immediate superclass, but never vice-versa.) So let's -fix up Employee::new() this way: - - sub new { - my $class = shift; - my $self = $class->SUPER::new(); - $self->{SALARY} = undef; - $self->{ID} = undef; - $self->{START_DATE} = undef; - bless ($self, $class); # reconsecrate - return $self; - } - -Now if you dump out an Employee or Boss object, you'll find -that new fields show up there now. - -=head2 Multiple Inheritance - -Ok, at the risk of confusing beginners and annoying OO gurus, it's -time to confess that Perl's object system includes that controversial -notion known as multiple inheritance, or MI for short. All this means -is that rather than having just one parent class who in turn might -itself have a parent class, etc., that you can directly inherit from -two or more parents. It's true that some uses of MI can get you into -trouble, although hopefully not quite so much trouble with Perl as with -dubiously-OO languages like C++. - -The way it works is actually pretty simple: just put more than one package -name in your @ISA array. When it comes time for Perl to go finding -methods for your object, it looks at each of these packages in order. -Well, kinda. It's actually a fully recursive, depth-first order by -default (see L for alternate method resolution orders). -Consider a bunch of @ISA arrays like this: - - @First::ISA = qw( Alpha ); - @Second::ISA = qw( Beta ); - @Third::ISA = qw( First Second ); - -If you have an object of class Third: - - my $ob = Third->new(); - $ob->spin(); - -How do we find a spin() method (or a new() method for that matter)? -Because the search is depth-first, classes will be looked up -in the following order: Third, First, Alpha, Second, and Beta. - -In practice, few class modules have been seen that actually -make use of MI. One nearly always chooses simple containership of -one class within another over MI. That's why our Person -object I a Fullname object. That doesn't mean -it I one. - -However, there is one particular area where MI in Perl is rampant: -borrowing another class's class methods. This is rather common, -especially with some bundled "objectless" classes, -like Exporter, DynaLoader, AutoLoader, and SelfLoader. These classes -do not provide constructors; they exist only so you may inherit their -class methods. (It's not entirely clear why inheritance was done -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 their -classes' behaviours to POSIX. - -Why don't people use MI for object methods much? One reason is that -it can have complicated side-effects. For one thing, your inheritance -graph (no longer a tree) might converge back to the same base class. -Although Perl guards against recursive inheritance, merely having parents -who are related to each other via a common ancestor, incestuous though -it sounds, is not forbidden. What if in our Third class shown above we -wanted its new() method to also call both overridden constructors in its -two 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 calling -overridden methods, you'd end up calling Nought::new() twice, -which might well be a bad idea. - -=head2 UNIVERSAL: The Root of All Objects - -Wouldn't it be convenient if all objects were rooted at some ultimate -base class? That way you could give every object common methods without -having to go and add it to each and every @ISA. Well, it turns out that -you can. You don't see it, but Perl tacitly and irrevocably assumes -that 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 put -whatever 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 Perl -binary, so they don't take any extra time to load. Predefined methods -include isa(), can(), and VERSION(). isa() tells you whether an object or -class "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 back -whether 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's -class) 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 all -uppercase function name is a Perl convention that indicates that the -function 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 explained -above, 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 or -higher available. This is not the same as loading in that exact version -number. No mechanism currently exists for concurrent installation of -multiple versions of a module. Lamentably. - -=head2 Deeper UNIVERSAL details - -It is also valid (though perhaps unwise in most cases) to put other -packages' names in @UNIVERSAL::ISA. These packages will also be -implicitly inherited by all classes, just as UNIVERSAL itself is. -However, neither UNIVERSAL nor any of its parents from the @ISA tree -are explicit base classes of all objects. To clarify, given the -following: - - @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 calling -Foo->isa('REALLYUNIVERSAL') or Foo->isa('UNIVERSAL') will return -false. - -If your class is using an alternate mro like C3 (see -L), method resolution within UNIVERSAL / @UNIVERSAL::ISA will -still 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 really -happens during method lookup, which is roughly like this -ugly 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 not -search in UNIVERSAL itself, only in the package's actual -@ISA. - -=head1 Alternate Object Representations - -Nothing requires objects to be implemented as hash references. An object -can be any sort of reference so long as its referent has been suitably -blessed. That means scalar, array, and code references are also fair -game. - -A scalar would work if the object has only one datum to hold. An array -would work for most cases, but makes inheritance a bit dodgy because -you have to invent new indices for the derived classes. - -=head2 Arrays as Objects - -If the user of your class honors the contract and sticks to the advertised -interface, then you can change its underlying interface if you feel -like it. Here's another implementation that conforms to the same -interface specification. This time we'll use an array reference -instead 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 succeeds - -You might guess that the array access would be a lot faster than the -hash access, but they're actually comparable. The array is a I -bit faster, but not more than ten or fifteen percent, even when you -replace 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 representation -because 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 many -hashes have that key. It's expected that sometime in the future, even -these differences will fade into obscurity as more efficient underlying -representations are devised. - -Still, the tiny edge in speed (and somewhat larger one in memory) -is enough to make some programmers choose an array representation -for simple classes. There's still a little problem with -scalability, though, because later in life when you feel -like creating subclasses, you'll find that hashes just work -out better. - -=head2 Closures as Objects - -Using a code reference to represent an object offers some fascinating -possibilities. We can create a new anonymous function (closure) who -alone in all the world can see the object's data. This is because we -put the data into an anonymous hash that's lexically visible only to -the closure we create, bless, and return as the object. This object's -methods 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't -be 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 sublimely -different: - - 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 bit -mysterious to those whose background is more firmly rooted in standard -procedural or object-based programming languages than in functional -programming languages whence closures derive. The object -created and returned by the new() method is itself not a data reference -as we've seen before. It's an anonymous code reference that has within -it 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 different -version of $self. - -When a method like C<$him-Ename("Jason")> is called, its implicit -zeroth argument is the invoking object--just as it is with all method -calls. But in this case, it's our code reference (something like a -function 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, so -that's just what we do when we say C<&{$_[0]}>. This is just a regular -function call, not a method call. The initial argument is the string -"NAME", and any remaining arguments are whatever had been passed to the -method itself. - -Once we're executing inside the closure that had been created in new(), -the $self hash reference suddenly becomes visible. The closure grabs -its first argument ("NAME" in this case because that's what the name() -method passed it), and uses that string to subscript into the private -hash hidden in its unique version of $self. - -Nothing under the sun will allow anyone outside the executing method to -be able to get at this hidden data. Well, nearly nothing. You I -single step through the program using the debugger and find out the -pieces 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 know -what will. Translation of this technique into C++, Java, or any other -braindead-static language is left as a futile exercise for aficionados -of those camps. - -You could even add a bit of nosiness via the caller() function and -make the closure refuse to operate unless called via its own package. -This would no doubt satisfy certain fastidious concerns of programming -police and related puritans. - -If you were wondering when Hubris, the third principle virtue of a -programmer, would come into play, here you have it. (More seriously, -Hubris is just the pride in craftsmanship that comes from having written -a sound bit of well-designed code.) - -=head1 AUTOLOAD: Proxy Methods - -Autoloading is a way to intercept calls to undefined methods. An autoload -routine may choose to create a new function on the fly, either loaded -from disk or perhaps just eval()ed right there. This define-on-the-fly -strategy is why it's called autoloading. - -But that's only one possible approach. Another one is to just -have the autoloaded method itself directly provide the -requested service. When used in this way, you may think -of autoloaded methods as "proxy" methods. - -When Perl tries to call an undefined function in a particular package -and that function is not defined, it looks for a function in -that same package called AUTOLOAD. If one exists, it's called -with the same arguments as the original function would have had. -The fully-qualified name of the function is stored in that package's -global variable $AUTOLOAD. Once called, the function can do anything -it would like, including defining a new function by the right name, and -then doing a really fancy kind of C right to it, erasing itself -from the call stack. - -What does this have to do with objects? After all, we keep talking about -functions, not methods. Well, since a method is just a function with -an extra argument and some fancier semantics about where it's found, -we can use autoloading for methods, too. Perl doesn't start looking -for an AUTOLOAD method until it has exhausted the recursive hunt up -through @ISA, though. Some programmers have even been known to define -a UNIVERSAL::AUTOLOAD method to trap unresolved method calls to any -kind of object. - -=head2 Autoloaded Data Methods - -You probably began to get a little suspicious about the duplicated -code way back earlier when we first showed you the Person class, and -then later the Employee class. Each method used to access the -hash fields looked virtually identical. This should have tickled -that great programming virtue, Impatience, but for the time, -we let Laziness win out, and so did nothing. Proxy methods can cure -this. - -Instead of writing a new function every time we want a new data field, -we'll use the autoload mechanism to generate (actually, mimic) methods on -the fly. To verify that we're accessing a valid member, we will check -against an C<_permitted> (pronounced "under-permitted") field, which -is a reference to a file-scoped lexical (like a C file static) hash of permitted fields in this record -called %fields. Why the underscore? For the same reason as the _CENSUS -field we once used: as a marker that means "for internal use only". - -Here's what the module initialization code and class -constructor will look like when taking this approach: - - package Person; - use Carp; - our $AUTOLOAD; # it's a package global - - my %fields = ( - name => undef, - age => undef, - peers => undef, - ); - - sub new { - my $class = shift; - my $self = { - _permitted => \%fields, - %fields, - }; - bless $self, $class; - return $self; - } - -If we wanted our record to have default values, we could fill those in -where current we have C in the %fields hash. - -Notice how we saved a reference to our class data on the object itself? -Remember that it's important to access class data through the object -itself instead of having any method reference %fields directly, or else -you won't have a decent inheritance. - -The real magic, though, is going to reside in our proxy method, which -will handle all calls to undefined methods for objects of class Person -(or subclasses of Person). It has to be called AUTOLOAD. Again, it's -all caps because it's called for us implicitly by Perl itself, not by -a user directly. - - sub AUTOLOAD { - my $self = shift; - my $type = ref($self) - or croak "$self is not an object"; - - my $name = $AUTOLOAD; - $name =~ s/.*://; # strip fully-qualified portion - - unless (exists $self->{_permitted}->{$name} ) { - croak "Can't access `$name' field in class $type"; - } - - if (@_) { - return $self->{$name} = shift; - } else { - return $self->{$name}; - } - } - -Pretty nifty, eh? All we have to do to add new data fields -is modify %fields. No new functions need be written. - -I could have avoided the C<_permitted> field entirely, but I -wanted to demonstrate how to store a reference to class data on the -object so you wouldn't have to access that class data -directly from an object method. - -=head2 Inherited Autoloaded Data Methods - -But what about inheritance? Can we define our Employee -class similarly? Yes, so long as we're careful enough. - -Here's how to be careful: - - package Employee; - use Person; - use strict; - our @ISA = qw(Person); - - my %fields = ( - id => undef, - salary => undef, - ); - - sub new { - my $class = shift; - my $self = $class->SUPER::new(); - my($element); - foreach $element (keys %fields) { - $self->{_permitted}->{$element} = $fields{$element}; - } - @{$self}{keys %fields} = values %fields; - return $self; - } - -Once we've done this, we don't even need to have an -AUTOLOAD function in the Employee package, because -we'll grab Person's version of that via inheritance, -and it will all work out just fine. - -=head1 Metaclassical Tools - -Even though proxy methods can provide a more convenient approach to making -more struct-like classes than tediously coding up data methods as -functions, it still leaves a bit to be desired. For one thing, it means -you have to handle bogus calls that you don't mean to trap via your proxy. -It also means you have to be quite careful when dealing with inheritance, -as detailed above. - -Perl programmers have responded to this by creating several different -class construction classes. These metaclasses are classes -that create other classes. A couple worth looking at are -Class::Struct and Alias. These and other related metaclasses can be -found in the modules directory on CPAN. - -=head2 Class::Struct - -One of the older ones is Class::Struct. In fact, its syntax and -interface were sketched out long before perl5 even solidified into a -real thing. What it does is provide you a way to "declare" a class -as having objects whose fields are of a specific type. The function -that does this is called, not surprisingly enough, struct(). Because -structures or records are not base types in Perl, each time you want to -create a class to provide a record-like data object, you yourself have -to define a new() method, plus separate data-access methods for each of -that record's fields. You'll quickly become bored with this process. -The Class::Struct::struct() function alleviates this tedium. - -Here's a simple example of using it: - - use Class::Struct qw(struct); - use Jobbie; # user-defined; see below - - struct 'Fred' => { - one => '$', - many => '@', - profession => 'Jobbie', # does not call Jobbie->new() - }; - - $ob = Fred->new(profession => Jobbie->new()); - $ob->one("hmmmm"); - - $ob->many(0, "here"); - $ob->many(1, "you"); - $ob->many(2, "go"); - print "Just set: ", $ob->many(2), "\n"; - - $ob->profession->salary(10_000); - -You can declare types in the struct to be basic Perl types, or -user-defined types (classes). User types will be initialized by calling -that class's new() method. - -Take care that the C object is not created automatically by the -C class's new() method, so you should specify a C object -when you create an instance of C. - -Here's a real-world example of using struct generation. Let's say you -wanted to override Perl's idea of gethostbyname() and gethostbyaddr() so -that they would return objects that acted like C structures. We don't -care about high-falutin' OO gunk. All we want is for these objects to -act like structs in the C sense. - - use Socket; - use Net::hostent; - $h = gethostbyname("perl.com"); # object return - printf "perl.com's real name is %s, address %s\n", - $h->name, inet_ntoa($h->addr); - -Here's how to do this using the Class::Struct module. -The crux is going to be this call: - - struct 'Net::hostent' => [ # note bracket - name => '$', - aliases => '@', - addrtype => '$', - 'length' => '$', - addr_list => '@', - ]; - -Which creates object methods of those names and types. -It even creates a new() method for us. - -We could also have implemented our object this way: - - struct 'Net::hostent' => { # note brace - name => '$', - aliases => '@', - addrtype => '$', - 'length' => '$', - addr_list => '@', - }; - -and then Class::Struct would have used an anonymous hash as the object -type, instead of an anonymous array. The array is faster and smaller, -but the hash works out better if you eventually want to do inheritance. -Since for this struct-like object we aren't planning on inheritance, -this time we'll opt for better speed and size over better flexibility. - -Here's the whole implementation: - - package Net::hostent; - use strict; - - BEGIN { - use Exporter (); - our @EXPORT = qw(gethostbyname gethostbyaddr gethost); - our @EXPORT_OK = qw( - $h_name @h_aliases - $h_addrtype $h_length - @h_addr_list $h_addr - ); - our %EXPORT_TAGS = ( FIELDS => [ @EXPORT_OK, @EXPORT ] ); - } - our @EXPORT_OK; - - # Class::Struct forbids use of @ISA - sub import { goto &Exporter::import } - - use Class::Struct qw(struct); - struct 'Net::hostent' => [ - name => '$', - aliases => '@', - addrtype => '$', - 'length' => '$', - addr_list => '@', - ]; - - sub addr { shift->addr_list->[0] } - - sub populate (@) { - return unless @_; - my $hob = new(); # Class::Struct made this! - $h_name = $hob->[0] = $_[0]; - @h_aliases = @{ $hob->[1] } = split ' ', $_[1]; - $h_addrtype = $hob->[2] = $_[2]; - $h_length = $hob->[3] = $_[3]; - $h_addr = $_[4]; - @h_addr_list = @{ $hob->[4] } = @_[ (4 .. $#_) ]; - return $hob; - } - - sub gethostbyname ($) { populate(CORE::gethostbyname(shift)) } - - sub gethostbyaddr ($;$) { - my ($addr, $addrtype); - $addr = shift; - require Socket unless @_; - $addrtype = @_ ? shift : Socket::AF_INET(); - populate(CORE::gethostbyaddr($addr, $addrtype)) - } - - sub gethost($) { - if ($_[0] =~ /^\d+(?:\.\d+(?:\.\d+(?:\.\d+)?)?)?$/) { - require Socket; - &gethostbyaddr(Socket::inet_aton(shift)); - } else { - &gethostbyname; - } - } - - 1; - -We've snuck in quite a fair bit of other concepts besides just dynamic -class creation, like overriding core functions, import/export bits, -function prototyping, short-cut function call via C<&whatever>, and -function replacement with C. These all mostly make -sense from the perspective of a traditional module, but as you can see, -we can also use them in an object module. - -You can look at other object-based, struct-like overrides of core -functions in the 5.004 release of Perl in File::stat, Net::hostent, -Net::netent, Net::protoent, Net::servent, Time::gmtime, Time::localtime, -User::grent, and User::pwent. These modules have a final component -that's all lowercase, by convention reserved for compiler pragmas, -because they affect the compilation and change a builtin function. -They also have the type names that a C programmer would most expect. - -=head2 Data Members as Variables - -If you're used to C++ objects, then you're accustomed to being able to -get at an object's data members as simple variables from within a method. -The Alias module provides for this, as well as a good bit more, such -as the possibility of private methods that the object can call but folks -outside the class cannot. - -Here's an example of creating a Person using the Alias module. -When you update these magical instance variables, you automatically -update value fields in the hash. Convenient, eh? - - package Person; - - # this is the same as before... - sub new { - my $class = shift; - my $self = { - NAME => undef, - AGE => undef, - PEERS => [], - }; - bless($self, $class); - return $self; - } - - use Alias qw(attr); - our ($NAME, $AGE, $PEERS); - - sub name { - my $self = attr shift; - if (@_) { $NAME = shift; } - return $NAME; - } - - sub age { - my $self = attr shift; - if (@_) { $AGE = shift; } - return $AGE; - } - - sub peers { - my $self = attr shift; - if (@_) { @PEERS = @_; } - return @PEERS; - } - - sub exclaim { - my $self = attr shift; - return sprintf "Hi, I'm %s, age %d, working with %s", - $NAME, $AGE, join(", ", @PEERS); - } - - sub happy_birthday { - my $self = attr shift; - return ++$AGE; - } - -The need for the C declaration is because what Alias does -is play with package globals with the same name as the fields. To use -globals while C is in effect, you have to predeclare them. -These package variables are localized to the block enclosing the attr() -call just as if you'd used a local() on them. However, that means that -they're still considered global variables with temporary values, just -as with any other local(). - -It would be nice to combine Alias with -something like Class::Struct or Class::MethodMaker. - -=head1 NOTES - -=head2 Object Terminology - -In the various OO literature, it seems that a lot of different words -are used to describe only a few different concepts. If you're not -already an object programmer, then you don't need to worry about all -these fancy words. But if you are, then you might like to know how to -get at the same concepts in Perl. - -For example, it's common to call an object an I of a class -and to call those objects' methods I. Data fields -peculiar to each object are often called I or I, and data fields common to all members of that class are -I, I, or I. - -Also, I, I, and I all describe -the same notion, whereas I, I, and -I describe the other related one. - -C++ programmers have I and I, -but Perl only has I and I. -Actually, Perl only has methods. Whether a method gets used -as a class or object method is by usage only. You could accidentally -call a class method (one expecting a string argument) on an -object (one expecting a reference), or vice versa. - -From the C++ perspective, all methods in Perl are virtual. -This, by the way, is why they are never checked for function -prototypes in the argument list as regular builtin and user-defined -functions can be. - -Because a class is itself something of an object, Perl's classes can be -taken as describing both a "class as meta-object" (also called I) philosophy and the "class as type definition" (I -behaviour, not I mechanism) idea. C++ supports the latter -notion, but not the former. - -=head1 SEE ALSO - -The following manpages will doubtless provide more -background for this one: -L, -L, -L, -L, -L, -and -L. - -L is a kinder, gentler introduction to object-oriented -programming. - -L provides more detail on class data. - -Some modules which might prove interesting are Class::Accessor, -Class::Class, Class::Contract, Class::Data::Inheritable, -Class::MethodMaker and Tie::SecureHash - - -=head1 AUTHOR AND COPYRIGHT - -Copyright (c) 1997, 1998 Tom Christiansen -All rights reserved. - -This documentation is free; you can redistribute it and/or modify it -under the same terms as Perl itself. - -Irrespective of its distribution, all code examples in this file -are hereby placed into the public domain. You are permitted and -encouraged to use this code in your own programs for fun -or for profit as you see fit. A simple comment in the code giving -credit would be courteous but is not required. - -=head1 COPYRIGHT - -=head2 Acknowledgments - -Thanks to -Larry Wall, -Roderick Schertler, -Gurusamy Sarathy, -Dean Roehrich, -Raphael Manfredi, -Brent Halsey, -Greg Bacon, -Brad Appleton, -and many others for their helpful comments. -- 2.7.4