Perl OO style 
Author Message
 Perl OO style

With some OO languages, a different method is invoked depending on the
arguments passed.  In Perl, things don't work this way.  What is the
commonly accepted idiom for doing things like this?

As a concrete example, I have strings like /foo[1]/bar[2]/baz[4] which
identify a node in a tree (tree with labeled nodes, in this case):
from the root node, go to the first foo child.  From there, go to the
second bar child, and to the fourth baz child from there.  The
internal representation is as an array of names and an array of
indices.

Now, a new SimplePath can be constructed as follows:

  - From an existing SimplePath, by cloning.
  - From a string in the format "/foo[1]/bar[2]/baz[4]".
  - From a parent SimplePath, plus a name and an index.
  - From a list of names and a list of indices.

Right now, I have a constructor which accepts a hash as argument, so
you can say this:

    $p = new SimplePath( string => '/foo[1]/bar[2]' );

Or this:

    $p = new SimplePath( parent => $p, name => 'baz', index => 4 );

But this means that the constructor is a hodgepodge of nested if
statements to determine which parameters were passed.  Powerful, but
difficult to maintain because of the logic.

How do Perl gurus approach this kind of thing?  I mean, I know,
TIMTOWTDI, but what's an especially clean, or maintainable, or
easy-to-understand, or concise, way to formulate this?

kai
--
I like BOTH kinds of music.



Mon, 27 Jan 2003 03:00:00 GMT  
 Perl OO style



[snip]

Quote:
> Now, a new SimplePath can be constructed as follows:

>   - From an existing SimplePath, by cloning.
>   - From a string in the format "/foo[1]/bar[2]/baz[4]".
>   - From a parent SimplePath, plus a name and an index.
>   - From a list of names and a list of indices.

> Right now, I have a constructor which accepts a hash as argument, so you
> can say this:

>     $p = new SimplePath( string => '/foo[1]/bar[2]' );

> Or this:

>     $p = new SimplePath( parent => $p, name => 'baz', index => 4 );

> But this means that the constructor is a hodgepodge of nested if
> statements to determine which parameters were passed.  Powerful, but
> difficult to maintain because of the logic.

Some thoughts:

Your indirect object notation will cause you problems eventually.

Better to look at it as:

        SimplePath::->new( ... );

There is nothing magic about "new". It's just the name of a subroutine.
You aren't required or necessarily expected to use this name.

TMTOWTDI, indeed. Some possibilities come to mind:

1. Multiple constructors with different names:

        SimplePath::->newFromString( 'whatever' );
        SimplePath::->newFromParent( ... );

2. When passing objects around (as opposed to unblessed scalars),
you can use what's known as "multiple dispatch" (partial code here):

package A;
sub new
{
        my $class = shift;
        my $obj = shift;
        my $self = bless({}, $class);

Quote:
}

sub initializeFromB
{
        my $self = shift;
        my $b = shift;
        $self->name( $b->name() ); # or whatever you want

Quote:
}

package B;
sub initializeA
{
        my $self = shift;
        my $newA = shift;

Quote:
}

package main;
my $someB = B::->new( ... );
my $newA = A::->new( $someB );

3. You can use a table lookup (though this doesn't handle inheritance as written):

package A;
sub initializeFromScalar { ... }
sub initializeFromHash { ... }
sub initializeFromXYZ { ... }
sub initializeFromUnknown { ... }

my %LookupTable = (
        'SCALAR' => \&initializeFromScalar,
        'HASH' => \&initializeFromHash,
        'XYZ' => \&initializeFromXYZ
);

sub new
{
        my $class = shift;
        my $arg = shift;
        my $initializer = $LookupTable{ ref( $arg ) } || \&initializeFromUnknown; # use exists here
        my $self = bless( {}, $class );

Quote:
}

4. use can() to get a code ref dynamically:

sub initializeFromXYZ { ... }
sub initializeFromSCALAR { ... }

sub new
{
        my $class = shift;
        my $arg = shift;
        my $self = bless( { }, $class );
        my $initializer = UNIVERSAL::can( $class, 'initializeFrom' . ref($arg) );
        if (defined($initializer))
        {

        }
        else { ... }

Quote:
}

--
Ned Konz
currently: Stanwood, WA

homepage:  http://bike-nomad.com


Mon, 27 Jan 2003 03:00:00 GMT  
 Perl OO style

Quote:


>>With some OO languages, a different method is invoked depending on
>>the arguments passed.  In Perl, things don't work this way.  What is
>>the commonly accepted idiom for doing things like this?

> You might be talking about multiple dispatch, which is similar to
> function overloading except that it applies to methods rather than
> functions.  Damian Conway wrote an article on MD for issue #17 of
> The Perl Journal.  In it he explains his module Class::Multimethods,
> which may be of some use to you.

Very good.  I couldn't find that issue of TPJ (and I don't have online
access), but I read the documentation for that module, and indeed,
this looks just like what I want.

So many packages on CPAN to learn about!

kai
--
I like BOTH kinds of music.



Mon, 27 Jan 2003 03:00:00 GMT  
 Perl OO style

Quote:

>With some OO languages, a different method is invoked depending on the
>arguments passed.  In Perl, things don't work this way.  What is the
>commonly accepted idiom for doing things like this?

You might be talking about multiple dispatch, which is similar to
function overloading except that it applies to methods rather than
functions.  Damian Conway wrote an article on MD for issue #17 of The
Perl Journal.  In it he explains his module Class::Multimethods, which
may be of some use to you.

He notes in a sidebar (Listing 4) that method overloading (like I think
you're talking about) is something you get for free when you have
multimethods + subroutines.

If this doesn't fit the bill, then I guess you'll probably have to use
if/else statements.  Alternatively, create methods like
new_from_SimplePath(), new_extends_SimplePath(), etc. that the user can
call explicitly or that you can dispatch to based on whether given keys
exist in the hash of arguments to your new() method.



Mon, 27 Jan 2003 03:00:00 GMT  
 
 [ 4 post ] 

 Relevant Pages 

1. COMIX and Delphi? (Apollo)

2. NLS usage in Delphi1.0 with Oracle?

3. OO Style Q: Argument checking?

4. OO Style Q: Argument checking?

5. Converting mmdf-style maildrops to sendmail-style maildrops

6. multimedia in pascal

7. Sorting DBISAM Table

8. Perl syntax and beyond (was: Re: Perl style and module searches)

9. OO Perl: accessing the base classes methods

10. My first OO perl script: array plus iterator?

11. Perl OO question

12. OO Inheritance in Perl 5

 

 
Powered by phpBB® Forum Software