in reply to OO, Library Configuration and Class Variables

You can use something like the earlier mentionated Factory pattern. The User class always need to get data from somewhere, you can pass the datasource to it.

Untested code:

# User doesn't mind if it is local or remote, he wants the data package Lookup; ... sub is_user_in_cache { ... } sub get_user_data { warn 'You should implement this method!'; } ... package Lookup::Remote; use base 'Lookup'; sub get_user_data { ... } ... package main; use Lookup::Remote; my $lookup = Lookup->new({ remote_uri => $uri, }); use User; # Tell User class the lookup method it will use. User->set_lookup($lookup); my $user = User->new({ id => 1234, });
You will create interface methods for all Lookup objects. You can also create other Lookup classes, like Lookup::DBI, Lookup::CSV, etc, just implementing the interface methods. You can write a base class like Lookup, having all caching you need, and then specializing the datasources (DBI, CSV, SOAP).

I hope this helps.

Replies are listed 'Best First'.
Re^2: OO, Library Configuration and Class Variables
by moot (Chaplain) on Jun 23, 2005 at 20:59 UTC
    Your answer mentions exactly what I'm trying to avoid - that the user of User knows about User's implementation:
    # izut said use Lookup::Remote; my $lookup = Lookup->new({ remote_uri => $uri, });
    This to me looks like User's use of Lookup::Remote is not hidden, and thus breaks the interface between User and code using it. In other words, any code instantiating a User does not and should not need to know that there could be a vast amount of code tucked away providing the user information (or the ultimate source of that information); merely that when $user->info() is requested, it is available.

    --
    Now hiring in Atlanta. /msg moot for details.

      package LookupFactory; use base qw( UserSource ); sub init_lookup { # run config based upon type of source requested } package UserSource; sub get { # get the type of user source (lookup, database, whatever) } package User sub new { # get a source based upon whatever the User config says to get } package App $user = User->new(); $user->info() # look ma! no config!
      I don't think it would break your model to have the user request the type of source from a config file. It's actually still not even necessary to do that, as it could select from a pre-created list of Lookup Sources.

      Code is (hopefully obviously) not tested and not intended to work
        Hmm.. I grok this apart from this bit:
        sub init_lookup { # run config based upon type of source requested }
        In case I wasn't clear, my original query was how to do this config - it seems you're saying that Loopup::Remote (or LookupFactory, here) can run its own AppConfig->file(), but this raises the issue of passing in the filename from which Lookup::Remote can configure (as you can probably tell I'm against hard-coding values!).

        Also, unless I have a separate configuration file for 'library values' from the one for 'application values', I'd need to define both sets of variables that AppConfig will use, in all instances of AppConfig, or deal with the warnings it spits out about undefined variables. And something about separating out those config files strikes me as wrong.

        I guess I could just modify User to accept an AppConfig instance and pretend that there is a broader use for this than just a single remote_uri parameter, or deal with app-code knowing something about User's internals.

        Thanks for the assistance.

        --
        Now hiring in Atlanta. /msg moot for details.

      You have to specify where the User module will load its data. You can also use something like this:
      package Lookup; sub new { # gets some config file }; sub get { warn 'You should implement this method!'; } sub set { warn 'You should implement this method!'; } package Lookup::Remote; use base 'Lookup'; sub get { ... }; sub set { ... }; package main; use User; User->init({ source => 'Lookup::Remote', }); my $user = User->new({ id => 1234, });
      Or you can even tell User constructor to check some value in main scope:
      package User; sub new { ... $self->{'source'} = $main::USER_LOOKUP_SOURCE; ... } package main; use User; our $USER_LOOKUP_SOURCE = LookupFactory->new('Remote'); my $user = User->new({ id => 1234, });
      You still have to inform the User class what class it will use to get data. I can be wrong, that's the reason I come to perlmonks :)

      Update: I think it is like DBI specs. I don't know what is going under the DBD module, but I have to inform DBI where the data is.

        I appreciate your response, but I think you're rather missing the point of what I'm trying to achieve. I don't want clients of the User class to even know there *is* a Lookup::Remote being used by User, much less have to specify Lookup::Remote as a source for discovery.

        The problem is how to communicate a high-level configuration value into a has-a class inside another class, not which has-a class to use.

        --
        Now hiring in Atlanta. /msg moot for details.