Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Multiple Prototypes for Object Constructor

by arunhorne (Pilgrim)
on May 22, 2003 at 14:33 UTC ( [id://260074]=perlquestion: print w/replies, xml ) Need Help??

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

Replies are listed 'Best First'.
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.

Re: Multiple Prototypes for Object Constructor
by adrianh (Chancellor) on May 22, 2003 at 14:48 UTC

    The normal Perl idiom is to use named parameters and create a hash. Something like this:

    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 :-)

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

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.

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

      Or if you are lazy, just grab Class::Multimethods. It presents a solution that is more open-closed than using an if-then-elsif-else. For example: you can add multi methods to the subclass without touching the superclass.

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);
    
        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);
        
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.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://260074]
Approved by broquaint
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chanting in the Monastery: (4)
As of 2024-04-24 01:39 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found