Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

Re^3: RFC: Object::Proxy (or somesuch)

by perrin (Chancellor)
on Nov 19, 2004 at 20:31 UTC ( [id://409155]=note: print w/replies, xml ) Need Help??


in reply to Re^2: RFC: Object::Proxy (or somesuch)
in thread RFC: Object::Proxy (or somesuch)

Do you have a real-world example of when this would be useful? I understand that it's not exactly like these other modules, but it's close enough that it's hard for me to see the point.

Replies are listed 'Best First'.
Re^4: RFC: Object::Proxy (or somesuch)
by dragonchild (Archbishop) on Nov 19, 2004 at 21:02 UTC
    I'm loading up a Tk window for a customer. There are some 2-3 dozen invoices for that customer. I go ahead and pull the info from the database for the invoices, and instantiate them. Now, part of the initialization of the invoice object is to instantiate all the lineitems associated with that invoice. Each invoice makes its own database call(s) to load up its lineitems.

    Now, the average user of this screen will look at 2-3 invoices. Why should I pre-load 25-35 invoices, with some 50-70 DB queries, when I can defer loading until the user actually tells me which one they care about?

    Now, so far, you say "Just use another screen." But, I didn't write the Invoice object code. I am told I have to reuse code because my PHB needs to look good, but I am not allowed to modify it because my PHB has no clout.

    Enter Class::LazyLoad. The Invoice object doesn't even know it's being deferred, nor does it care. The Customer screen now loads really fast and all the information that used to be there still is.

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

      This is the kind of thing that Class::DBI does for me automatically, but that doesn't help you since this is someone else's code. After taking a brief glance at Class::LazyObject though, I think that it would do what you want without additional effort. There is also Class::Proxy and Class::Proxy::Lite, but the docs for those are a bit harder to follow.

        Trust me perrin, I have looked at all these modules, and at times even read the underlying source code, and decide against all of them. And in case you are interested, I will tell you why.

        After taking a brief glance at Class::LazyObject though, I think that it would do what you want without additional effort.

        This is from the docs of Class::LazyObject:

        Class::LazyObject automates creating a class for deflated objects that inflate to a particular other class.

        This is not what we are looking for. We want to proxy the actual class, then when needed inflate it to be the real class, but never have to deal with making a lazy class. Another point from Class::LazyObject's docs I found to go against our needs was this, in the docs for the inherit method (which is an important method since it seems to bind the lazy and non-lazy classes together):

        Class::LazyObject->inherit should only be called by any class that inherits from Class::LazyObject.
        From this I can decude that I will surely need to create a Lazy class in addition to my normal one. I just don't like that, it's extra work that doesn't need to be done even if it is "automated".

        The author of Class::LazyObject actually gives an excellent comparison of Class::LazyObject with Object::Realize::Later in the docs (you can find it here. In there the author points out an important difference between the visibility of Class::LazyObject and Object::Realize::Later

        ... great pains have been taken in Class::LazyObject to keep the lazy object namespace free of object methods. You can call any method on a deflated object and it will be correctly passed on to the inflated class.

        Object::Realize::Later takes a slightly different approach. Lazy loading is considered a feature of an object, and appropriate details are exposed to allow code to take advantage of this.

        While I had already decided Class::LazyObject was not what I needed, this description made me think that Object::Realize::Later was not what I wanted either. Transparency was an important component for my needs and I despise namespace pollution, especially in objects.

        As for Class::Proxy and Class::Proxy::Lite they are not doing the same thing as our module. Despite the Object::Proxy name, we realized very early on that this module was not really a proxy. So suffice to say, a proxy module would not fit the bill. However I have looked over these module as well when I was writing IOC::Proxy.

        Class::Proxy uses some odd Class::Listener object at it's base, and to be honest, I got tired of trying to trace what was happening. This opacity was not what I was looking for.

        As for Class::Proxy::Lite, this is what turned me off for that module:

        Apparently, it's not possible to catch calls to the can() and isa() methods on instances of Class::Proxy::Lite. This makes it impossible to implement a true proxy without defining UNIVERSAL::isa and UNIVERSAL::can, which I'm reluctant to do.
        It seemed to me that the author just didn't implement a can or isa function in Class::Proxy::Lite to catch it, since AUTOLOAD will not trap them. Although I there is a possibility that some of the details of how he chose to implement Class::Proxy::Lite got in the way of that happening. I didn't delve too much further in to see.

        There is also Class::Wrap by Simon Cozens. This module basically proxies the entire class by re-writing it's symbol table and moving the original methods into a ::hidden:: namespace. As with most of Simon's modules, it is an interesting bit of code which he threw out onto the CPAN and then never touched again. I see one major flaw in it being that he fails to account for inheritance (he only proxies the immediate class, and so all inherited methods will then not be proxied,... bug or feature,... you decide).

        I ultimately wrote IOC::Proxy to fit my needs, which were; to be able to proxy instances and not classes, to be able to proxy the full inheritance tree of methods, and to be virtually indistinguishable from the real thing. IOC::Proxy accomplishes all those goals, in a somewhat paranoid and slightly insane way. There is some details about how it does this in the docs if you care to read.

        In the end, I hear your point about not re-inventing the wheel. And CPAN is a wonderful resource, but by no means a complete resource. Before I code anything which I think I might be able to find on CPAN, I make an exhasutive search to see what is out there. Sometimes I use what I find, and other times I end up writing my own module, but never without thoroughly reading and digesting as much as I can about what is already there. After all, if I cannot reuse the actual code, I can at least reuse the experience and knowledge I find in that code.

        -stvn
      Let me just say:

      kewl!

      /J

Re^4: RFC: Object::Proxy (or somesuch)
by stvn (Monsignor) on Nov 19, 2004 at 21:19 UTC

    The point of being able to lazyload just a single instance is basically to be as unintrusive as possible, while still allowing this to be possible. Let me explain in more detail.

    Say you want to lazy load ever instance of the class.

    use Class::LazyLoader 'My::Class'; push @lazy_classes => My::Class->new() for 1 .. 100;
    Every single instance of My::Class is now lazily loaded. This is obvious, and how most of the class proxy/lazy loading module out there work.

    Now take this particular example (based on the IOC issues mentioned above in response to dio).

    My A constructor requires an instance of B, and my B constructor requires an instance of A. The best solution is to give A a real B instance, and give B a lazy-loaded A instance.

    Now say my C constructor requires an instance of A. I previously gave B a lazy-loaded A, however, there is no need to give C a lazy-loaded A, that is an unessecary level of indirection. But if lazy-loading was on a class level only, I would not be able to make this choice. With lazy-loading being available on a per-instance basis I can give C a real A instance.

    Of course the need (or desire) to fiddle at this low a level is a rare instance and one not likely to be an issue for anyone but me. However, this is exactly the issue that caused dragonchild and I to start talking about and building this module.

    -stvn

Log In?
Username:
Password:

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

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

    No recent polls found