Re: RFC: The Poor Man's Accessor Filter (upside-down)
by tye (Sage) on Oct 08, 2008 at 16:51 UTC
|
If it hurts... Other than "because the cool kids are doing it", why are you subjecting yourself to the complexity of the bizarreness that is "inside-out objects"? ...especially without using a framework that takes care of the destructor complications and the need to write a bunch of accessors (which also results in poor OO design, IME, since you end up making public accessors for getting at the raw attributes of your objects instead of separating your design between the attributes you need to store and the interfaces you need to provide).
I find it so bizarre that it has become the default practice to jump through convoluted hoops to provide draconian isolation of your attributes into lexical variables and then go roll an all-access accessor for each. Shouldn't one feel foolish after doing that?
I'd rather have compile-time catching of mispelling of attribute names (instead of run-time catching). But more importantly, I'd rather have a distinction between the internal interface that implements the object and the external interface that defines how it is meant to be used. That's a big part of the point of OO.
I've heard people argue that you should use accessor methods inside a class in order to access your attributes because the layer of abstraction may prove useful at some future date. But since Perl doesn't have /private/ methods, this effectively breaks down the distinction between internal representation and external interface. And that leads to bad interface design (why spend time designing a proper interface when your default "add this attribute" rote already produces a method that anyone can use to set things however they want?).
We've gotten rid of the evil "it is just a hash under the covers" and replaced it with "what it is under the covers is very securely protected from your inspection, but it works exactly like a hash except with accessor names instead of key names". "Just a hash" was so evil that we had to destroy it and bury the replacement and then produce interfaces where the "just a hash" nature is in your face rather than under the covers.
In trying to hide the hash, we've produced something much more complicated that exposes even more hashiness.
So, no, I don't think adding a source filter is a net improvement on that situation. :)
| [reply] |
|
|
Tye, first of all, thank you for replying. However, your reply is so much a discussion topic starter rather than a reply that I haven't much of an idea as to how to respond -- indeed it appears that I may not have been intended to respond to it, but rather somehow my post serves as an outlet for your venerable frustration.
My purpose is to encapsulate data, and provide methods which operate on said data. Some methods are straight accessors. Some do fancy things. Other objects are free to violate the data if they wish, if they dare, but none of my objects trust the data of other objects (Law of Demeter).
Your point stands, though. Perhaps my code is just masquerading behind a bare hashref, and I should cast aside the thin veneer of respectability. At the same time, these thinly disguised methods are useful... and present a uniform interface to polite users. Those methods which do more than simply parrot out a hash value stand with the accessors. I admit there is a potential for confusion between a "lvalue" accessor and a traditional method. I'll have to think about that as well.
As for inside-out objects, Perlmonks seemed to have made it abundantly clear that I-O Objects are easier to learn and less error-prone than using a hashref. Now Tye is telling me otherwise -- and I wouldn't get bothered by this except I know enough about Tye to take notice.
As far as a framework, well there are plenty to choose from, and I tend to go with the simplest solution which fits my purposes -- in this case, a source filter works fine. But that's a double-edged sword, because there's always the vague notion that someone may try to use my class (as unlikely as that sounds) for some purpose I wasn't expecting and my code breaks, feebly and uselessly, to the chagrin of this supposed-future-user. Principle of Least Surprise violated -- core dump -- my fault.
| [reply] |
|
|
Perlmonks seemed to have made it abundantly clear that I-O Objects are easier to learn and less error-prone than using a hashref.
Do you have super psychic x-ray vision? :)
| [reply] |
Re: RFC: The Poor Man's Accessor Filter
by moritz (Cardinal) on Oct 08, 2008 at 15:33 UTC
|
So what's my faux pas?
Using a source filter.
There are many good reasons why source filters aren't recommended, for example because they can lead to bugs that are very hard to find, and that you can't combine multiple source filters in general.
Why not use Class::InsideOut or Object::Inside for it? If you must do it yourself, write a subroutine that installs the handler in the caller's namespace - might be a few more lines, but much more robust than a source filter.
| [reply] |
|
|
If you must do it yourself, write a subroutine that installs the handler in the caller's namespace - might be a few more lines, but much more robust than a source filter.
That won't work (in pure Perl) in traditional inside out objects, as the attributes are stored in lexical variables. No code from other files is able to touch it.
But there are other solutions. Accessors are typically very similar, one could generate them with an editor macro.
| [reply] |
|
|
Editor macros would be a perfect fix for my COBOL Fingers. Thank you for that suggestion, which should have been obvious to me.
At the same time, there's still that Perlish allure of simplifying code which ought to look simple. My macros relieve my fingers, but my eyes still have to grok a lot of line noise.
| [reply] |
|
|
|
|
|
|
|
Ah, I didn't realize source filters were so flighty. A shame.
Class::InsideOut and Object::Inside look nice. The former looks svelte and perhaps easier to pick up, while the latter appears to be more comprehensive.
| [reply] |
|
|
There is also ovid's Class::BuildMethods which does not care how you implement your objects (but can work with the inside out kind), e.g.
package Soldier;
use strict;
use Class::BuildMethods
'name',
rank => { default => 'private' };
sub new {
my $class = shift;
return bless [] => $class;
}
package main;
use warnings;
use Data::Dumper;
my $foo = Soldier->new;
$foo->name('John');
$foo->rank('Major');
printf "name: %s, rank: %s\n", $foo->name, $foo->rank;
print Dumper($foo) . "\n";
output:
name: John, rank: Major
$VAR1 = bless( [], 'Soldier' );
| [reply] [d/l] [select] |
|
|
Re: RFC: The Poor Man's Accessor Filter
by shmem (Chancellor) on Oct 08, 2008 at 20:37 UTC
|
#!/usr/bin/perl
package Foo;
use Alter;
use strict;
sub new {
my $class = shift;
my $hashref = bless {}, $class;
Alter::alter($hashref, {});
$hashref;
}
sub attr : lvalue {
my $self = shift;
my $attr = shift;
my $ego = Alter::ego($self);
if (@_) {
$ego->{$attr} = shift;
}
$ego->{$attr};
}
package main;
use strict;
my $foo = Foo->new();
print "\$foo is a HASH\n" if $foo->isa('HASH');
$foo->{foo} = 'bar';
$foo->attr('foo') = 'quux';
print "\$foo->{foo} = $foo->{foo}\n";
print "attr \$foo('foo') = ", $foo->attr('foo'),"\n";
__END__
$foo is a HASH
$foo->{foo} = bar
attr $foo('foo') = quux
The object is just a hashref. You can store anything in it, but it's private data is only accessible via its methods. See Alter.
And no, I wouldn't use source filters, except for templates, maybe. | [reply] [d/l] |
Re: RFC: The Poor Man's Accessor Filter
by jdporter (Paladin) on Oct 09, 2008 at 13:39 UTC
|
{
package Bar;
use Moose;
has name => is => 'rw';
}
Note: The above is a minimal attribute declaration with accessors generated. If you leave off the is part, i.e.
has 'name';
you'll get the attribute but no accessors.
Between the mind which plans and the hands which build, there must be a mediator... and this mediator must be the heart.
| [reply] [d/l] [select] |