Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

When to use prototype inheritence?

by bsb (Priest)
on Sep 08, 2003 at 09:02 UTC ( [id://289718]=perlmeditation: print w/replies, xml ) Need Help??

While we, well I, tend to use Perl's language level class-based OO, it's possible to do prototype-bassed OO. You can use Class::Prototyped, Class::Classless, other modules or even write your own basic system. What I've been meditating on is:

When is prototype OO to be prefered?

I've only used prototypes in a configuration framework to permit overrideable defaults and per-object methods. Also, I've done a little JavaScript. So I'm far from an expert.

Are there particular domains or problems best approached using prototypes?
Is there a "code smell" the suggests it's time to move to prototypes?
Are there design problems that prototype-OO can induce?
What are the tradeoffs?

Here's my list, but I'm pulling them out of thin air:

Advantages to proto-OO:

  • More flexibility, particularly at run time
  • Open framework concentrating on interfaces
  • Unifies instance attributes and methods (often the case)
Disadvantages:
  • Slower (I have no benchmarks though)
  • Action at a distance if you change a property of a parent object (read-only objects are ok)
  • More prone to dynamic spaghetti dependence (tm)
  • Multiple inheritence seems difficult

Of course, with Perl you can use both approaches within the same application. The question is when?

Brad

Replies are listed 'Best First'.
Re: When to use prototype inheritence?
by Corion (Patriarch) on Sep 08, 2003 at 09:13 UTC

    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
      It depends on the system, but I think you could.
      Admittedly not with local(*F::Q::name).

      You could probably just do:

      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)
        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() };
Re: When to use prototype inheritence?
by hardburn (Abbot) on Sep 08, 2003 at 16:59 UTC

    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

      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/

Re: When to use prototype inheritence?
by bsb (Priest) on Sep 09, 2003 at 09:07 UTC
    Some further thoughts.

    Prototypes fit well for:

    • Read-only configurations (no action at distance)
    • Serializable inheritence hierarchies (no requires required for deserializing, B::Deparse to serialize)
    • Exception objects (often one-hit sub-types in a type hierarchy)
    • Proxy objects that delegate
    • Parameter objects, defaults (?)

    The common thread looks like runtime flexibility needs.

Re: When to use prototype inheritence?
by tovod-everett (Sexton) on Sep 16, 2003 at 01:10 UTC
    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

      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

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others perusing the Monastery: (4)
As of 2024-04-19 14:40 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found