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

Inside Out Classes and the internal Hashes

by KurtSchwind (Chaplain)
on Dec 17, 2007 at 20:53 UTC ( [id://657514]=perlquestion: print w/replies, xml ) Need Help??

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

I've been working on some OO perl and I'm just starting to work with Inside out classes. Every example I see is very similar and I can emulate it without a problem.

However, I've taken a different approach and I can't find an example that does it the way I plan on doing it. So am I just way off here? Is there a major flaw in my method?

Here seems to be the traditional inside-out class :

package PointX; use strict; use warnings; use Scalar::Util qw(refaddr); use Carp qw(carp cluck croak confess); our $VERSION = '0.1b'; { my %xcord; my %ycord; sub new { my ($class, $cords) = @_; my $new_object = bless \do{my $anon_scalar;}, $class; $xcord{refaddr $new_object} = $cords->{'x'}; $ycord{refaddr $new_object} = $cords->{'y'}; return $new_object; } sub DESTROY { my ($self) = @_; delete $cord{refaddr $self}; return; } sub set_x { my($self, $xcord) = @_; $xcord{refaddr $self} = $xcord; return ($xcord{refaddr $self}); } sub get_x { my($self) = @_; return ($xcord{refaddr $self}); } sub set_y { my($self, $ycord) = @_; $ycord{refaddr $self} = $ycord; return ($ycord{refaddr $self}); } sub get_y { my($self) = @_; return ($ycord{refaddr $self}); } } # end of internal scoping. 1;

But am I wrong if I get rid of the multiple hashes and go with a single hash ref like this?

package PointX; use strict; use warnings; use Scalar::Util qw(refaddr); use Carp qw(carp cluck croak confess); our $VERSION = '0.1b'; { my %cord; sub new { my ($class, $cords) = @_; my $new_object = bless \do{my $anon_scalar;}, $class; $cord{refaddr $new_object}->{'x'} = $cords->{'x'}; $cord{refaddr $new_object}->{'y'} = $cords->{'y'}; return $new_object; } sub DESTROY { my ($self) = @_; delete $cord{refaddr $self}; return; } sub set_x { my($self, $xcord) = @_; $cord{refaddr $self}->{'x'} = $xcord; return ($cord{refaddr $self}->{'x'}); } sub get_x { my($self) = @_; return ($cord{refaddr $self}->{'x'}); } sub set_y { my($self, $ycord) = @_; $cord{refaddr $self}->{'y'} = $ycord; return ($cord{refaddr $self}->{'y'}); } sub get_y { my($self) = @_; return ($cord{refaddr $self}->{'y'}); } } # end of internal scoping. 1;

In the second example, I wrote a test program that seems to work just fine and as I'd expect.

#!/usr/bin/perl use strict; use warnings; use PointX; my $point = PointX->new({x=>3, y=>20}); print "The x cord is ".$point->get_x()."\n"; print "The y cord is ".$point->get_y()."\n"; $point->set_x(10); print "The x cord is ".$point->get_x()."\n"; print "The y cord is ".$point->get_y()."\n";
which produces the output:
The x cord is 3 The y cord is 20 The x cord is 10 The y cord is 20

So why do most things just keep adding internal hashes instead of using 1 hash for everything? I'm new to this inside out class way of doing things, so I want to nip any bad habits in the bud (if indeed this is a bad habit).

This is a trivial example of just having 2 items tracked with a single hash. My current application will track around a dozen. I'd prefer not to have a dozen hashes, but I will if it's deemed to be The-Right-Thing (tm).

--
I used to drive a Heisenbergmobile, but every time I looked at the speedometer, I got lost.

Replies are listed 'Best First'.
Re: Inside Out Classes and the internal Hashes
by kyle (Abbot) on Dec 17, 2007 at 21:05 UTC

    Your example works fine, and it will continue to work fine, but it loses one of the advantages of inside-out objects. With the usual way, if you mistype an attribute name somewhere (and you use strict), Perl will cough up an error message before it even runs anything.

    sub get_x { return $xc00rd{refaddr $_[0]}; # oh noez! }

    The way you have it, Perl will just autovivify the attribute you hadn't created before.

    sub get_x { # $coord{refaddr $_[0]}->{'X'} == 42 return $coord{refaddr $_[0]}->{'ex'}; # but this is undef }

    This can lead to subtle and difficult bugs down the line, but only if you ever make mistakes while typing.

    As an aside, consider using a framework to make inside-out classes easier (such as Object::InsideOut or Class::Std). These will create constructors, destructors, and accessors for you.

Re: Inside Out Classes and the internal Hashes
by Joost (Canon) on Dec 17, 2007 at 21:14 UTC
    There's nothing fundamentally wrong with your approach. The main difference is that using a separate hash for each property means you get "automatic spellchecking" of properties:
    use strict; my %prop; sub method { my ($self) = @_; $prXp{id($self)} = 1; # throws an error: $prXp is not declared }
    And using separate hashes means less typing.

    One other thing: using refaddr is not safe when using fork() (on windows) or threads (on any system) since the refaddr changes over a CLONE. Object::InsideOut seems to deal with this issue.

      One other thing: using refaddr is not safe when using fork() (on windows) or threads (on any system) since the refaddr changes over a CLONE. Object::InsideOut seems to deal with this issue.
      But at what cost. Object::InsideOut has a huge overhead: Over 90% of the run time of the test script of the poster is spent generating a unique id for the objects. Madness.
Re: Inside Out Classes and the internal Hashes
by shmem (Chancellor) on Dec 17, 2007 at 22:02 UTC
    { ... sub new { my ($class, $cords) = @_; ... } ... } my $point = PointX->new({x=>3, y=>20});

    By passing a hash reference as an argument to your constructor, you are creating and destroying a hash at each object creation just for passing the arguments, which wouldn't be the case if you passed in a list (built => 'with', fat => 'commas'). A lexical hash inside the constructor which takes the arguments is created and allocated once (but its keys and values are cleared when its scope is left).

    This is a trivial example of just having 2 items tracked with a single hash. My current application will track around a dozen. I'd prefer not to have a dozen hashes, but I will if it's deemed to be The-Right-Thing (tm).

    Check out Anno's Alter package, which is a nice alternative to "traditional" inside-out objects based on lexical hashes. It supports multiple inheritance, doesn't need refaddr, needs no fiddling with DESTROY, objects created that way are garbage-collected in the same way as all perl data types and thread safe. It implements data encapsulation in a most perlish and elegant way.

    IMHO, it supersedes and obsoletes the lexical-hash-style inside-out packages. Somebody had to say it...

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: Inside Out Classes and the internal Hashes
by FunkyMonk (Chancellor) on Dec 17, 2007 at 22:32 UTC
    Rolling your own inside-out objects is a great learning exercise (some would say it's a rite of passage, much like writing your own templating system:), but for any real usage I'd use a module such as Class::InsideOut

      So, this just brings up another question.

      There seems to be at least 3 major modules to do this. Object::InsideOut, Class::InsideOut and Class::Std. So which one do I use?

      I need a "production ready" solution which is why I was rolling my own. In the absence of information on those modules, I wasn't sure if they were really robust or not. Are they all pure perl? Is one of these going to be a core module soon?

      --
      I used to drive a Heisenbergmobile, but every time I looked at the speedometer, I got lost.

        I don't have a definitive answer for you, but perhaps this will help.

        • Class::Std is written by TheDamian, referenced by PBP, and it's the only one I've actually used. From what I hear, it has trouble with threading, but I haven't done any threading, so I haven't cared.
        • Object::InsideOut seems to be the kitchen sink version, written after Class::Std, in an effort to address its drawbacks. It may be slow.
        • Class::InsideOut is the small and simple version. I don't know anything about it.

        Good question.

        And, actually, I'd just figured everyone was using Moose these days.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others admiring the Monastery: (6)
As of 2024-04-20 00:33 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found