http://qs321.pair.com?node_id=329819

flyingmoose has asked for the wisdom of the Perl Monks concerning the following question:

Folks,

One of the things that often holds me back from writing more OO in Perl is not neccesarily the tacked-on nature of it all (which doesn't bother me), but the rather long nature of most constructors. I have written a few OO modules, but I'm still rather new to OO in Perl (but not in other languages).

Two related questions about shortening boilerplate:

(A) Are there any modules that consistantly (emphasis on consistantly) simplify the need to create such boilerplate? CPAN has 8 pages of stuff in the Class:: namespace, and I'm having trouble seperating the wheat and the chaff.

(B) In terms of using named arguments in constructors (which I'm fond of -- especially when dealing with more than 1 or 2 arguments), does anyone see anything wrong with the following code? Note: I know this does not check for required argument types...that's a flaw. Perl/Tk must be doing something slightly different with it's named parameters.

package HeckIfIKnow; sub new { my ($obj,%opts)=@_; return bless {%opts} => (ref $obj || $obj); } ##### Package main: my $foo = HeckIfIKnow::new( -opt1 => '3456', -opt2 => '1234' );

For what it's worth, I really need want Perl6 :)

Replies are listed 'Best First'.
Re: Reducing Perl OO boilerplate
by blue_cowdawg (Monsignor) on Feb 18, 2004 at 02:36 UTC

    Some nits I need to pick with your code:

    1. In
      Package main:
      You have a syntax error. Change the ":" to a ";" and you have it made.
    2. Next, to get the effect you are after
      my $foo = HeckIfIKnow::new( -opt1 => '3456', -opt2 => '1234' );
      becomes
      my $foo = new HeckIfIKnow( -opt1 => '3456', -opt2 => '1234' );
    As you have it written $foo looks like the following when printed via Data::Dumper's Dumper method:
    $VAR1 = bless( { '3456' => '-opt2', '1234' => undef }, '-opt1' );
    which I don't think you were after. After fixing it my way you get:
    $VAR1 = bless( { '-opt2' => '1234', '-opt1' => '3456' }, 'HeckIfIKnow' );
    which looks more like it.


    Peter L. Berghold -- Unix Professional
    Peter at Berghold dot Net
       Dog trainer, dog agility exhibitor, brewer of fine Belgian style ales. Happiness is a warm, tired, contented dog curled up at your side and a good Belgian ale in your chalice.
      I'll cut off the people who will tell you, don't use new, since if you have a function called new in your main:: space, it'll break your program.

      BUT, tell me.. you have a function that's called add, it adds two things, right? You have a function called, parseString, it parses strings, right?

      Why would you have a main:: funciton called new? New what? New time of day? New cheese? A function called new is badly named. :P

      Update: See this node for more details on ->new vs new and someone actually having the problem.


      Play that funky music white boy..
        I'll cut off the people who will tell you, don't use new, since if you have a function called new in your main:: space, it'll break your program.

        Uh, no. Although there is no rule that OO constructors be called "new" it's a pretty standard convention. Some modules (notably DBI) use an alternative constructor name, but it certainly does NOT break anything if you have a subroutine named "new" in package main.

        Why would you have a funciton called new? New what? New time of day? New cheese?
        It's a constructor. It makes a new object. That's why it's called new.

        As for the original question, I'm rather fond of Class::MethodMaker. Works for me, though YMMV.

        Gary Blackburn
        Trained Killer

Re: Reducing Perl OO boilerplate
by dragonchild (Archbishop) on Feb 18, 2004 at 13:24 UTC
    To address the above discussion really quick - do HeckIfIKnow->new( ... ) and sidestep the whole problem. Larry himself has said that indirect method calling was a mistake.

    To address the OP's question ... there are a few commonly-used classes (Class::MethodMaker and Class::Struct) that I would examine, if I were you. Personally, I found the exercise of creating my own OO-base class to be both fun and edifying.

    As for what's wrong ... I can't say this with certainty, but it looks like you're using classes as a way to created HashesThatCanDoStuff. It's still primarily a data structure, just jazzed up. Now, that's fine and all, but it's not OO. OO is three things (q.v. http://c2.com/cgi/wiki?ObjectOriented for more info):

    1. Encapsulation (You can't poke inside the Foo)
    2. Inheritance (You can make a Bar that is-a Foo)
    3. Polymorphism (A method of Foo will do different things depending on the calling context)

    While Perl's OO gives you 2 & 3 without you doing anything, HashesThatCanDoStuff don't have Encapsulation. (In fact, pure data-hiding is non-trivial in Perl, but that's another story.)

    ------
    We are the carpenters and bricklayers of the Information Age.

    Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

      You're right, it's not full blown OO -- and that is a fault with my shortened constructor. I realized this last night when I realized my shortened constructors had pretty much lost that loving feeling (i.e. probably made inheritance about useless). Any suggestions about how to make HashesThatCanDoStuff a little more flexible?

      Aside: I'm not really going for HashesThatCanDoStuff at all, I'm after named parameters, and the data structures would have been, most likely, used to implement Composites via named parameters. After all, a hash is a hash, but what you put in it makes it special :)

        What do you mean by "named parameters"? "Composites"? "named parameters", to me, are a way of passing information to a function. It doesn't have much to do with OO, specifically.

        I would look at the problem differently.

        • What system are you trying to model?
        • What are the things that do stuff in that system?
        • What is the minimal set of actions that each thing needs to be able to do?
        • How will those things interact?

        Once you know those things, then you can start working out what attributes each class needs to have in order to fulfill its requirements. Then, and only then, can you figure out where that information needs to come from. You may find out that most of it shouldn't come from the client at all!

        Now, to answer the question "How do I pass attribute/value pairs to a constructor?" ... your very short constructor is actually the canonical (if non-resilient) way of doing it. *shrugs* Personally, I'd never use that outside of golf, but that's just me. There's CPAN code you use every day that uses that (or similarly terse) constructs. Heck, CGI doesn't even use strict!

        ------
        We are the carpenters and bricklayers of the Information Age.

        Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

Re: Reducing Perl OO boilerplate
by tilly (Archbishop) on Feb 18, 2004 at 22:57 UTC
    I'm amazed that merlyn didn't pipe up in this thread. :-)

    Your ref construction is the one he disliked at ref($proto) - just say no!. (Further explanation available at A Cat's eye view of OO.) Unless you really think that you will be using new as a clone() method, you can drop that one. Furthermore, while you are at it, the usual way that new will be called is with the package name (aka class) as the first argument. Calling it $obj is misleading at best. Furthermore if you have gone to the work of creating a hash, why dereference it and build another to take a reference to that? You had a perfectly good hash.

    With those changes your code turns into:

    package HeckIfIKnow; sub new { my ($class, %opts) = @_; return bless(\%opts, $class); }
    Now for where you call it. The keyword is package, not Package. You need a ;, not a :. You have to make it a method call, not a function call. And given that you don't have to tell data from metadata, there is no need to make all of your options have - in the name (and it is customary not to). With that in mind the rest of your code becomes:
    package main; my $foo = HeckIfIKnow->new( opt1 => '3456', opt2 => '1234', );
    Yes, you can reduce the boilerplate further than this with a variety of modules. But my advice is that until you are comfortable with the mechanics of how Perl does things, that you put off piling on shortcuts and complications.
      Thanks, good input. This was all hand typed with a small font, so some of the typos were just that... but I'll take your advice on not needing a clone() and the hash efficiency. You are probably right about needing to do it by hand for a while first too. I guess I was just getting excited about wanting to play with my new toys. Thanks!
Re: Reducing Perl OO boilerplate
by qq (Hermit) on Feb 18, 2004 at 13:29 UTC

    I'm very fond of Class::MakeMethods. It starts simple, and grows to whatever level of complexity you can handle.

    qq