billyak has asked for the wisdom of the Perl Monks concerning the following question:

This question could be applicable to any programming language, really, but it sort of works out well because I'm using perl. :)

In OOP, how should interpackage coordination work? I have several objects that read off of data objects in another package. There is a primary object that controls the other objects. Here is my plan right now:

Primary (one object to rule them all; global config, etc.)
- Parse (parses input)
- DataManager (stores data objects)
- DataObjects (stores data objects)
- UI (input/output)
- Search

UI -> Parse -> DataManager -> DataObjects
UI -> Search -> DataManager -> DataObjects -> DataManager -> 
   Search -> UI

Thats all fine and good and it makes sense laid out, My problem is connecting it all. If I send a big config hash to Primary, I have to pass a reference to the Primary object to _every_ new sub-package object that I make. There has got to be a better way!

Having to refer to a Primary's object ref, then to the ref to another package, then to whatever else I need in the package, etc. seems really clumsy. $name = $ui->{main}->{data}->{data_obj_ref}->name :shudder: I don't know what my exact question is, other than "what am I doing wrong here?" This can't be right, and I feel I'm sort of getting the short end of the deal; I'm (attempting) to go with OO but I'm going through more work accessing objects between packages than I am making it easier to maintain.

I would really appreciate any help or suggestions. Thanks.

-billyak

Replies are listed 'Best First'.
Re: Cross-Package/Object Communication
by lachoy (Parson) on Aug 21, 2002 at 15:23 UTC

    If you make the 'Primary' object a singleton then you can whip it up whenver you like:

    my $primary = Primary->instance; my $name = $primary->name; # or if you only need one item from it my $name = Primary->instance->name;

    See code implementing a singleton at Re: how to make a universally inherited method?

    Chris
    M-x auto-bs-mode

      Thanks! You just saved me so much work it is disturbing. A problem seemingly so large that is easily corrected with a few lines of code. Simply amazing. Thank you.

      -billyak
•Re: Cross-Package/Object Communication
by merlyn (Sage) on Aug 21, 2002 at 16:00 UTC
    My intuition tells me that you've invented "data thingies" that don't actually map into real world objects, and thus their roles are muddy (at least in your description).

    I'd suggest you go back to role-playing the real task, and seeing who needs to talk to what. In general, a "manager object" is a very bad idea, especially when it turns out to be a singleton. You've just thrown away most of the advantages of OO when you get to that point.

    For example, the LWP::UserAgent class might at first glance appear to be a "manager object", but it's really an object that maps to a particular browser, which is a real thing. And just as you can have multiple browsers, on your desktop, you can have multiple instances of LWP::UserAgent in your program.

    So, rethink your problem from the perspective of actors, not data, and your need for a "Primary object" will probably go away or take on an obvious new role. Such is the OO way.

    -- Randal L. Schwartz, Perl hacker

      My problem is how to "start" an OO program. From what I've done, it has always been something like

      use x; my $x = new x; $x->set(blah=>"four"); $x->go;
      Or something to that extent. Perhaps it is my fear of going non-OO in the launcher/manager part that is holding me back. Is it standard to have XML parsing or other config stuff non-OO fed into the objects to start the actual process? I could have a %main::CONFIG and pass that to all the objects that are initialized? That still doesn't change the fact that the ref to the config stuff has to be passed on to every object I initialize, though.

      So it is safe so say that I am still confused as to how to work this.

      -billyak
      I don’t quite understand what you are saying, you don’t happen to know of any real world examples as the whole "perspective of actors" isn't sinking into my poor brain.

      I went and looked for examples of controlling large projects and scoop used a singleton and only one class from memory. Also it seemed to be the norm amongst most projects to have a 'manager object'.
Re: Cross-Package/Object Communication
by lestrrat (Deacon) on Aug 21, 2002 at 16:46 UTC

    Since you were talking about config parameters and such....

    I usually have a package that defines some constants, and use it like

    use My::Constants qw/ MY_FOO MY_BAR MY_LOGFILE_NAME /; my $fh = IO::File->new( MY_LOGFILE_NAME ); ....

    My::Constants has default values for those constants, and optionally overrides those values with values that it reads from a XML config file , or from %ENV

    I do this instead of using a config hash or something mainly because I wanted a compile time check of config variable names

      Great idea! After maybe half an hour (hey, I did figure it out) I can make an empty hash or array ref as a constant then bless _that_. So my object ref will be a constant. :) Excellent.

      -billyak
Re: Cross-Package/Object Communication
by IOrdy (Friar) on Aug 21, 2002 at 16:06 UTC
    At the moment I've been writing my first "big" application and I've been structuring my code roughly like yours (2 style) though with a lot more packages and I too would love to know if there is a better solution as it's become increasingly awkward to access all the data my application needs at any one point. My current structure is as follows: Xmlio::Apache::Handler (mod_perl apache handler starts new instance of Xmilo)

    Xmilo (main package)
      # inits in order the following and adds returned reference to blessed hash.
      - Xmilo::Config (loads config from XML file and returns reference)
      - Xmilo::DB::mySQL (make DB connection and returns reference)
      - Xmilo::User (checks cookie data against database, uses Xmilo::Cookie to fetch cookie data and returns reference to user info hash)

      # check for an OP code by checking the params added when Xmilo::Apache::Handler started Xmilo. example ?op=new;
      - Xmilo::OP (reads OP directory for all OP classes and checks tha then using a common constructor creates a new instance of the OP and passes everything in the main hash to the OP package)
        - Xmilo::OP::<opcode> (checks that current user has level of access required for module then executes sql statements etc. and parses returned XML with Xmilo::XSLT and returns XHTML to Xmilo)

      # Display returned XML
      Xmilo::Apache::Handler (takes returned XHTML from Xmilo::OP::<opcode> and displayes it vi Xmilo::Apache::Handler)

      # End


    Now the problem is my main class will be the only class ever that has all the elements required to create a new Xmilo::Config object so when I need to access config data from Xmilo::Config in say one of the OP codes I have to use $xmilo->{config}->get_value('name');

    It's a short example but sometimes I end up with $xmilo->{foo}->{bar}->{foobar}->method(); which just seems wrong.

    Is there something fundamental that I too am missing?