One thing I don't like with classless objects is, that you can't easily temporarily patch/overwrite a method. In my tests I often do the following :
# (test for WWW::Mechanize::Shell)
{ my $called;
local *WWW::Mechanize::get = sub {
$called++;
};
ok( $shell->foo() );
is( $called, 1, '$shell->agent->get was called one time");
};
This is relatively clean and dosen't require much mucking to find out the package of the sub I want to patch. A classless object would possibly have its subs in some package which is dynamically generated, which makes patching/overriding harder unless I fall back on Hook::LexWrap (and even then ...)
perl -MHTTP::Daemon -MHTTP::Response -MLWP::Simple -e ' ; # The
$d = new HTTP::Daemon and fork and getprint $d->url and exit;#spider
($c = $d->accept())->get_request(); $c->send_response( new #in the
HTTP::Response(200,$_,$_,qq(Just another Perl hacker\n))); ' # web
| [reply] [d/l] [select] |
my $object = $proto->new();
$object->set(some=>'stuff');
{ my $test_object = $object;
$test_object->{method} =
sub { print "got:".$_[0]->{_proto_}->method() };
$test_object->method();
}
This would depend on how the classless/prototype
system works. (I don't know one well enough to give
a full example) | [reply] [d/l] |
I believe that with Class::Classless, you can locally override the specific hash value you're interested in:
local $test_object->{method} =
sub { print "got:".$_[0]->{_proto_}->method() };
| [reply] [d/l] |
Class::Prototyped strikes me as a good way to do something like Java's nested classes (external link). I don't think I'd want it for a top-level class.
---- I wanted to explore how Perl's closures can be manipulated, and ended up creating an object system by accident.
-- Schemer
Note: All code is untested, unless otherwise stated
| [reply] |
I think you're right.
The good fit would be because the class is small
and belongs only in the context of it's definition.
That is, within the main class.
What's more, the returned class can be more dynamic
than the Java version. Response to different
interfaces, different methods, etc.
There's an alternative version of
Inner Classes
at http://perldesignpatterns.com/
| [reply] |
| [reply] |
I should start by admitting to being a co-author and the currenty maintainer of Class::Prototyped, so I'm a bit biased!
My general rule of thumb is that I use C::P when I'm in a situation where someone using the module I'm writing would either need to do one of two things:
- Pass lots of anonymous subroutines to new() methods
- Subclass from my classes before doing anything cool
This tends to be the generation of what I call behavioral objects (if there is a proper pattern name for this, please let me know:). Examples are objects that interface with database records (all sorts of validation and stuff), objects that interface with individual fields on a database record (even more validation:), etc. Imagine if you had to create a separate class for every field on every table in your database. If you don't do that, though, you're in a world where you pass all these anonymous subroutines as parameters, you've got to write code to implement inheritance yourself, etc., etc. Yeach!
I frequently try to avoid using C::P because I don't want to increase the barrier to usage for modules I'm releasing, but before long I realize that I'm just reinventing the wheel and that there's a reason Ned and I developed C::P! So I bite the bullet, start using it, and suddenly everything starts clearing up.
Now, time for the obligatory sales portion!
C::P is designed to address many of the concerns like those raised by Corion. Quoting from the (rather voluminous) docs:
In Self, everything is an object and there are no classes at all. Perl, for better or worse, has a class system based on packages. We decided that it would be better not to throw out the conventional way of structuring inheritance hierarchies, so in Class::Prototyped, classes are first-class objects.
However, objects are not first-class classes. To understand this dichotomy, we need to understand that there is a difference between the way "classes" and the way "objects" are expected to behave. The central difference is that "classes" are expected to persist whether or not that are any references to them. If you create a class, the class exists whether or not it appears in anyone's @ISA and whether or not there are any objects in it. Once a class is created, it persists until the program terminates.
Objects, on the other hand, should follow the normal behaviors of reference-counted destruction - once the number of references to them drops to zero, they should miraculously disappear - the memory they used needs to be returned to Perl, their DESTROY methods need to be called, and so forth.
What this means is that you should use C::P in such a way that things that are classes get class names. Then people like Corion can easily do things like:
{ my $called;
WWW::Mechanize->reflect->addSlot(get => sub {
$called++;
});
ok( $shell->foo() );
is( $called, 1, '$shell->agent->get was called one time');
}
I think the single greatest insight that Ned and I had was providing a unified OO interface for mucking about with the structure of classes that applies both to C::P created classes and to those written without any knowledge of C::P! The other cool thing about C::P is that because it uses all of the normal Perl class mechanisms (when it creates objects, it creates a new class), you get access to all of the normal Perl behavior, including multiple inheritance. There is no overhead to C::P except for object instantiation (which can be avoided if you want dumb leaf nodes by providing your own new method) and the normal Perl method call overhead. Unless you are using slot attributes, in which case there is the normal Perl subroutine call overhead (different from method overhead in that there is no need to find the implementatation).
Some of the cool features, like slot attributes, are only accessible if you buy into the whole C::P approach, but it's relatively easy to mix and match the C::P approach with more traditional Perl approaches, especially once you grok the few underlying concepts. I will be the first to admit that I've done an incredibly poor job of selling C::P to the community, but part of that is because, independent of time constraints, I don't really know where to start.
OK, enough blathering.
--Toby Ovod-Everett | [reply] [d/l] |
Thanks very much for your reply.
Is Class::SelfMethods superceded by C::P then?
So I bite the bullet, start using it, and suddenly everything starts clearing up.
I had the same feeling when I started using a
really lightweight, prototype-based configuration.
All these objects that would've needed either many
parameters, tweaking the minute details of their
behaviour or a lasagne layers of overrideable methods
suddenly became simple, clear and 100% adaptable.
That experience got me reading about Self, JavaScript
and the various Perl modules. I think I'm already
sold.
Brad
PS. I'll have to read Class::Prototyped, the implementation
sounds interesting.
PPS. You may want to defend C::P over at Class::Classless vs. Class::Prototyped
| [reply] |