Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Singletons and Inheritance

by PerlingTheUK (Hermit)
on Jul 20, 2004 at 11:01 UTC ( #375867=perlquestion: print w/replies, xml ) Need Help??
PerlingTheUK has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks,

I would love to hear -yet again- your opinions. I have now decided to use SQLite for my program and want to use this step to also clean up my currently slightly messy data structure.

I have decided to create database source classes for every table, that do the database communication part, as well as object classes, that actually hold the data. All Object classes and source classes have a base class.
Finally there is a source access class, which returns arrays containing the objects, or can match them by my keys.

What I am looking for is a good idea on how to realize the singleton.
As demonstrated in http://c2.com/cgi/wiki?PerlSingleton, there are a couple of ways to create singleton by basically setting the "bless" function to a scalar and just returning this whenever it is required again. Having done a lot of work on updating all these data structures whenever some icky bits in the database changed on C++, I wanted to use the power of Perl to try and get the singleton definition out of the subclasses or any access classes and right into the base database source class.
I am now thinking of a good way to do this. The basic scheme I use for my objects is based on Damian Conway's OO Perl. I plan to use the the singleton technique
package Singleton; my $singleton; sub new { my $class = shift; $singleton ||= bless {}, $class; }
within a subclass. Do I miss out any problems when I just replace $singleton by a hash which includes the classname:
package SingletonBase; my $singleton; sub new { my $class = shift; $singleton{ $class } ||= bless {}, $class; }
The actual base class would look something like this:
# encapsulated class data { my %_singleton; my %_attr_data = ( _dbd => [ undef, 'r/w' ], _dbname => [ undef, 'r/w' ], _dbpass => [ undef, 'r/w' ], _dbserver => [ undef, 'r/w' ] ); my $_count = 0; sub _get { my ( $class ) = @_; return $_singleton{ $class } if ( $defined( _singleton{ $class } ) + ); return 0; } sub _create { my ( $self ) = @_; my $_singleton{ ref($self) } = $self; } sub _accessible { my ( $self, $attr, $mode ) = @_; $_attr_data{$attr}[1] =~ /$mode/; } sub _default_for { my ( $self, $attr ) = @_; $_attr_data{$attr}[0]; } sub _standard_keys { keys %_attr_data } sub _count { my ( $self ) = @_; ++$_count; $self->{ "_id" } = $_count; } } sub new { my ( $caller, %arg ) = @_; my $caller_is_obj = ref( $caller ); my $class = $caller_is_obj || $caller; return _get( $class ) if ( _get( $class ) ); my $self = bless {}, $class; $self->_create(); foreach my $attrname ( $self->_standard_keys() ) { my ( $argname ) = ( $attrname =~ /^_(.*)/ ); if ( exists $arg{ $argname } ) { $self->{ $attrname } = $arg{ $argname } } elsif ( $caller_is_obj ) { $self->{ $attrname } = $caller->{ $attrname } } else { $self->{ $attrname } = $self->_default_for( $attrname ) } } $self->_count(); return $self; }
I have added the functions _get, _create and %_singleton in the closure as well as changed the lines above and below the bless statement. Do I miss anything out? Are there any big setbacks? It seems too easy to me. Would it for some reason be a lot better to go via getInstance methods? Thank you for all help. PerlingTheUK

Replies are listed 'Best First'.
Re: Singletons and Inheritance
by Joost (Canon) on Jul 20, 2004 at 11:12 UTC
    Looks ok to me. But I wouldn't make it completely impossible to create more than one instance (as you are doing here). I'd probably leave the new() method as a normal constructor and add a getInstance() method that implements the singleton.

    Also, are you sure you want $object->new() to be meaningful? There have been discussions on it for ages, because there is no universally accepted behaviour (should it make a clone, or only copy the object reference, or create a new "empty" object...).

      I surely have to take the copy part out as it is useless, for singleton objects anyway, but I want to accept arguments even though the constructor will just ignore use them if a reference to an old object is returned.
      Maybe I should warn the user if he does so.
      I am not sure how new and get instance would work together though, which instance would I return if I have two objects. What is the benefit of this?
        It's just my personal preference - I've run into too many situations where in the end you do NOT want a forced singleton. So I'd do:

        my %instances; sub getInstance { my $class = shift; $instances{$class} ||= $class->new(@_); return $instances{$class}; }
        And if any code calls $class->new() directly, they'll get another instance which getInstance knows nothing about, so it doesn't interfere.

        Also, I think splitting up the code like this makes it a lot clearer.

        OTOH, if you have a good reason to always disallow more than one instance (I can only think of interfaces to hardware), you could rename it so that getInstance is named new() and new() is named _new() or something (and don't document _new() in the public API).

Re: Singletons and Inheritance
by gellyfish (Monsignor) on Jul 20, 2004 at 11:13 UTC

    You might want to check out the code for Class::Singleton which keeps the instance in a package variable for each sub-class, it also gives you the flexibility of how you create each sub-classes instances.

    /J\

Re: Singletons and Inheritance
by gaal (Parson) on Jul 20, 2004 at 11:17 UTC
    First, Perl doesn't distinguish between names like "new" and "getInstance", so there's no language reason to prefer one.

    If your client code has a design reason to know it's dealing with a singleton, you might have a stylistic incentive to use different names, otherwise, make it transparent.

    The most elegant way I know of applying singletonhood to a class (if you don't mind introducing new tech) is with the Aspect module. All your code remains the same; you just say

    use Aspect::Singleton; aspect Singleton => 'Foo::new';

    at the top of your class, and that's it.
      For me a getInstance method would not be part of the class itself but of the access class, that is basically registering with a first object of any kind whenever one is created and then returns either the registered object or if not yet creates a new one. This method normally does not return the sources but effectivley the data and therefor can for reduce the amount of data to what is required.
        What do you mean that the method "normally does not return the sources"? That getInstance would be part of some proxy class? I don't see why this should be part of the Singleton pattern. It causes duplicity in classes that can be confusing.

        The way I see it there are two reasons why you might want a singleton. The first is that you *need to have* only one instance of something, because it represents a unique entity that can only have one state; having multiple instances risks getting many of them out of date. The second is that you *only need* one, for example if the class doesn't really represent an ontic entity but does host some calulations (In that case you might be better off using class methods, but nevre mind that). In this case singletonhood inproves performance but isn't critical to design.

        In the first case, some people would like to make it explicit that a singleton is being used, and thus would prefer different coding styles when using it -- getInstance being the typical spelling. In the second case, there may be less incentive to rub in the singletonhood of the object.

        What I understand you to be saying is that you have some data or representational or proxy layer on top of the object. That's well enough, but it doesn't have much to do with singletons; you can have those on instantiable objects as well.

Re: Singletons and Inheritance
by adrianh (Chancellor) on Jul 20, 2004 at 13:40 UTC

    I'd second the recommendation to consider Class::Singleton.

    Actually, I'm unsure why you need a singleton in this particular instance? Use your singletons wisely is a nice little article on the topic (Java code, but the points still apply.)

Re: Singletons and Inheritance
by bean (Monk) on Jul 20, 2004 at 21:29 UTC
    I personally think Singletons are silly. Why not just use package (class) variables? What's the point of being able to instantiate a class when you only want one instance (which you get for free with package variables)? If you must have Singletons, the way you've done it looks fine - it's a simple concept, it should be simple to implement.

    But what's this about a database class for every table? That doesn't sound right...
Re: Singletons and Inheritance
by clscott (Friar) on Jul 21, 2004 at 20:55 UTC

    Are you trying to re-invent Class::DBI?

    --
    Clayton
      Not exactly. The idea is to create a Buffer of objects the can be read from a database but are in memory for faster access.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://375867]
Approved by gellyfish
Front-paged by broquaint
help
Chatterbox?
[haukex]: I think you're right, I think Pod::Simple is the preferred parser now
[haukex]: But I was just using it as an author test anyway
[Corion]: haukex: Aaah - I thought you were still running these tests on every machine, but you only run these as author or Devel::Cover tests
[Corion]: haukex: Yeah, I think back then I used Test::Inline, which used a pod parser that was going through some changes and I didn't want to cater for all the various versions and thus stopped testing the Pod completely
[choroba]: I usually do this with presentations
[Corion]: But now I think statically (re)generating the Pod tests is a saner approach, and likely I'll regenerate the tests either in Makefile.PL or from xt/ but have them live below t/
[choroba]: I keep the snippets in files of their own, and use a Makefile to syntax highlight them and insert them into slides, while also running them and inserting the output if required
[Corion]: choroba: Ooooh - I didn't think of that! I write my presentations as POD and if it "roughly" looks like Perl code, I should also syntax-check that...
[haukex]: Yes sorry I don't run them all the time, my POD tests are only run as author tests (and are excluded when I'm using Devel::Cover)
[Corion]: choroba: Hmm - no, I keep the snippets inline, but as my framework also has support for capturing output etc., maybe I should do the same...

How do I use this? | Other CB clients
Other Users?
Others wandering the Monastery: (6)
As of 2017-02-27 12:25 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Before electricity was invented, what was the Electric Eel called?






    Results (385 votes). Check out past polls.