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

•Re: RFC: Class::DispatchToAll

by merlyn (Sage)
on Jul 10, 2002 at 23:11 UTC ( #180906=note: print w/ replies, xml ) Need Help??


in reply to RFC: Class::DispatchToAll

I'm sorry, but I've been doing OO since 1980 (with the original Smalltalk 80 image). I can't see how this would be useful in the slightest. Can you give an example of a real use of this, that is reasonably normal OO and not just a laboratory case? As in, what problem were you trying to solve when you came up with this?

-- Randal L. Schwartz, Perl hacker


Comment on •Re: RFC: Class::DispatchToAll
Re: •Re: RFC: Class::DispatchToAll
by TheDamian (Priest) on Jul 11, 2002 at 04:23 UTC
    The problem this solves is very real-world. Consider a Perl class hierarchy in which a class Child inherits from classes Mother and Father:
    package Child; use base qw(Mother Father);
    If both Mother and Father have DESTROY methods, what happens? Under normal Perl semantics, only Mother::DESTROY would be called, since it's the left-most, depth-first method encountered during the dispatch. But failing to call one (or more) of an object's inherited destructors is not correct behaviour. Hence the need to be able to call all of them:
    package Child; use base qw(Mother Father); sub DESTROY { $_[0]->EVERY::DESTROY }
      The problem this solves is very real-world. Consider a Perl class hierarchy in which a class Child inherits from classes Mother and Father:

      Uh, oh. Multiple Inheritance.

      A couple of years of doing Smalltalk (which didn't have multiple inheritance, but which let you fake interface interitance via mixins), and a couple of years of Java and C++ have lead me to believe that Multiple Inheritance is a Very Risky Thing, and that it can always be worked around by either composition or reducing to inheriting from one data-bearing class and multiple (data-less) interface classes. Doing so avoids the multiple destructor problem.

      Perhaps you've run into a situation where multiple inheritance is the right thing to do. If so, I'd like to hear about it.

        It happens often in LPC. It's a language mostly used for building MUDs. Often you will have a standard weapon class and a standard wearable class. All weapons inherit from the weapon class, while all wearables inherit from the wearable class. But boxing gloves would inherit from both.

        Some problems LPC has with multiple inheritance aren't found in Perl (for instance, the possibility of duplicating variables when a class is inherited by two paths - different implementations of LPC solve it differently). Other things are solved differently, for instance, one flavour of LPC forbids inheriting from two classes if they have methods with the same name - unless the inheriting class defines a method with that same name.

        There is nothing wrong with multiple inheritance. It's just hard to implement is right, which is way some languages take the easy way out and outright forbid it. Inheritance is already a hard problem in Perl, but multiple inheritance is a real nightmare. But that's the fault of Perl, not of multiple inheritance.

        Abigail

      OK, that problem is solved by NEXT, and discipline. It's the responsibility when you want to extend, rather than override, to call your parent method. Particularly with DESTROY.

      So I still haven't seen a place where calling all of the methods blindly is useful or maintainable.

      -- Randal L. Schwartz, Perl hacker

Re: ?Re: RFC: Class::DispatchToAll
by frag (Hermit) on Jul 11, 2002 at 04:44 UTC
    I just thinking about something like this just two days ago! I've had to set up a heirarchy of modules to do something very similar, but the alternate dispatch system I employed was specific to those modules and not something that you could just drop in to any other heirarchy, like this is.

    The problem can be stated like this: say you want a object to try to get_money(). Say the object is a member of the classes Executive::Enron, Citizen::American and Human::AbleBodied. So you try $KenLay->get_money();. It so happens that Executive::Enron::get_money() will return a failure. However, method dispatch stops at this failure, without trying the methods in other classes. For the sake of argument, we'll call this a bad thing. (I'm speaking highly rhetorically here, ok? For a non-dark side example, consider object $YAPC, member of classes Herd::Camels and NonProfit::OfficiallyLicensed. Herd::Camels::get_money() contains 'goto &pass_hat;' but NonProfit::OfficialyLicensed::get_money() writes grant proposals.)

    You can create wrapper methods or a dispatch table to explicitly call each fully-qualifed method in order, but that would be unusably brittle. You could use NEXT, but if the modules you're dealing with already exist, you'd have to rewrite all the modules (or at least the methods, in new subclasses) from the heirarchy that you are working with.

    Thanks to the things I learned from Damian's Advanced Objects course (another satisfied customer!), I was able to devise a solution. But it wasn't particularly easy, and can't be simply carried over to other modules. I was just wondering if this sort of thing could be done in a portable fashion when I saw this. Thanks, domm!

    -- Frag.
    --
    "It's beat time, it's hop time, it's monk time!"

      Again, multiple inheritance is a broken concept in general. This whole thing reeks of "bad design".

      Why would you be passing control from your derived method to a base method that might fail? You should be calling it directly, taking the results (or none), then deciding whether or not to call the next. Again, when you inherited from the multiple base classes, it's your responsibility to decide how to handle the multiple dispatch. And that's what NEXT is for.

      -- Randal L. Schwartz, Perl hacker

        I don't think I explained myself well, or else I'm not understanding your criticism. I am talking about calling a set of methods from parent classes, taking the results, and deciding whether or not to call the next. (domm's module doesn't do this checking, but it wouldn't be hard to incorporate it. In any case, in this sort of design you must take into account the possibility that all the parental methods would be invoked anyway.) It's set up so that this is only true for a given, fixed set of functions; functions that can be clearly traced back in the code. (These functions are also documented thoroughly and are vetted to be free of side-effects upon non-localized data. I agree with you about the importance of discipline in OOP.) It seems to me that this actually solves one of the major multiple inheritance headaches, by designating a method in the child class that acts as a wrapper for all of the parental methods that attempt the same function.

        Note that I misspoke in my earlier comment about wrapper methods; essentially, that's all this is, dispatch being controlled and monitored by a single method. You've got a generic wrapper method in the child class that takes the names of the parental methods that it should handle. This does not vary at run-time, but is a constant within the child class that is handling the dispatch.

        Also please note that I am not arguing that multiple inheritance is some sort of no-brainer to be used cavalierly. I am saying that I have found a particular situation where I think it makes sense. I don't question that this is a single solution to a limited set of problems.

        Finally, I have to admit that I just don't see that NEXT is always the best approach. NEXT allows each method in the parent class to make its own decisions about whether to continue dispatch or not. But it seems more natural to me, and safer, even, to have a central method oversee the process.

        -- Frag.
        --
        "It's beat time, it's hop time, it's monk time!"

Re: ?Re: RFC: Class::DispatchToAll
by domm (Chaplain) on Jul 11, 2002 at 08:19 UTC
    My Problem: I've got several classes, looking a little bit like this:
                         App
                          |
                      App::Basic
                     /         \
    App::Basic::Special1     App::Basic::Special2 
         |                        |
         |                        |
         |            My::App     |
         |           /       \    |
    My::App::Special1         My::App::Special2
    
    Note that My::App doesn't inherit from App. And note that there are My::OtherApp, Your::App too, looking like My::App and inheriting from App::*

    Each of this Classes contains some config values as Class Data.

    I want to be able to fetch all those config values, merge them and do something with the result.

    E.g.: Say, each class contains an array called @order and a hash called %lables.
    @order contains some fields to be displayed
    %lables contains the name of those fields as keys and a "human readable" name as values.

    @order=(field1,field2,field2,..); %lable={ field1=>"Name of Field 1", field2=>"Name of Field 2", .. );

    Each Class now defines some of those values. Some Classes append new values to @order and %lables, some overwrite data in %lables.

    There are some basic fields (used everywhere in the app) defined in App::Basic. Each App::Basic::Special1/2 adds some fields unique to this special case.

    Now, My::App changes some field lables (e.g. use another language). My::App::Special1 may add even more fields that are only needed in My::App (there might be a My::OtherApp::Special1 that inherits from App::Basic::Special1 and doesn't use this fields)

    Then I call dispatch_to_all('get_fields') which fetches all those @field arrays, massage the returned array of all return values to fit my needs, and voila: I get the array of fields used by this Class, without having to specify the "Basic" fields in all Subclasses (which would result in a lot of typing and and even bigger lot of problems should I need to change a App::Basic config value)

    This was/is my problem, and that's why I wrote Class::DispatchToAll, which seems the best way to solve this problem to me. But if you now another way, please let me know...

    -- #!/usr/bin/perl for(ref bless{},just'another'perl'hacker){s-:+-$"-g&&print$_.$/}
      Then I call dispatch_to_all('get_fields')
      This is where I lost you. If a class has a get_fields, I'd expect it to return all appropriate fields. If it has overridden a base class method of the same name, then there's a reason for that, so it should either call all the immediate base class methods and derive an appropriate response from that, or simply ignore those and riffle through it's own data to produce the result.

      Perhaps you don't get why "extending" and "overriding" are useful. You are breaking that model. At that point, don't call it OO programming any more. You are merely aggregating through hierarchies, turning "IS A" into "HAS A" relationships. This will scare every single maintenance programmer that has to look at your code. Please don't do that. All the studies show that most of corporate software money is spent on maintaining, not creating, the software. You have just spent a whole lot more money than you even imagine.

      -- Randal L. Schwartz, Perl hacker

        If a class has a get_fields, I'd expect it to return all appropriate fields.

        I do not want to write 100 accessor methods in each and every class, telling all those methods where to look for the data.

        Instead, I have one general accessor (get_config) in App. Through normal Perl inheritance, every class can access it.

        So, how can this one general method know which values to return? Is there a "standard" way to do this? Or is my architecture completly screwd?

        Perhaps you don't get why "extending" and "overriding" are useful.

        I know Perl OO quite well. Overriding is completly clear to me, but what do you mean by "extending"? Something like

        sub method { my $self=shift; my $return=$self->SUPER::method; # do something return $return }

        You are breaking that model.

        I know. I break it by intention, because it doesn't fit my needs. And as TheDamian is doing something similar (NEXT.pm), it seems that I am not the only one with that need...

        At that point, don't call it OO programming any more. You are merely aggregating through hierarchies, turning "IS A" into "HAS A" relationships.

        I am a so-called "accidental programmer" (i.e. I didn't learn programming at university), and while I am quite aware of OO-methodology, I cannot tell whether this is still OO or not.

        This will scare every single maintenance programmer that has to look at your code. Please don't do that

        Currently, I am maintaining an old version of this App (written by me, so it's my fault...). In the old version, all those fields are specied in there own class. I can tell you from hourlong search-and-replace-sessions that it this is quite a pain to maintain, as I have to wade to a lot of config files and change/add some values, if I change something in the whole App.

        It would seem much clearer to me if each class only adds its specif stuff (or changes already defines stuff). This is what I hope to accomplish with Class::DispatchToAll.

        -- #!/usr/bin/perl for(ref bless{},just'another'perl'hacker){s-:+-$"-g&&print$_.$/}

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (10)
As of 2014-12-21 14:29 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Is guessing a good strategy for surviving in the IT business?





    Results (106 votes), past polls