Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Access to single object from multiple other objects

by elTriberium (Friar)
on Oct 30, 2013 at 19:21 UTC ( [id://1060420]=perlquestion: print w/replies, xml ) Need Help??

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

Hi Monks!

I often run into the situation where I need access to a single object from many other objects. For example in my current project I have a config object, which parses an XML configuration file and several modules need to access this data. I want to avoid parsing the XML file over and over again, so I just want to have a single instance of this Config object.

I'm using Moose and know that I could use MooseX::Singleton to create a singleton config object, but also have heard several warnings against using singletons. I was in the same situation before, where I made my Log module a singleton, but then wanted to have different logfiles for different modules and ran into issues with just having a singleton object.

So this is more a general design question: How would you design access to a module that is required by multiple modules? I want to avoid passing that module back and forth when instantiating the calling objects as that would also quickly grow cumbersome

Thanks!

  • Comment on Access to single object from multiple other objects

Replies are listed 'Best First'.
Re: Access to single object from multiple other objects
by tobyink (Canon) on Oct 31, 2013 at 08:56 UTC

    Singletons and global variables are both manifestations of "global state". Generally speaking, global state is the enemy. (And I don't mean that as an anti-UN conspiracy theorist!)

    If the global state is mutable (i.e. different parts of the code can alter it), then it can result in spooky action at a distance. Even if it's immutable, it can result in too tight coupling between different bits of code.

    It's certainly best to pass into functions all the data they need to do their job, and pass to object constructors all the data the object needs.

    It can be cumbersome to pass around database handles from object to object, but there are patterns that can be followed to help eliminate some of these difficulties. For example, say you have a Document object that creates several Section objects, and needs to pass its database handle on to its sections. You might have something like this...

    sub get_header { my $self = shift; Section->new(dbh => $self->dbh, title => "Header"); } sub get_toc { my $self = shift; Section->new(dbh => $self->dbh, title => "Contents"); } sub get_footer { my $self = shift; Section->new(dbh => $self->dbh, title => "Footer"); }

    This can be transformed into:

    sub make_section { my $self = shift; Section->new(dbh => $self->dbh, @_); } sub get_header { my $self = shift; $self->make_section(title => "Header"); } sub get_toc { my $self = shift; $self->make_section(title => "Contents"); } sub get_footer { my $self = shift; $self->make_section(title => "Footer"); }

    This means that the dbh pass-the-parcel happens in just one place instead of many. It also makes it easier for subclasses of Document to override the construction of Section objects.

    (My module MooseX::ConstructInstance can help with implementing this pattern.)

    use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name
Re: Access to single object from multiple other objects
by sundialsvc4 (Abbot) on Oct 30, 2013 at 20:44 UTC

    Whether using Moose or not, I usually solve the problem by defining a single Config.pm unit which exposes a single variable (or, as the case may be, a subroutine).   And so, “here is where all the configuration variables can be found.”   All that you have to do is to use it.

    Furthermore, I usually define within that unit a conf(variable, variable ...) subroutine which, given a list of keys as its argument list ... (@_) ... traverses the (otherwise private ...) data-structure which it defines, returning a reference that corresponds to the nested list of keys provided ... and dieing if the (nested...) key cannot be found.   (This makes an otherwise difficult-to-debug problem trivial to spot, and to trace to the exact bit of code that was requesting it.)

    The logic within this accessor-subroutine can be as smart or as dumb as it needs to be.   It is simply “the way that everything in the application gets configuration-variables,” and it is equipped to detect and immediately flag any attempts to request undefined keys.   Although my code rarely uses actual “singletons,” this is how I would handle any sort of singleton case:   a well-known public subroutine returns the appropriate reference on-request.

Re: Access to single object from multiple other objects
by moritz (Cardinal) on Oct 31, 2013 at 16:38 UTC
Re: Access to single object from multiple other objects
by kschwab (Vicar) on Nov 02, 2013 at 21:21 UTC

    One option would be a simplistic factory that held a single "shared thing", and was responsible for creating the other objects, so that they could have a reference to the shared thing without having to explicitly pass the "shared thing" around.

    Something like this:

    #!/usr/bin/perl -w use strict; package SharedThing; sub new { my $that = shift; my $class = ref($that) || $that; return bless {"counter" => 0}, ref($that) || $that; } sub hello { my $this=shift; $this->{counter}++; printf "hello, count is [%d]\n",$this->{counter}; } package MyFactory; sub new { my $that = shift; my $class = ref($that) || $that; my $this = {}; bless $this, $class; $this->{sharedthing}=SharedThing->new(); return $this; } sub A {A->new(@_);} sub B {B->new(@_);} package A; sub new { my $that = shift; my $class = ref($that) || $that; my $this = {}; bless $this, $class; $this->{factory}=shift; # call the "hello" method of the "shared thing" so that # we can see that it's indeed a single instance $this->{factory}->{sharedthing}->hello(); return $this; } package B; our @ISA=qw(A); package main; my $factory=MyFactory->new(); my $a1=$factory->A(); my $a2=$factory->A(); my $b1=$factory->B(); my $b2=$factory->B();

    All of the instances of A and B have a reference to the factory passed to them when they are instantiated, and so, they can get to $this->{factory}->{sharedthing}...the single shared object.

Re: Access to single object from multiple other objects (argument)
by Anonymous Monk on Oct 30, 2013 at 23:17 UTC
Re: Access to single object from multiple other objects
by elTriberium (Friar) on Oct 31, 2013 at 19:55 UTC

    Thanks everyone for the suggestions!

    Looks like passing it to the objects as an argument is the preffered solution. My only issue with that is that I have a relatively large object hierarchy, with something similar to the following objects:

    A instantiates B and C B instantiates D D instantiates E [...]

    So now if E needs access to the config module I'll have to pass it all the way down.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1060420]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others studying the Monastery: (3)
As of 2024-04-26 06:16 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found