11.2 Testing Your Objects for Good Behavior
Besides providing a
place for you to put universally available methods, the
UNIVERSAL package comes preloaded with two very
useful utility methods: isa and
can. Because these methods are defined in
UNIVERSAL, they are automatically available to all
objects.
The isa
method tests to see whether a given class or instance is a member of
a given class or a member of a class that inherits from the given
class. For example, continuing on with the
Animal-family from the past chapters:
if (Horse->isa("Animal")) { # does Horse inherit from Animal?
print "A Horse is an Animal.\n";
}
my $tv_horse = Horse->named("Mr. Ed");
if ($tv_horse->isa("Animal")) { # is it an Animal?
print $tv_horse->name, " is an Animal.\n";
if ($tv_horse->isa("Horse")) { # is it a Horse?
print "In fact, ", $tv_horse->name, " is a Horse.\n";
} else {
print "...but it's not a Horse.\n";
}
}
This is handy when you have a heterogeneous mix of objects in a data
structure and want to distinguish particular categories of objects:
my @horses = grep $_->isa("Horse"), @all_animals;
The result will be only the horses (or race horses) from the array.
Compare that with:
my @horses_only = ref $_ eq "Horse", @all_animals;
which picks out just the horses because a
RaceHorse won't return
Horse for ref.
In general, you shouldn't use:
ref($some_object) eq "SomeClass"
in your programs because it prevents future users from subclassing
that class. Use the isa construct as given
earlier.
One downside of the isa
call here is that it works only on blessed references or scalars that
look like class names. If you happen to pass it an unblessed
reference, you get a fatal (but trappable) error of:
Can't call method "isa" on unblessed reference at ...
To call isa more
robustly, don't call it as a method. Instead, call
it as a subroutine:
if (UNIVERSAL::isa($unknown_thing, "Animal")) {
... it's an Animal! ...
}
This works regardless of what $unknown_thing
contains.
As in the case of isa, you can test for acceptable
behaviors with the can method. For example:
if ($tv_horse->can("eat")) {
$tv_horse->eat("hay");
}
If the result of can is true, then somewhere in
the inheritance hierarchy, a class has defined an
eat method. Again, the caveats about
$tv_horse being only either a blessed reference or
a class name as a scalar still apply, so the robust solution when you
might deal with nearly anything looks like:
if (UNIVERSAL::can($tv_horse, "eat")) { ... }
Note that if you
defined UNIVERSAL::fandango earlier, then:
$object->can("fandango")
always returns true because all objects can do the fandango.
|