arunhorne has asked for the wisdom of the Perl Monks concerning the following question:
Monks
I am writing a simple object to represent an edge in a graph. As such the object has three scalar properties, from_node, to_node and label. I want to create three possible constructors:
Edge->new(); # all properties set to undef
Edge->new("from_node","to_node"); # label set to undef
Edge->new("from_node","to_node","label"); # all props set
Can anyone show me how to do this? My present constructor code is below but handles only the first constructor prototype:
sub new()
{
my $class = shift;
my $self = {};
$self->{'from_node'} = undef;
$self->{'to_node'} = undef;
$self->{'label'} = undef;
bless $self, $class;
return $self;
}
____________
Arun
Re: Multiple Prototypes for Object Constructor
by jdporter (Paladin) on May 22, 2003 at 14:41 UTC
|
Please note: prototypes are ignored on method calls. (Sorry!)
# reworked slightly, for conciseness.
sub new
{
my %self;
( my $class, $self{qw( from_node to_node label )} ) = @_;
bless \%self, $class;
}
The point is that the subroutine can deal with the arguments itself, including the number of arguments, in any way it sees fit.
If you want specifically to disallow the Edge->new("from_node"); case (not unreasonable), then you could do something like
sub new
{
@_ == 2 and die "Error: can't set just one of the links!";
my %self;
( my $class, $self{qw( from_node to_node label )} ) = @_;
bless \%self, $class;
}
or even
sub new
{
my %self;
( my $class, $self{qw( from_node to_node label )} ) = @_;
$self{'from_node'} && ! $self{'to_node'} and
die "Error: can't set just one of the links!";
bless \%self, $class;
}
jdporter The 6th Rule of Perl Club is -- There is no Rule #6. | [reply] [d/l] [select] |
Re: Multiple Prototypes for Object Constructor
by adrianh (Chancellor) on May 22, 2003 at 14:48 UTC
|
package Edge;
sub new {
my ($class, %param) = @_;
bless {
map { $_ => $param{$_} } qw(from_node to_node label)
}, $class;
};
use Data::Dumper;
print Dumper(Edge->new);
print Dumper(Edge->new(from_node => 'foo', to_node => 'bar'));
print Dumper(Edge->new(from_node => 'foo', to_node => 'bar', label =>
+'ni'));
__END__
Produces:
$VAR1 = bless( {
'to_node' => undef,
'from_node' => undef,
'label' => undef
}, 'Edge' );
$VAR1 = bless( {
'to_node' => 'bar',
'from_node' => 'foo',
'label' => undef
}, 'Edge' );
$VAR1 = bless( {
'to_node' => 'bar',
'from_node' => 'foo',
'label' => 'ni'
}, 'Edge' );
This gives you extra flexibility since you do not tie yourself down to a specific number or order of parameters. In Perl6 it will even be efficient :-)
| [reply] [d/l] |
Re: Multiple Prototypes for Object Constructor
by broquaint (Abbot) on May 22, 2003 at 14:45 UTC
|
I think this is a fairly standard approach
{
## accepted attributes
my %options = map { $_ => undef } qw/ from_node to_node label /;
sub new {
my $class = shift;
bless {
%options,
map { exists $options{$_} ? ( $_ => 1 ) : () } @_
}, $class
}
}
And hopefully that should do the right thing for your given case.
HTH
_________ broquaint
update: code complies with OP request | [reply] [d/l] |
Re: Multiple Prototypes for Object Constructor
by dws (Chancellor) on May 22, 2003 at 19:07 UTC
|
I am writing a simple object to represent an edge in a graph.
Before you proceed with writing a no-argument new() constructor, consider whether an unconnected edge object would by semantically valid in your system. If not, drop that constructor.
When you can construct a system such that no object is ever in a semantically invalid state between method invocations, the system is a lot easier to test, and a lot more robust.
That said, an optional trailing parameter is easy to handle.
sub new {
my $class = shift;
my($from, $to, $label) = @_;
my $self = {};
$self->{'from_node'} = $from;
$self->{'to_node'} = $to;
$self->{'label'} = $label; # which may be undef
bless $self, $class;
}
This relies on a side-effect of assiging into a list of variables from an array: If the array is smaller than the list, unmatched variables get assigned undef.
This is less code than the named argument forms that other people are suggesting, but is also less flexible should you decide to support other edge attributes that can be set at construction time.
| [reply] [d/l] [select] |
Re: Multiple Prototypes for Object Constructor
by Abigail-II (Bishop) on May 22, 2003 at 15:09 UTC
|
Perl does not have a way to define more than one subroutine
with the same name. There's no dispatch based on the signature. Not that Perl is doing anything with prototypes
belonging to methods - after all, prototypes are a compile-time thing, and method lookup is done at runtime.
However, you are free to inspect the arguments to subroutines,
and do different things based on the number of arguments.
Arguments are found in @_, using an array in
scalar context gives you the number of elements, and Perl has
an if-then-elsif-else construct, which gives you
all the building blocks to do what you want.
Abigail | [reply] [d/l] [select] |
|
| [reply] [d/l] |
Re: Multiple Prototypes for Object Constructor
by nite_man (Deacon) on May 22, 2003 at 15:41 UTC
|
I would like to suggest my way to solve this problem. In my mind, it's better to use _init() method for initialization your object if it needs:
package Edge;
use vars qw(%FIELDS);
# Define class properties
use fields qw(from_node to_node label);
{
# Default values for class properties
my $_def_values = { from_node => undef,
to_node => undef,
label => undef,
};
sub _class_def { $_def_values; }
}
# Constractor
sub new {
my $proto = shift;
my $class = ref($proto) || $proto;
# Define default values for object instance
my $defaults = ref($proto) ? $proto : $class->_class_def();
my $self = {};
for(keys %$defaults) { $self->{$_} = $defaults->{$_} }
+
bless $self, $class;
$self->_init(@_) if @_;
return $self;
}
# Initialization of object
sub _init {
my $self = shift;
my %params = @_;
for my $par(keys %params) {
$self->{$par} = $params{$par} if exists $FIELDS{$par};
}
}
--------------------------------
SV* sv_bless(SV* sv, HV* stash);
| [reply] [d/l] [select] |
|
| [reply] |
|
Thanks for correction. Maybe you are right. My experiense is not enough for confirmation or refutation your assertion. I use a book 'Object Oriented Perl' by Damian Conway and there are two ways to do a clon of existing object (p. 104). In my mind, for person, who use some class, it's easier to call one method new() for creation or copy of an object intead of calling separate methods for this.
--------------------------------
SV* sv_bless(SV* sv, HV* stash);
| [reply] [d/l] |
Re: Multiple Prototypes for Object Constructor
by perrin (Chancellor) on May 22, 2003 at 19:58 UTC
|
Others have given good answers about how to deal with this, but I just wanted to point out that in perl you can have multiple constructors and they don't need to be called "new." I've used this to create database-backed objects which can be instantiated with a call to create() with new data or a call to fetch() with an object ID. | [reply] |
|
|