Last week we started our Slot module and defined a constructor. We tested it my creating a new object and accessing its ‘credits’ key, which we said was a Bad Thing(tm). Let’s add an object routine to our module that returns the current value of the ‘credits’ attribute (this is called an accessor method):
sub credits {
my $self = shift;
return $self->{credits};
}
It is OK to access the datastructure directly inside the object module, it is the outside world that should get its information from object methods. This method shifts off the first argument, which will be the object itself (a reference to the hash) and returns the value of the ‘credits’ key in that hash. That’s it. We can now modify the little test script from last week to use this method rather than accessing the value directly:
#!/usr/bin/perl -w
use strict;
use Slot;
my $slot = Slot->new(100);
print $slot->credits(),"\n";
Why is this better than directly accessing the hash key itself? Because, as a user of this module, we aren’t supposed to know (or care) that the object is a hash. It could have been an array reference, or it could be in the next release. If we wrote code that accessed the object as a hash, and then we downloaded the latest version and the author changed it to use an array reference underneath, all our current code would break. Just to show you, let’s rebuild the module using an array:
package Slot;
use strict;
sub new {
my $class = shift;
my $credits = shift || 100;
my $self = [ $credits, # credits
1, # bet
0, # win
0, # paid
undef, # spin
];
return bless $self, $class;
}
sub credits {
my $self = shift;
return $self->[0];
}
1;
__END__
Maybe the module author decided that array accesses are faster (not enough to warrant using them in this case for sure, but it’s an example), and changed the module to this version. Our new code that uses the credits() method still works just fine, but our old code that used $slot->{credits} won’t work at all now.
And that’s a very important point about objects — the object encapsulates the data and provides an interface for us to use. Just like we don’t need to know how a real slot machine (or radio) works inside, we shouldn’t need to know how our object is built on the inside (unless we are the ones building it of course). That means, as an object builder, you need to provide any methods the user might need to operate the object. This means you will be free in the future to change the underlying object and the user never needs to know and doesn’t have to worry about a new version breaking old programs that use it.
Let’s quickly add another attribute method, the bet() method:
sub bet {
my $self = shift;
$self->{bet} = shift || $self->{bet};
}
This is a dual purpose method — we not only have a way to retrieve the current betting amount, we can also set it at the same time. This is a useful way of creating accessor methods instead of creating separate get_bet() and set_bet() methods. If an argument is passed to this method and it is not 0, we set the bet attribute to the new value and return it, otherwise we use the old value. We haven’t done any error checking to ensure an integer was passed in, that is an exercise for the reader.
We haven’t yet actually defined how our slot machine will work, and that will be the subject of next week’s discussion.
*****



