Object Oriented Perl : Query

T

Thens

I have a base class called as Weapon and I have derived classes 'Gun'
and 'RocketLauncher'. I need to instantiate Gun or RocketLauncher
based on a parameter say distance from the target. If the distance is
less than 20m I need a gun and if it is more I will need a
RocketLauncher.

This is my attempt at a Perl code that does this. Is this the right
way to do this. Any pointers will be of help.

#! /usr/local/bin/perl -w

package Weapon;

use strict;

sub new {
my ( $self ) = @_;
my $class = ref($self) || $self;
return bless { }, $class;
}

sub hitPoints {
my ( $self ) = @_;
return $self->{hitpoints};
}

sub fire {
my ( $self, $target ) = @_;
print "Destroyed the target !! ", $self->hitPoints;
#$target->hurt($self->hitPoints);
}

package Gun;
use strict;
use base 'Weapon';

sub new {
my ( $self ) = @_;
my $class = ref($self) || $self;
$self = new Weapon();

$self->{type} = 'GUN';
$self->{hitpoints} = 10;

return $self;
}


package RocketLauncher;
use strict;
use base 'Weapon';

sub new {
my ( $self ) = @_;
my $class = ref($self) || $self;
$self = new Weapon();

$self->{type} = 'ROCKET';
$self->{hitpoints} = 100;
return $self;

}

1;
 
A

Anno Siegel

Thens said:
I have a base class called as Weapon and I have derived classes 'Gun'
and 'RocketLauncher'. I need to instantiate Gun or RocketLauncher
based on a parameter say distance from the target. If the distance is
less than 20m I need a gun and if it is more I will need a
RocketLauncher.

This is my attempt at a Perl code that does this. Is this the right
way to do this. Any pointers will be of help.

Do what? I see no attempt to create one or the other depending on a
parameter.

I don't see any gross errors in the class setup. However, you should
switch on warnings as a matter of course. Also, storing the weapon
type in the object is not really necessary, since ref() can always tell
you that.

[code left in place for reference]

Anno
 
A

Ala Qumsieh

Thens said:
This is my attempt at a Perl code that does this. Is this the right
way to do this. Any pointers will be of help.

There is a subtle mistake in your code. Have a look at the following
pods for more info on Perl's OO mechanisms:

perlboot
perltoot
perlobj

Also, if you're serious about using Perl's OO, then reading Damian
Conway's "Object Oriented Perl" is a must.
#! /usr/local/bin/perl -w

package Weapon;

use strict;

sub new {
my ( $self ) = @_;
my $class = ref($self) || $self;
return bless { }, $class;
}

sub hitPoints {
my ( $self ) = @_;
return $self->{hitpoints};
}

sub fire {
my ( $self, $target ) = @_;
print "Destroyed the target !! ", $self->hitPoints;
#$target->hurt($self->hitPoints);
}

package Gun;
use strict;
use base 'Weapon';

sub new {
my ( $self ) = @_;
my $class = ref($self) || $self;
$self = new Weapon();

Here, $self is an instance of the Weapon class. It is NOT a Gun object.
If you add the following:

print ref $self;

You will get Weapon, not Gun. Depending on your application, that could
not be a problem, but you're not getting true inheritance here. As long
as your Gun package does not have any distinct methods of its own (or
override any methods in Weapon, other than new()), then you are fine.
But if you require true inheritance, then you need to make $self an
instance of Gun. You do that by using SUPER:: like this:

$self = $class->SUPER::new;

This will look for the first new() method defined in the base class(es)
of Gun and will invoke it with $class as the parameter. So, it will call
the new() method in Weapon, which will return a hash ref blessed into
the Gun class.
$self->{type} = 'GUN';
$self->{hitpoints} = 10;

return $self;
}

Ditto for RocketLauncher.

--Ala
 
A

Anno Siegel

Ala Qumsieh said:
Thens said:
This is my attempt at a Perl code that does this. Is this the right
way to do this. Any pointers will be of help.

There is a subtle mistake in your code. Have a look at the following
[snippage]
sub new {
my ( $self ) = @_;
my $class = ref($self) || $self;
$self = new Weapon();

Here, $self is an instance of the Weapon class. It is NOT a Gun object.

Ah, I hadn't noticed when I certified this code as "free of gross errors"
in another followup. This is a gross error in classes that do inheritance.

Anno
 
P

Peter J. Acklam

Thens> sub new {
Thens> my ( $self ) = @_;
Thens> my $class = ref($self) || $self;
Thens> return bless { }, $class;
Thens> }

Please see <http://www.stonehenge.com/merlyn/UnixReview/col52.html>
about why that ref($self) thing is bad.

You recommend not using ref($self) because some people might guess
incorrectly what new() does when it is used as an instance method.

How about this: If people guess method behaviour rather than
reading the docs their votes don't count. Allow $object->new(),
document the behaviour, and expect people to read it.

Peter
 
A

Anno Siegel

Peter J. Acklam said:
You recommend not using ref($self) because some people might guess
incorrectly what new() does when it is used as an instance method.

How about this: If people guess method behaviour rather than
reading the docs their votes don't count. Allow $object->new(),
document the behaviour, and expect people to read it.

The point isn't so much that people fail to look up the definition,
but that they (specifically, readers of code that uses the class)
are forced to look it up. When you know what "Class->new(...)" does,
you also know what "ref( $obj)->new(...)" does, but for the meaning
of "$obj->new(...)" you must go to the documentation.

The shortcut of calling ->new as an object method is inherently
unclear. Unless there are massive advantages in allowing it (there
aren't in most classes), I think it is better to leave it out. Its
use as a matter of course smacks of cargo cult.

Anno
 
B

Bryan Castillo

Thens said:
I have a base class called as Weapon and I have derived classes 'Gun'
and 'RocketLauncher'. I need to instantiate Gun or RocketLauncher
based on a parameter say distance from the target. If the distance is
less than 20m I need a gun and if it is more I will need a
RocketLauncher.

This is my attempt at a Perl code that does this. Is this the right
way to do this. Any pointers will be of help.

I added some comments and code to your code. There was a problem with
the new methods in your child classes, they would have actually
returned you the wrong class type. All of the new methods would have
given you plain Weapon objects.
You don't show any code about how to create a class depending on the
conditions of distance as your statements above led me to belive the
code would try to do. Was your question more along the lines of "Did
I create me classes properly to use inheritance?"

Or pehaps you were trying to find out how to have a class
automatically change its type in the fire method? So when you invoke
fire it checks the target distance and then calls fire on the proper
weapon. Here is a class below
which will act like a weapon, but delegate out to other weapons in the
fire
method depending on the distance.

package SmartWeapon;
use strict;
use base 'Weapon';

sub new {
my $self = Weapon::new(@_);
$self->{rocket} = RocketLauncher->new();
$self->{gun} = Gun->new();
}

sub fire {
my ($self, $target) = @_;
if ($target->getDistance() < 20) {
$self->{gun}->fire($target);
}
else {
$self->{rocket}->fire($target);
}
}
#! /usr/local/bin/perl -w

package Weapon;

use strict;

sub new {
my ( $self ) = @_;
my $class = ref($self) || $self;
return bless { }, $class;
}

sub hitPoints {
my ( $self ) = @_;
return $self->{hitpoints};
}

sub fire {
my ( $self, $target ) = @_;
print "Destroyed the target !! ", $self->hitPoints;
#$target->hurt($self->hitPoints);
}

package Gun;
use strict;
use base 'Weapon';

sub new {

# you could allow your parent class to intialize everything
# it needs to like this.
my $self = Weapon::new(@_);

# Don't need this stuff
# my ( $self ) = @_;
# my $class = ref($self) || $self;
# $self = new Weapon();

# now you can initialize the variables needed for the child class
$self->{type} = 'GUN';
$self->{hitpoints} = 10;

return $self;
}


package RocketLauncher;
use strict;
use base 'Weapon';

sub new {

my $self = Weapon::new(@_);

# don't need this stuff
 
P

Peter J. Acklam

The shortcut of calling ->new as an object method is
inherently unclear. Unless there are massive advantages
in allowing it (there aren't in most classes), I think it
is better to leave it out. Its use as a matter of course
smacks of cargo cult.

The "$class = ref($obj) || $obj;" construction is used many
places in the Perl docs and the Perl standard modules, so
its not strange that people choose to use it.

Anyway, if it's use is disallowed an appropriate error
message should be given. Maybe something like this:

sub new {
my $class = shift;
croak "new(): not an instance method" if ref $class;
...
}

Peter
 
A

Anno Siegel

Peter J. Acklam said:
The "$class = ref($obj) || $obj;" construction is used many
places in the Perl docs and the Perl standard modules, so

Yes. Much to the regret of some of us. It's a pretty idiom,
and it has been propagated by well-renowned people. (I'm
tempted to say, people who should know better :)
its not strange that people choose to use it.

No, it isn't. It is one of the harder design decisions *not* to
do something on behalf of the user that seems to offer itself.

If it is easy to do, and can as well be done outside the routine,
think twice before doing it inside (and forcing it upon the user).
You wouldn't sort a list result just because some users may want
it sorted and others won't care. In this case, the performance
penalty is obvious, so experienced programmers won't do it.

The disadvantage of making ->new object-callable is "only" that
the reader can't guess what exactly the object call does. That's
less visible drawback, and so the idiom had a chance to spread.
Anyway, if it's use is disallowed an appropriate error
message should be given. Maybe something like this:

sub new {
my $class = shift;
croak "new(): not an instance method" if ref $class;
...
}

Perhaps, but only because users may have come to expect it to be
callable that way. Basically, ->new is a class method and the
user has no business calling it through an object. If they
do, they may as well deal with the consequences.

Anno
 
P

Peter J. Acklam

Yes. Much to the regret of some of us. It's a pretty idiom,
and it has been propagated by well-renowned people. (I'm
tempted to say, people who should know better :)

Personally, I have let "$obj->new" be exactly the same as
"ref($obj)->new" which wasn't even mentioned on Randal's page.
Perhaps, but only because users may have come to expect it to be
callable that way. Basically, ->new is a class method and the
user has no business calling it through an object. If they do,
they may as well deal with the consequences.

Users all to often do things they have no business of doing. :)

I think that *all* inappropriate or incorrect usage should display
a descriptive error message.

Peter
 
A

Anno Siegel

Peter J. Acklam said:
Personally, I have let "$obj->new" be exactly the same as
"ref($obj)->new" which wasn't even mentioned on Randal's page.

I'm not saying that it is fundamentally wrong to do that. But the
decision to let a method (any method, not just ->new) be callable
both ways is a design decision not to be taken lightly. There are
methods that naturally work that way, most prominently those that
ignore their first argument. Those are fair game, the user doesn't
even have to know if they're class- or object methods. The call of
a class method through an object still looks confusing because it
looks like the individual object mattered while it doesn't.

The ->new method is not of this kind (it *does* use its first argument),
and in those cases there should be a solid reason for enabling the
potentially misleading call. "It's easy to do and might come in handy"
is not a solid reason.
Users all to often do things they have no business of doing. :)
True.

I think that *all* inappropriate or incorrect usage should display
a descriptive error message.

Well, that's an ideal, probably even an attainable ideal with the right
technology, but in practice I tend to make a difference.

Errors that I expect to be caused (and seen) by the end user must be
caught and reported (in the end-user's terms, as far as possible).
Those are mostly what would be classified as data- or input errors.

Another type is usage errors. They happen through inappropriate
usage of the class (module, software, library, whatever), and are
only seen while an application of the class is written, not when
it's debugged and running. Those can be caught by whatever internal
mechanism catches them. So I wouldn't check the first parameter of
a class method that isn't documented to be callable through an object
but let Perl die on whatever the consequences are.

Anno
 
A

Anno Siegel

? the Platypus {aka David Formosa} said:
(e-mail address removed) (Peter J. Acklam) writes:

[...]
Users all to often do things they have no business of doing. :)

I think that *all* inappropriate or incorrect usage should display
a descriptive error message.

Isn't it the unix philosophy (and in turn perl's) to not forbid
inapproprate usage or "incorrect" usage for fear that it would also
prevent people from doing something brillent.

Un-planned for, un-thought of usage is what shouldn't be prohibited.
Inappropriate and incorrect usage (as in calling a class method through
an object) isn't going to get you anywhere. No philosophy will change
that.

Anno
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,023
Latest member
websitedesig25

Latest Threads

Top