I'm working in a web app framework, and part of it consists of a number of services, all implemented as singletons. They all extend a Service class, where the singleton behaviour is implemented, looking something like this:
class Service { protected static $instance; public function Service() { if (isset(self::$instance)) { throw new Exception('Please use Service::getInstance.'); } } public static function &getInstance() { if (empty(self::$instance)) { self::$instance = new self(); } return self::$instance; } }
Now, if I have a class called FileService implemented like this:
class FileService extends Service { // Lots of neat stuff in here }
... calling FileService::getInstance() will not yield a FileService instance, like I want it to, but a Service instance. I assume the problem here is the "self" keyword used in the Service constructor.
Is there some other way to achieve what I want here? The singleton code is only a few lines, but I'd still like to avoid any code redundance whenever I can.
All you need to extend a singleton class is a constructor with protected or package-default in the singleton class. If there are only private constructors you simply won't be able to extend it. If there are public constructors then it's not a singleton class.
The truth is that singletons aren't inherently bad if they're used correctly. The goal of the singleton pattern is to ensure only one instance of a class is alive at any one time. That, however, is not the goal many developers have in mind when using singletons.
I would argue the most common way to implement a singleton is to use an enum with one instance. That might be a "better" way but definitely not the most common way. In all the projects I have worked on, Singletons are implemented as I have shown above.
Can you modify a singleton? Multiple Singletons Created by a Factory Specially Asked to Create Multiple Objects. One of the strengths of the Singleton design pattern, as opposed to static methods, is that if you change your mind and want more than one, the Singleton class can be easily altered.
Code:
abstract class Singleton { protected function __construct() { } final public static function getInstance() { static $instances = array(); $calledClass = get_called_class(); if (!isset($instances[$calledClass])) { $instances[$calledClass] = new $calledClass(); } return $instances[$calledClass]; } final private function __clone() { } } class FileService extends Singleton { // Lots of neat stuff in here } $fs = FileService::getInstance();
If you use PHP < 5.3, add this too:
// get_called_class() is only in PHP >= 5.3. if (!function_exists('get_called_class')) { function get_called_class() { $bt = debug_backtrace(); $l = 0; do { $l++; $lines = file($bt[$l]['file']); $callerLine = $lines[$bt[$l]['line']-1]; preg_match('/([a-zA-Z0-9\_]+)::'.$bt[$l]['function'].'/', $callerLine, $matches); } while ($matches[1] === 'parent' && $matches[1]); return $matches[1]; } }
Had I paid more attention in 5.3 class, I would have known how to solve this myself. Using the new late static binding feature of PHP 5.3, I believe Coronatus' proposition can be simplified into this:
class Singleton { protected static $instance; protected function __construct() { } final public static function getInstance() { if (!isset(static::$instance)) { static::$instance = new static(); } return static::$instance; } final private function __clone() { } }
I tried it out, and it works like a charm. Pre 5.3 is still a whole different story, though.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With