Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Singletons and Inheritance

by PerlingTheUK (Hermit)
on Jul 20, 2004 at 11:01 UTC ( [id://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
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://375867]
Approved by gellyfish
Front-paged by broquaint
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (4)
As of 2024-03-29 01:42 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found