Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Re: Better Inside-Out Objects :)

by xdg (Monsignor)
on Oct 06, 2006 at 17:18 UTC ( [id://576716]=note: print w/replies, xml ) Need Help??


in reply to Better Inside-Out Objects :)

You don't seem to really want "inside-out" objects -- you seem to want encapsulated objects. Inside-out is just one way of doing that and it seems like you're going out of your way to try to make it act like something else.

A couple quick observations:

  • Is there exta overhead for overloading?

  • What if Foo or Bar want to overload "%{}" themselves?

  • You give up compile time typo protection. e.g. $self->{ sercet }

I'll let you know if more things occur to me later.

Update

The more I think about it, the more this just seems like a way of disguising an accessor call by way of overloading.

And there's no reason the trick requires inside-out objects. Here's a plain-old blessed hash version -- and it still gives you package-specific hash values that don't collide:

SmartHash.pm

package SmartHash; use strict; use warnings; use Carp; use overload "%{}" => sub { my $caller = caller; croak "Illegal object access" unless $caller->isa(__PACKAGE__) +; return shift if $caller eq __PACKAGE__; return shift->{$caller} ||= {}; }, fallback => 1; 1;

Foo.pm

package Foo; use strict; use warnings; use base 'SmartHash'; sub new { my $class = shift; bless {}, $class; } sub foo { my $self = shift; $self->{foo} = shift if @_; return $self->{foo}; } 1;

Bar.pm

package Bar; use strict; use warnings; use base 'Foo'; sub bar { my $self = shift; $self->{bar} = shift if @_; return $self->{bar}; } sub foo { my $self = shift; $self->{foo} = shift if @_; return $self->{foo}; } 1;

testbar.t

use strict; use warnings; use Test::More tests => 9; use Test::Exception; require_ok( 'Bar' ); require_ok( 'Foo' ); my $o = Bar->new; my $p = Foo->new; dies_ok { $o->{bar} = 42 } "dies on direct access"; $o->bar(13); is( $o->bar, 13, "Bar obj set/get bar" ); $o->foo(42); is( $o->foo, 42, "Bar obj set/get Bar's foo" ); $o->Foo::foo(23); is( $o->Foo::foo, 23, "Bar obj set/get Foo's foo" ); $p->foo(99); is( $p->foo, 99, "Foo obj set/get foo"); is( $o->foo, 42, "Bar obj's foo is still Bar obj's foo" ); is( $o->Foo::foo, 23, "Bar obj's Foo's foo is still Bar obj's Foo's fo +o" );

prove -v testbar.t

testfoo....1..9 ok 1 - require Bar; ok 2 - require Foo; ok 3 - dies on direct access ok 4 - Bar obj set/get bar ok 5 - Bar obj set/get Bar's foo ok 6 - Bar obj set/get Foo's foo ok 7 - Foo obj set/get foo ok 8 - Bar obj's foo is still Bar obj's foo ok 9 - Bar obj's Foo's foo is still Bar obj's Foo's foo ok All tests successful. Files=1, Tests=9, 0 wallclock secs ( 0.00 cusr + 0.00 csys = 0.00 C +PU)

I guess I'm not clear on what you find so difficult about inside-out objects (assuming a sane inside-out class generator to handle destruction, serialization, threads, etc.). Is "$foo{ id $self }" so much more difficult than "$self->{foo}"? Or even "$self->foo()"?

What are you trying to optimize for? The hash-based interface does avoid having to pre-declare any properties -- you can just say "$self->{baz}" and the "baz" property springs into existence. That comes at the cost of the typo checking.

I can also see a use for it if you're trying to upgrade legacy code. You can just drop "use base 'Encapsulate'" into your object modules and they should continue working like normal without having to edit anything else there, but everything else external that accesses objects will break.

But just as is, this feels close to an XY Problem to me.

-xdg

Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Replies are listed 'Best First'.
Re^2: Better Inside-Out Objects :)
by Ovid (Cardinal) on Oct 06, 2006 at 21:29 UTC

    OK, I'll grant that I don't get the compile-time attribute safety and thus this isn't really "inside-out" in the full sense of the word. However, it's not an XY problem because the specific problems that I deal with over and over again are twofold: one, people constantly violate encapsulation. Two, the compile-time safety isn't much of an issue for me because I write enough tests for my code (caveat: when working in a sane environment), that I catch the bugs up front.

    Further, the syntax of various inside-out object modules is just frickin' painful to me and I've used them and tried to evangelize them! When I try to get others to used them, I invariably get a huge amount of resistance along the lines of "blessed hashes are good enough for me". Regardless of whether or not it's rational, when you're on a team of five programmers and the other four agree with the concept but flat-out reject the implementations, you have to know when to cut your losses.

    For me, it's the encapsulation which is my biggest concern. If I can provide a way that programmers won't violate encapsulation but it's very easy for to migrate to (with sane code, nothing breaks!), then I've solved the exact problem I'm faced with. If you're trying to solve a different problem, then the XY problem applies.

    Cheers,
    Ovid

    New address of my CGI Course.

      the syntax of various inside-out object modules is just frickin' painful to me

      So, specifically, the whole "id $self" stuff? I can see that, though I suspect it's all about what people are used to seeing.

      I think the overload-like-a-hash approach will be problematic in the long run because the apparent simplicity masks significant complexity.

      Serialization is probably the first place this will really pop up and fail to behave like people expect. What happens when someone tries to throw one of these objects at Data::Dumper et al? What happens when someone tries to create an object on the fly by loading data into a hash reference manually and then blessing it into an object? (E.g. loading YAML or other external config data?)

      my $ref = { foo => 23, bar => 42, }; bless $ref, "Bar";

      -xdg

      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

        What happens when someone tries to throw one of these objects at Data::Dumper et al?

        That's a problem with Inside-Out Objects in general.

        What happens when someone tries to create an object on the fly by loading data into a hash reference manually and then blessing it into an object?

        They ought to be shot. Constructors (incl cloning and deserializing constructors) exist for a reason. You can't expect to clone an object by copying it's bytes or it's fields.

Log In?
Username:
Password:

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

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

    No recent polls found