Example: File Information Class

 <  Day Day Up  >  

If you recall from last hour , you built a module to give information on files. After building the module, it had a series of defects. Each of those defects can easily be fixed by using a class instead of a regular module.

The object-oriented version of the file information module is presented in Listing 18.6.

Listing 18.6. The FileInfo Module, as a Class.
 1: #!/usr/bin/perl -w 2: 3: package TYPFileInfoOO; 4: use strict; 5: 6: sub new { 7:       my($class,$filename)=@_; 8:       if (! -e $filename) { 9:               die "$filename doesn't exist!"; 10:      } 11:      bless { filename => $filename }; 12: } 13: sub bytes {  # Returns the size of the file in bytes 14:      my($self)=@_; 15:      return -s $self->{filename}; 16: } 17: sub lines {  # Returns the number of text lines in the file 18:      my($self)=@_; 19:      my $count = 0; 20:      open(FH, $self->{filename})  die "Can't open $self->{filename}: $!"; 21:      while(<FH>) { 22:              $count++; 23:      } 24:      close(FH); 25:      return $count;  26: } 27: sub name {   # Returns just the filename portion of the path 28:      my($self)=@_; 29:      if ($self->{filename} =~ m/([\w\.]+)$/) { 30:              return ; 31:      } 32:      return $self->{filename}; 33: } 34: sub extension {      # Returns just the extension portion of the filename 35:      my($self)=@_; 36:      if ($self->{filename} =~ m/\.(.*?)$/) { 37:              return ; 38:      } 39:      return ""; 40: } 41: sub modified {       # Returns the last modified time, suitable for localtime() 42:      my($self)=@_; 43:      my @stats = stat($self->{filename}); 44:      return $stats[9];  # Modified time. 45: } 46: 1; 

This listing is virtually unchanged from 17.8. The primary difference surrounds the fact that the global variable $FileInfoName is now gone.

Line 7: This new constructor is a bit different than the last. In order to create a TYPFileInfoOO object, you must pass the filename in to the constructor. (See Listing 18.7 for an example.) The new method is passed the class name as the first argument. You're really not interested in it, so just ignore it.

Lines 8 “10 : Because the filename is passed in during the constructor, and can't be changed anywhere else, you can check here to see if it exists. Contrast this to relying on $FileInfoName in the last hour.

Line 9 : An anonymous hash reference is created, with one key-value pair to store the filename. This is blessed and returned (implicitly) from the subroutine.

Line 14 : Because this is an OOP module, each of the subroutines will be called as methods and the first argument passed to the subroutine is the blessed reference created in the constructor ”in this case you stash it in $self .

Line 15 : To get to the original filename passed into the constructor, use $self->{filename} . This is used to get the size of the file.

The rest of the module continues the pattern of simply retrieving the filename from $self->{filename} . Remember that each instance of the class (each object) will have its own version of $self .

Using the File Information Class

Listing 18.7 shows how the class would be used to write the same program you wrote in Hour 17, "Writing Modules."

Listing 18.7. A Reworking of Listing 17.9 with a Class
 1:  #!/usr/bin/perl -w 2: 3:  use strict; 4:  use TYPFileInfoOO; 5: 6:  my $fileInfo = new TYPFileInfoOO("/temp/message.txt"); 7: 8:  print "\nFilename: "  . $fileInfo->name(); 9:  print "\nExtension: " . $fileInfo->extension(); 10: print "\nModified: " . localtime($fileInfo->modified()); 11: print "\nBytes: " . $fileInfo->bytes(); 12: print "\nLines: " . $fileInfo->lines(); 

Line 6 : Notice that this constructor (the new part) takes a pathname as an argument. From now on, each method call against the resulting object will use whatever path was passed in.

Lines 8 “12 : This time around, to call the functions, use the object that you've created with the appropriate method.

Virtually no change, right? Well, like the Car class, the power of this class doesn't come out until you have multiple objects in play simultaneously .

To demonstrate this, Listing 18.8 presents a program to sort a directory by file size.

Listing 18.8. Using Multiple FileInfo Objects at Once
 1: #!/usr/bin/perl -w 2: 3: use TYPFileInfoOO; 4: 5: my @fileobjects; 6: foreach my $pathname ( glob("/temp/*.txt") ) { 7:       if (-d $pathname) { 8:               next; 9:       } 10:      push @fileobjects, new TYPFileInfoOO($pathname); 11: } 12: 13: foreach my $fileobj ( 14:      sort { $a->bytes() <=> $b->bytes() } 15:      @fileobjects ) { 16: 17:      printf("%20s %9d %9d\n", 18:              $fileobj->name(), $fileobj->bytes(), $fileobj->lines()); 19: } 

Line 6 : This uses the built-in glob function presented in Hour 10, "Files and Directories," to retrieve a list of all the text files in a directory.

Lines 7 “9 : Directory names may also be returned by glob . This class isn't designed for directories, so skip them.

Line 10 : Each pathname is used to construct a TYPFileInfoOO object. The resulting object is then accumulated in @fileobjects . If there are 100 text files, 100 independent objects will be created and thrown into @fileobjects .

Lines 13 “15 : The array @fileobject is sorted by size (the number of bytes in the file). Because you're sorting an array of objects, the sort subroutine uses $a and $b as objects, calling their byte method to get the size for sorting. The variable $fileobj is set to each object in the sorted list and the loop body is run.

Lines 17 “18 : $fileobj will take on the value of each object in the list. Because it's an object, simply call methods against the object to get the values.

The results (on my system) look something like this:

 chess.txt       135         8          sample.txt       456        30        outwords.txt      1031       141         message.txt      1112        36          parrot.txt      1152       144         outfile.txt      3799         1     NewSchedule.txt      4119        57      volleyball.txt      5289       440             log.txt     15529       363 

This program would have been much more difficult to write using the old non-OOP version of the FileInfo module.

Let's revisit the list of problems presented at the end of Hour 17, and see if we've solved any of them.

  1. Problem: Namespace cluttering from importing names.

    Solved : An OOP module doesn't need to import any names at all. Each object has its own methods all accessed from the object itself. Objects of different types can all share method names and there are no namespace collisions at all!

  2. Problem: Special-secret variable names to control behavior.

    Solved : The constructor takes the pathname as an argument, and the dirty work is done inside of the class. The only names you have to know are method names to use with the object.

  3. Problem: Can't do concurrent operations with the module.

    Solved : Because each object maintains its own internal state and structures, there's no real limit to how many independent objects you can create.

  4. Problem: With users accessing internals, the behavior becomes unpredictable.

    Mostly Solved : No internal mechanisms are directly visible to the module user . Arguments get passed to the constructor and the methods, and the module can keep its internals private. In Perl, it's still possible to directly change the internals components of a class from outside of the class ”but it's not recommended, and sometimes not easy.

 <  Day Day Up  >  


SAMS Teach Yourself Perl in 24 Hours
Sams Teach Yourself Perl in 24 Hours (3rd Edition)
ISBN: 0672327937
EAN: 2147483647
Year: 2005
Pages: 241

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net