Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Re: (jeffa) Re: My first stab at OO perl...

by Theseus (Pilgrim)
on Jul 16, 2002 at 17:09 UTC ( [id://182163]=note: print w/replies, xml ) Need Help??


in reply to (jeffa) Re: My first stab at OO perl...
in thread My first stab at OO perl...

Maybe I just don't understand WHY the ref($cargocult) idea is so bad. To me, it makes perfect sense... If someone calls the constructor on an instance rather than the class, it forces it to act as if it were called on the class. Isn't this "correct" in most cases?

I tried putting the line you tried and then calling my constructor on an already running instance, and it does return an object, but one blessed incorrectly(read: differently) for my purposes. When I try and invoke any methods on this object that is blessed into "Expense=HASH(0x123456)" and not "Expense" it gives me an error.

So, basically, my question is this... Can you please give me a real world scenario or two where I would want to call $object->new and not CLASS->new, and then an example of how you use that new returned object's methods, since it is not blessed into the correct class? Here's the code I used to come to these conclusions:

use Data::Dumper; $exp1 = new Expense; $exp1->place("place1"); $exp1->description("desc1"); $exp1->amount("1"); $exp1->details("details1"); $exp2 = new Expense; $exp2->place("place2"); $exp2->description("desc2"); $exp2->amount("2"); $exp2->details("details2"); $exp3 = $exp1->new; $exp3->place("place3"); $exp3->description("desc3"); $exp3->amount("3"); $exp3->details("details3"); print Data::Dumper->Dump( [ $exp1, $exp2, $exp3 ] , [ qw( exp1 exp2 ex +p3 ) ] ); { package Expense; sub new { #my $either = shift; #my $class = ref($either) || $either; my $class = shift; my $self = {}; $self->{_DESCRIPTION} = undef; $self->{_PLACE} = undef; $self->{_AMOUNT} = undef; $self->{_ID} = undef; $self->{_DETAILS} = undef; bless $self, $class; } sub place { my $self = shift; my $hiddenkey = "_PLACE"; if (@_) { $self->{$hiddenkey} = shift; } return $self->{$hiddenkey}; } sub description { my $self = shift; my $hiddenkey = "_DESCRIPTION"; if (@_) { $self->{$hiddenkey} = shift; } return $self->{$hiddenkey}; } sub amount { my $self = shift; my $hiddenkey = "_AMOUNT"; if (@_) { $self->{$hiddenkey} = shift; } return $self->{$hiddenkey}; } sub id { my $self = shift; my $hiddenkey = "_ID"; if (@_) { $self->{$hiddenkey} = shift; } return $self->{$hiddenkey}; } sub details { my $self = shift; my $hiddenkey = "_DETAILS"; if (@_) { $self->{$hiddenkey} = shift; } return $self->{$hiddenkey}; } } #end package

Replies are listed 'Best First'.
(jeffa) 3Re: My first stab at OO perl...
by jeffa (Bishop) on Jul 16, 2002 at 17:26 UTC
    Calling an instance constructor? Yuck! If you want to clone an object, use the right tool, such as Clone:
    use strict; use Clone qw(clone); my $foo = foo->new(5); my $bar = clone($foo); print $bar->id(), "\n"; package foo; use strict; sub new { my ($class,$id) = @_; my $self = { id => $id || 42, }; return bless $self,$class; } sub id { my ($self,$id) = @_; return $self->{id} unless $id; $self->{id} = $id; }
    or provide your own clone() method for the class. I really recommend that you read TheDamian's excellent book, Object Oriented Perl, by the way.

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
      So you agree that calling instance constructors is a bad thing, but you don't agree that inserting code to prevent it from being done(or at least return the same result as if it had been done correctly) is a good thing? That's exactly what I don't understand.

      I personally would never call $newobject = $oldobject->new, but I can't say that nobody who uses a class I design won't, and I want to account for that.

      I'd like to hear your thoughts on the subject(and I'd love to hear merlyn's, if he sees this thread).

        sub new { my $classname = shift; return undef if ref $classname; # Other stuff here. }
        Just because Perl doesn't do it automatically doesn't mean you can't write gatekeepers on your own functions.

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

        Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

      Although you should know that Damian uses ref($caller) extensively in his book (see all the CD::Music examples). I'm not saying that's right, but it makes the context of the recommendation to read the book sound sort of self-contradictory.

      Me, I've been castigated by merlyn for using ref($caller), and I've (sort of) come around to his view. That is, $obj->new() just always struck me as a perfectly reasonable way to obtain a new, 'blank' object of the object's class. Expecting it to clone just never made any sense to me (if you meant it to clone, you'd've named it clone(), right?). But I've come to sense that I'm in the minority on this one, and calling $obj->new() hasn't ever been truly necessary in my code anyway; if this is going to be that confusing to that many people, then it's a de facto bad idea. (Though still, I'd like to know how many people would really be confused by this, and how many wouldn't be, but are just saying that someone else may get confused.)

      -- Frag.

        You almost got me. :)

        On page 103, TheDamian explains that calling a 'constructor' as an object method is a lazy way to copy an object, but observe what happens when you change an object's attributes at runtime and try to clone it:
        use strict; use Clone qw(clone); my $foo = foo->new(); $foo->{spam} = {spam =>'eggs'}; my $bad_clone = $foo->new(); my $good_clone = clone($foo); $bad_clone->{spam}->{spam} or warn "bad has no eggs"; $good_clone->{spam}->{spam} or warn "good has no eggs"; package foo; use strict; sub new { my $class = shift; my $proto = ref($class) || $class; return bless {}, $proto; }
        TheDamian also explains on page 105 that
        While many experts view the overloading of constructors as object copiers to be a natural extension of their functionality, others consider the technique unintuitive, too subtle, and more likely to produce obscure and hard-to-maintain code.
        and on page 106
        If you have a choice, it's probably better to code a seperate clone method. Apart from keeping your individual methods simpler and less prone to bugs, the method's name will force client code to be more clearly self-documenting.

        jeffa

        doesn't like spam

      I'm reading through perltoot(1), trying to grok perl's way of doing OO, and it actually recommends doing just what Theseus did. From perltoot:

      sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = {}; $self->{NAME} = undef; $self->{AGE} = undef; $self->{PEERS} = []; bless ($self, $class); return $self; }

      So is this only recommended if you plan to allow for inheritance? Or did I miss something?

      -Dan

        Here I go replying to myself again... :-)

        Your code *does* allow for inheritance, it just doesn't allow for someone to create a new instance by calling $foo->new() on an already made instance. Right?

        So: I now get it. Though, personally, I'd rather have it die immediately than not work correctly. And, btw, it's rather confusing to have that bit in perltoot(1).

        Thanks for making me think about this! :)

        -Dan

Re: Re: (jeffa) Re: My first stab at OO perl...
by LanceDeeply (Chaplain) on Jul 16, 2002 at 21:32 UTC
    Maybe I just don't understand WHY the ref($cargocult) idea is so bad. To me, it makes perfect sense... If someone calls the constructor on an instance rather than the class, it forces it to act as if it were called on the class. Isn't this "correct" in most cases?

    new is new and clone is clone. Conviently merging the two into one and differentiating by the call context feels like an icky version of polymorphism.

    btw: Great node, but I gotta tell ya- there are days when I wish I was bagging groceries rather than coding/debugging/supporting. :)

    Here are some nodes w/ some really good explanations:

    Ovid's (Ovid - minor code nits) Re: Adding autoloaded methods to symbol table with using strict refs
    merlyn's ref($proto) - just say no!

Re: Re: (jeffa) Re: My first stab at OO perl...
by Anonymous Monk on Jul 17, 2002 at 00:02 UTC
    It is bad because while you can have the constructor work fine if someone is confused about the difference between a class and an instance of the class, you cannot perform the same trick for any other non-trivial methods. See Re (tilly) 2: Paradigm Shift - Dual Use Constructors for more detailed discussion of that point.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (4)
As of 2024-03-29 08:09 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found