Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw

Making an Existing Class a Singleton

by agianni (Hermit)
on Jan 23, 2008 at 16:41 UTC ( [id://663825] : perlquestion . print w/replies, xml ) Need Help??

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

I have a config class for my Web application, and for a number of reasons, I think it would be useful to make it a singleton class. Unfortunately, it's already set up, has a standard interface and inherits from Class::Accessor. I know about Class::Singleton, but it doesn't look like there would be any easy way to take an existing class and make it a singleton class with that.

Short of encapsulating my current config object in a new singleton class, can anyone suggest a way to accomplish this?

perl -e 'split//,q{john hurl, pest caretaker}and(map{print @_[$_]}(joi +n(q{},map{sprintf(qq{%010u},$_)}(2**2*307*4993,5*101*641*5261,7*59*79 +*36997,13*17*71*45131,3**2*67*89*167*181))=~/\d{2}/g));'

Replies are listed 'Best First'.
Re: Making an Existing Class a Singleton
by halley (Prior) on Jan 23, 2008 at 17:13 UTC
    You highlight the exact difference between a pattern and a class library. The Gang of Four who published the Design Patterns book decided to describe patterns, or recurring ideas in feature implementation, instead of publishing a book on some great classes to put into any language's built-in class library. Patterns are idioms, not parts.

    It seems very neat and tidy to offer a class like Class::Singleton but the devil is in the details, and your exact scenario is where theory meets reality. Don't think "I have to glue on some premade code to adopt a pattern." Think "understanding the standard implementation of a standard pattern will help me effect the same feature in my own code quickly and robustly."

    Another poster has already shown you how to make a typical singleton-compliant constructor. Make a hidden module variable that keeps track of which instance is regarded as the master instance, and check that variable before making a possibly redundant instance. That's the singleton pattern. It can be done in your own code in 30 seconds.

    You don't need a class library to implement patterns for you, and any class library purporting to implement academic patterns for you is likely going to have some over-engineered non-elegant features bolted on to deal with real-world problems. They'll call it "flexibility."

    [ e d @ h a l l e y . c c ]

Re: Making an Existing Class a Singleton
by kyle (Abbot) on Jan 23, 2008 at 16:54 UTC

    Here's the new method from Class::Accessor:

    sub new { my($proto, $fields) = @_; my($class) = ref $proto || $proto; $fields = {} unless defined $fields; # make a copy of $fields. bless {%$fields}, $class; }

    If you haven't defined a new method of your own, that's what you're using. In that case, you can just write your own, based on that, to be something like this:

    my $singleton; sub new { my($proto, $fields) = @_; my($class) = ref $proto || $proto; if ( ! defined $singleton ) { $fields = {} unless defined $fields; # make a copy of $fields. $singleton = bless {%$fields}, $class; } return $singleton; }

    After that, you should be able to use it as usual with the caveat that after the first time the object is created, subsequent attempts to create it will not have the arguments to new honored. That is, if you do this:

    my $c1 = C->new( foo => 'bar' ); my $c2 = C->new( foo => 12345 );

    ...then $c2->{foo} will still be 'bar'. If you're not passing any arguments to new, this won't be a problem.

    If you've written your own new instead of using the one from Class::Accessor, you can adapt it in a similar way.

      Rather than duplicating the whole parent class method, you can let it do most of the work:
      my $singleton; sub new { my $proto = shift; if ( !defined $singleton ) { $singleton = $proto->SUPER::new( @_ ); } return $singleton; }
      If you use perl 5.10 (or want to start thinking in that direction) the variable $singleton is a perfect candidate for a state variable.
      That makes sense. I'm used to caching stuff in the object's blessed hash, but that's the wrong scope for the kind of caching that a singleton needs. It was so obvious that I missed it :)
      perl -e 'split//,q{john hurl, pest caretaker}and(map{print @_[$_]}(joi +n(q{},map{sprintf(qq{%010u},$_)}(2**2*307*4993,5*101*641*5261,7*59*79 +*36997,13*17*71*45131,3**2*67*89*167*181))=~/\d{2}/g));'
Re: Making an Existing Class a Singleton
by salva (Canon) on Jan 23, 2008 at 17:46 UTC
      I'd recommend against this CPAN Module for one reason. You have to extend it to make use of it.

      I'd suggest creating a class that has a package scoped variable that you can feed and retrieve single objects. Feed it way of instantiating your object if it isn't created.

      Singleton objects are always a very touchy, delicate things. Containment, testing and what not are difficult.

        I'd recommend against this CPAN Module for one reason. You have to extend it to make use of it.

        And what is the problem with that?

Re: Making an Existing Class a Singleton
by pmccool (Initiate) on Jan 25, 2008 at 09:54 UTC
    I've done this by memoizing the constructor. Quick and easy...