Example: Implementation of Car in Perl

Listing 18.1 shows how a simple car could be represented in Perl. This listing should be saved as Car.pm.

To create a class in Perl, a constructor must bless a reference into the class. What this means is a reference (it doesn't matter what type, most people use hash references) is going to be created within the new subroutine. The bless function will take that reference and associate it with the class as an instance. The new subroutine should then return that reference.

Listing 18.1. First Part of the Car Class
1: #!/usr/bin/perl -w
2:
3: use strict;
4: package Car;
5:
6: my $model_name = "RoadPerl";
7: my $vin = 1;
8:
9: sub new {
10:    my $car = {
11:        vin => $vin++,
12:        rpm => 0,
13:    };
14:    bless $car;    
15: }

Line 4: This establishes that all the package variables mentioned within, and all the subroutines, will be in the Car namespace. With a class, you don't need to export any subroutine names, which differs from the modules you saw last hour.

Lines 6–7: These variables will be visible only within this class. Because any subroutine in the class can see them, and they're not inside of your blessed reference; these are called class variables.

Lines 10–13: A hash reference is created. Because it will be passed to every method called by this class, you've put some information in it that's unique to each car produced. The VIN, or vehicle identification number, starts at 1 and goes up for each car you produce, and the RPM (engine speed) is initialized to 0.

Line 14: The reference is blessed into the class. The bless function returns the reference, so it's usually the last statement in the constructor.

There's no rule that says that the constructor for Car has to be called new; it's just convention. In fact, your class can have as many constructors as it wants, or none at all. But it's a little pointless without a constructor.

Listing 18.2 continues our Car Class by providing methods to get the speed of the car and the VIN. Methods that are simply returning properties of a class are called accessor methods.

Listing 18.2. Property Methods in the Car Class
16: sub vin {
17:     my($self)=@_;
18:     return $self->{vin};
19: }
20: sub speed {
21:     my($self)=@_;
22:     return $self->{rpm}/100;
23: }

Line 17: When the vin method is called (because it is a method) the first argument to the subroutine will be a copy of the original blessed reference from line 13. Normally, the first line of the method takes the argument list from @_ and moves it into more meaningful variable names. By tradition, the object itself is called $self.

Line 18: With the original reference in hand, it's a simple matter of retrieving the VIN number from the hash reference and returning it.

Lines 21–22: Again, $self is set to the original reference. This time you've taken the RPM speed of the engine, done a little math on it, and returned a speed for the vehicle.

The Car Class's next two methods, shown in Listing 18.3, control the car's internals and provide the Car's actions.

Listing 18.3. Methods in the Car Class
24: sub brake {
25:     my($self)=@_;
26:     $self->{rpm}=0;
27:     print "Skreetch!
";
28: }
29: sub accelerate {
30:     my($self,$value)=@_;
31:     $self->{rpm}=$self->{rpm} + $value;
32:     if ($self->{rpm} > 10) {
33:         die "Engine blew up in " . $self->{vin};
34:     }
35:     print "Vroom!
";
36: }

Lines 25–27: If the brake method is called on a car object, this subroutine will set the motor rpm back to zero and print a message. This is done by using the $self reference, and manipulating the RPM value in the hash.

Line 30: If the car wants to accelerate, it has to call this method with an additional value to indicate how much acceleration to give the car. This is stashed into $value.

Line 31: The value passed in is used to increment the RPM of the car.

Lines 32–35: If you accelerate too many times or too much, the engine will blow up, and the program will stop. Otherwise a satisfying message will print.

Finally, Listing 18.4 provides methods to get information about the entire Car Class. Because an object instance isn't required to use these, they're called class methods.

Listing 18.4. Remainder of the Car Class
37: sub model_name {
38:     return $model_name;
39: }
40: sub models_sold {
41:     return $vin-1;
42: }

Lines 38 & 41: Notice that these methods don't use $self. Why? Because the value returned is the same for every instance of this class. This is information that the class knows about itself and about all the instances of the class.

You can call class methods in any of the following ways:

Car->model_name();  # Probably the preferred style
Car::model_name();  # Using the package name directly, see Hour 17
$car->model_name();  # Using an instance of Car works too.
					

Using the Car Class

Now you have a car class. How do you use it? Using the class, it would look like this:

my $newcar = new Car;

So now $newcar contains a blessed reference into the car class. What does this mean? If you use that reference with the method operator ->, functions within that class will be called, like so:

print $newcar->vin();

When Perl sees this it will call the vin subroutine inside of the Car class. As an added bonus it will pass a copy of that original reference as the first argument to the subroutine vin.

If you want your car to accelerate, you'll need to pass an additional value indicating how much:

print $newcar->accelerate(2);

Again, Perl finds the class that $newcar has been blessed into, finds the accelerate subroutine, and calls it. It will be called with two arguments: the reference (as all methods are) and the value 2. You know that internally, it will be used to increase the car's RPM. But from our perspective, it will just print Vroom!.

Listing 18.5 is a small program to create a sort-of race. Several cars will be created, each one will accelerate a random amount, and the first car to blow up loses. (It's an odd race, but these are odd cars.)

Listing 18.5 . Race Using the Car Class
 1: #!/usr/bin/perl -w
 2:
 3: use Car;
 4:
 5: for($i = 0; $i < 5; $i++) {
 6:     my $car = new Car;
 7:     push @derby, $car;
 8:}
 9:
10: while(1) {
11:     foreach (@derby) {
12:         $_->accelerate(rand(2));
13:     }
14:     foreach(sort { $a->speed() <=> $b->speed() } @derby) {
15:         print $_->vin() . " is going " . $_->speed() . "
";
16:     }
17:     print "
";
18: }

Lines 5–8: Loop five times, create five cars, and push them into the array @derby. This will be the field for the race.

Line 10: This will continue the race until one of the cars blows up. The accelerate method eventually will die when a car goes too fast, causing program termination.

Lines 11–13: Each car in @derby is accelerated by a random amount between 0 and 2.

Line 14: The array @derby is sorted. Because it contains objects, $a and $b take on the value of each object during the sort. The speed of each object is compared and returned.

Line 15: The car number (the VIN) and the current speed are printed for each car in the race.

The end of a sample run looks like this:

2 is going 3.7921142578125
1 is going 5.81231689453125
4 is going 5.99920654296875
5 is going 8.396240234375
3 is going 9.25238037109375
Vroom!
Vroom!
Engine blew up in 3 at Car.pm line 33.

So what are the highlights of using classes—even one as simple as this—as opposed to subroutines from modules?

  • No function names were imported into the program.

  • You can have multiple classes with the same subroutine names and no conflicts. Each object will invoke the correct method from its own class.

  • Each instance of Car is an independent entity. When you accelerate in one, it doesn't affect the other.

  • Class variables can be used to track information on all the Car objects created.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.123.190