Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Comment on

( #3333=superdoc: print w/ replies, xml ) Need Help??
Update: There is a version 2 of this that I consider better, but you should read this before reading version 2

Intro
When programming in OO Perl I prefer to keep everything, including configuration options, accessible via a method rather than directly accessing the objects data structure. The sometimes tedious part is adding all the methods as the configuration file grows. I don't want to hard code my accessor methods and I certainly don't want to modify my API modules everytime a new configuration option is added. What follows is a method for keeping both the API developer and the application developer happy and providing flexibility without complete loss of control. This was in part was started after I saw that many of the WWW::Search::X modules were directly accessing the object data structure and felt that level of access was "dirty."

A Good API
The purpose of an API is to provide building blocks for application developers. API modules once deployed are expanded, but the base functionality has to remain. These modules remain in use for a long time hopefully lasting for months or years without major revision once deployed. So how can we achieve this goal and keep an OO interface while expanding configuration options at the developer level and keeping the developer from creating their own accessors?

The Concept
We start off with the Class::Accessor module. Class::Accessor creates accessor methods within your class by passing a list of the method names you want generated into its mk_accessors method.

If you combine the Class::Accessor module with a configuration module such as Config::Auto you get a combination that makes it easier to add options to your applications as well as maintain and extend. It also puts some power (with limited danger) back into the hands of the developer. Any Config::* module could be used I use Config::Auto because it has the ability to use different formats of configration files with modifying our core module. Using plain text configuration files make it easier for installers to adjust the application to their needs and allows for reuse of the same configuration values between languages if needed. This all makes our applications decoupled because they rely on less and less language specific values.

Creating Our "Base" Class
We are going to create an initial module named Base.pm for this OO project. We use Class::Accessor within the Base module and from an application level the usage of OurApp::Base class is hidden. The Base module is for the API or object creator not the person writing applications based on it. We decide on our module name first, the one we want people to use. Lets call it OurApp So inside of OurApp.pm we do:
package OurApp; use strict; use base ( 'OurApp::Base' ); 1;
That was a lot of work :), but what did we do? We created a sub class of our Base class file. The Base package will handle all our actual class creation activities. So lets make that class now.
package OurApp::Base; use strict; use base ( 'Class::Accessor' ); use Config::Auto; sub new { my ($class,%args) = @_; my $self = {}; bless $self , $class; if ($args{conf_file}) { $self->_make_accessors(\%args); } return $self; } sub _make_accessors { my ($self,$args) = @_; my $config = Config::Auto::parse( $args->{conf_file}, format => $args->{conf_format} || 'e +qual' ); OurApp::Base->mk_accessors(keys %{$config}); foreach (keys %{$config}) { $self->$_($config->{$_}); } } 1;


Configuration File Contents
base_directory=/home/trs80 show_code=1 turn_off_sprinkler=0 feed_dog=1


Test Script
#!/usr/bin/perl use strict; use OurApp; my $object = OurApp->new( conf_file => "/home/trs80/ourapp.conf", conf_format => 'equal' ); print $object->base_directory() , "\n"; $object->base_directory("/home/otheruser/"); print $object->base_directory() , "\n";
What This Does for Us
Now if we run this the configuration file keys will be come accessor methods with the values as assigned in the conf file, but we can also set them to new values using the accessor method.

This gets rid of a lot of typing and makes it easy to add new options to the application without hard coding and without rewriting the Base class.

Now say you want to set up a phone_number method, you simply add: phone_number=555 1212 to your configuration file. The method is automatically added to your application the next time it runs.

This is also helpful for when you know the parameters are going to change on something or you decide in the future to move something into the business logic. Your config key equals your method name so you can look up the names of methods in that file. It is also useful to set values between the development and production server.

Further Exercises/Thoughts
Add values to the configuration and you see there is no need to modify the API level modules, this allows you make your application more decoupled because you can add variable elements at the configuration/development level and developers can create new client/need defined methods so the application keeps an OO interface as much as possible.

This technique can be extended to allow for separate configuration files that can override the master configuration, that is at the end user level they may have their own configuration files that can be loaded to override the master configurations file default. If needed the _make_accessors method could be overridden in OurApp.pm to assign values based on system default or user preferences.

This technique has helped me greatly when running a code set in a development environment and then moving it to a production server.

There should be more flexibility built into the new method that allows for passing data structures that can be used for the configuration, such as a hash ref for method and defaults values as well as array or array ref to provide empty method creation. Another nice feature is to have a default/fallback configuration file specified in the _make_accessors method. Ultimately it would be nice to allow a database based configuration option that doesn't require the application developer to create all the DBI code.


Caveats
There are some hidden and not so hidden dangers to this technique. One danger is repeating a configuration variable (key) names or colliding with built in real methods, but naming conventions and guidelines can help avoid that. The technique I have adopted is to name the configuration variables conf___, which will likely not occur directly in the API at any point. Another hidden danger is that there is no way to document the accessors efficiently since they are often added by the developer and never moved into the core, but then that is reasonable to some extent since the API has served it's purpose. The end developer has the responsibility to adequately document their specific needs.

There is most likely a cost in terms of speed at some level, but that cost would vary depending on several factors:
  1. The number of configuration elements
  2. The lifespan of the objects
  3. Number of times an object needs to be created
  4. The scale of the application



In reply to Object Oriented configuration values by trs80

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • Outside of code tags, you may need to use entities for some characters:
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Chatterbox?
    and the web crawler heard nothing...

    How do I use this? | Other CB clients
    Other Users?
    Others romping around the Monastery: (13)
    As of 2014-10-23 18:56 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?

      For retirement, I am banking on:










      Results (128 votes), past polls