LogoPhly, boy, phly
the weblog and site of Matthew Weier O'Phinney

Monday, February 5. 2007

Extending Singletons

This morning, I was wondering about how to extend a singleton class such that you could retrieve the new class when retrieving the singleton later. In particular, Zend_Controller_Front is a singleton, but what if I want to extend it later? A number of plugins in the Zend Framework, particularly view helpers and routing functionality, make use of the singleton; would I need to alter all of these later so I could make use of the new subclass?

For instance, try the following code:


class My_Controller_Front extends Zend_Controller_Front
{}

$front = My_Controller_Front::getInstance();
 

You'll get an instance of Zend_Controller_Front. But if you do the following:


class My_Controller_Front extends Zend_Controller_Front
{
    protected static $_instance;

    public static function getInstance()
    {
        if (null === self::$_instance) {
            self::$_instance = new self();
        }

        return self::$_instance;
    }
}

$front = My_Controller_Front::getInstance();
 

You'll now get an instance of My_Controller_Front. However, since $_instance is private in Zend_Controller_Front, calling Zend_Controller_Front::getInstance() will still return a Zend_Controller_Front instance -- not good.

However, if I redefine Zend_Controller_Front::$_instance as protected, and have the following:


class My_Controller_Front extends Zend_Controller_Front
{
    public static function getInstance()
    {
        if (null === self::$_instance) {
            self::$_instance = new self();
        }

        return self::$_instance;
    }
}

$front = My_Controller_Front::getInstance();
 

Then the any time I call getInstance() on either My_Controller_Front or Zend_Controller_Front, I get a My_Controller_Front instance!

So, the takeaway is: if you think a singleton object could ever benefit from extension, define the static property holding the instance as protected, and then, in any extending class, override the method retrieving the instance.

Posted by Matthew Weier O'Phinney in PHP at 10:04 | Comments (8) | Trackbacks (0)

Trackbacks
Trackback specific URI for this entry

No Trackbacks

Comments
Display comments as (Linear | Threaded)

This is a very interesting point that I have noticed and find very telling: Using "private" should be thought of as "private can't be extended" or more to the point "private final"

I am always wary of applications that design objects to be extended, and then use "private" instead of "protected." It always seems like good PPP, but in the end, it makes objects non-extendable.

A good rule of thumb: if a user could cause a complete crash or worse a subtle logic bug by modifying the variable, use "private" otherwise always use "protected."

It's like government: secrecy results in things like the Nixon administration and impeachment, it's a whole big mess. :-)
#1 Greg Beaver (Link) on 2007-02-05 12:01 (Reply)
Maybe you could use a dynamic proxy, delegating all other method calls than your own to the aggregated front controller object? You could use interceptors to automatize that. That would at least solve the problem without modifying the ZW code...
#2 Daniel on 2007-02-05 13:35 (Reply)
With the use of protected, surely your sentance :

"Then the any time I call getInstance() on either My_Controller_Front or Zend_Controller_Front, I get a My_Controller_Front instance! "

Isn't strictly correct - order matters - namely calling

Zend_Controller_Front::getInstance() before a My_Controller_Front::getInstance()

Results in a Zend_Controller_Front instance returned all the time; and likewise, calling My_Controller_Front first will result in it always being returned.

Interestingly (from my point of view) redefining a 'protected static $_instance' in My_Controller_Front results in totally different behaviour.
#3 DG (Link) on 2007-02-06 03:27 (Reply)
You're absolutely correct -- the order in which the calls are made matters. Calling Zend_Controller_Front::getInstance() first will force all instances to be Zend_Controller_Front instances -- and vice versa.

Also, good point regarding redefining the $_instance variable in the subclass. If you do so, you've created two separate singleton classes at that point -- which makes it pretty worthless for most things I was trying to do.
#3.1 Matthew Weier O'Phinney (Link) on 2007-02-06 10:56 (Reply)
Thanks, that's a useful observation. But this illustrates one of the reasons I dislike Singletons and try to limit my use of them as much as possible (within reason). When I call Zend_Controller_Front::getInstance(), I expect an instance of Zend_Controller_Front. When I call My_Controller_Front::getInstance(), I expect an instance of My_Controller_Front.

I actually prefer $front = new My_Front_Controller(), like it was changed to briefly. This isn't the consensus, though, and I can live with it. ;-)

Or, in the case of something like Zend_Registry:

$registry = new My_Registry();
$front->setParam('registry', $registry);
#4 Matthew Ratzloff (Link) on 2007-02-07 13:07 (Reply)
The solution is indeed using an external registry..

Registry::getIntance()->mySingleton

obviously this is not a real singleton (only the registry is) but this has the benefit of singletons, still allowing you to extend your classes.

I disagree with gregs points (but obviously this is a matter of taste)

I tend to make most of my properties private.. if a subclass needs the data I'll use a protected getter method..
#5 Evert (Link) on 2007-02-08 16:22 (Reply)
It does not have the same benefits as a Singleton has. You don't use Singletons because you are afraid of global variables - you use a registry system for that.

You use Singletons because you don't know if you gonna need them.

The "Solution" with a protected variable is just bad. It's very very bad. The problem is, that you will never know what you get.

When someone calls Foo::getInstance() somewhere you might get a Foo object. Though if someone else calls Bar::getInstance() in another place, you might get a Bar object.

And this is really not a good thing to have.

In general, you don't want to inherit from a Singleton. If you really really need to do so, then you want to use a registry inside the Singleton which defines what object to create. You need to initialize the singleton class with parameters on what object you really want when getInstance() is called. This will get messy, so it's best to avoid it.

In general, inheriting from a singleton smells like a flawed design...
#5.1 Toni Schornboeck on 2007-02-11 01:29 (Reply)
The singletons in Php5 is very limited for extending as you said - it's true. The main reason is the Php's missing LSB(late static binding), which is going to be included in Php6. However you have still the possibility to drive singletons by SingletonFactory static object, which holds array of instances of all instantiated singletons. I prefer this way than mixing static and dynamic properties in one class. It has also advantages in J2ee, where singleton is really problem.

class SingletonFactory
{
private static $instance_array = array();

public static function GetInstanceOf($class_name)
{
if (!isset(self::$instance_array[$class_name]))
self::$instance_array[$class_name] = new $class_name();

return self::$instance_array[$class_name];
}
}
#5.1.1 Jirka F on 2007-09-14 07:15 (Reply)

Add Comment

Standard emoticons like :-) and ;-) are converted to images.
E-Mail addresses will not be displayed and will only be used for E-Mail notifications

To prevent automated Bots from commentspamming, please enter the string you see in the image below in the appropriate input box. Your comment will only be submitted if the strings match. Please ensure that your browser supports and accepts cookies, or your comment cannot be verified correctly.
CAPTCHA

 
 
  • Home
  • Resume
  • Blog
  • Phly PEAR Channel
  • Contact Me
  • About this site

ZCE

Zend Education Advisory Board Member

Add to Technorati Favorites

Calendar

Back September '08 Forward
Mon Tue Wed Thu Fri Sat Sun
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30          

Quicksearch

Links

  • PHLY - PHp LibrarY
  • Paul M. Jones
  • Mike Naberezny
  • Shahar Evron
  • Planet PHP
  • Zend Where I now work
  • Garden.org Where I once worked

Archives

September 2008
August 2008
July 2008
Recent...
Older...

Categories

XML Linux
XML Personal
XML Aikido
XML Family
XML Programming
XML Dojo
XML Perl
XML PHP

All categories

Syndicate This Blog

XML RSS 0.91 feed
XML RSS 1.0 feed
XML RSS 2.0 feed
ATOM/XML ATOM 0.3 feed
ATOM/XML ATOM 1.0 feed
XML RSS 2.0 Comments

Show tagged entries

xml best practices
xml books
xml conferences
xml dojo
xml dpc08
xml file_fortune
xml linux
xml mvc
xml pear
xml personal
xml php
xml programming
xml ubuntu
xml webinar
xml zendcon
xml zend framework
© 2004 - present, Matthew Weier O'Phinney
matthew-web <at> weierophinney.net