1

I try to implement a singleton pattern with the capability of passing arguments to the instructor. For the singleton pattern I want to create a base class. The main problem with this is that not all derived classes have the same amount of parameters. For the singleton base class I have this so far:

abstract class Singleton
{
    /**
     * Function: __construct
     * Description: the function which creates the object.
     */
    protected function __construct()
    {

    }
    private final function __clone()
    {
    }
    public static function getInstance()
    {
        $args = func_get_args();
        static $instance = null;

        return $instance = $instance ?  : new static();
    }
}    

So far, this works, but I can't pass arguments to the constructor with this. Of course I can directly pass the $args array to the constructor, but I don't want to have to unfold the array in each constructor of the derived classes, I want to be able to pass the arguments as normal parameters.

I've tried several things already: I tried using call_user_func_array, but I couldn't figure out how to construct an object with this function (if it's possible at all), and I've also tried using the ReflectionClass. The problem with the ReflectionClass was that this class can't access the constructor since the constructor is protected.

So does anyone has any ideas for me how to solve this?

ps. sorry if I'm acting difficult, but I just try to find the best solution (and understand that solution).

Tiddo
  • 6,331
  • 6
  • 52
  • 85

2 Answers2

3

First off, you'd be better to use a static member variable. And since you can't dynamically call new, you're stuck using an init function.

How I would do it is:

abstract class Singleton
{
    protected static $instances = array();
    /**
     * Function: __construct
     * Description: the function which creates the object.
     */
    private function __construct()
    {
    }
    private final function __clone()
    {
    }

    protected abstract function _init();

    public static function getInstance()
    {
        $args = func_get_args();
        $class = strtolower(get_called_class());
        if (!isset(self::$instances[$class])) {
            self::$instances[$class] = new $class();
            call_user_func_array(
                array(
                    self::$instances[$class],
                    '_init'
                ),
                $args
            );
        }
        return self::$instances[$class];
    }
}

So, your extending class would use _init instead of __construct:

class foo extends Singleton {
    protected function _init($foo) {
        $this->foo = $foo;
    }
}

Notice that Singleton::__construct is now private to prevent being mis-used.

All this is not withstanding the fact that singletons are evil and should be avoided. So if you must use them, you can. But find a better way, since they really are that bad...

ircmaxell
  • 163,128
  • 34
  • 264
  • 314
  • note: this is a "multiton" not a standard "singleton", and [looks remarkably similar to the code posted on wikipedia](http://en.wikipedia.org/wiki/Multiton_pattern). – zzzzBov Feb 04 '11 at 17:20
  • Isn't there a cleaner way (php 5.3?) to achieve this? I like the 'new static' syntax for this in php 5.3 very much, but as far as I know, it's not possible to call a constructor using call_user_func_array. Btw I think that if used well, singletons aren't always evil: I prefer singletons over global vars/pure static classes, and for e.g. database access I don't want every class which needs to access the database to create it's own connections. – Tiddo Feb 04 '11 at 17:23
  • @Tiddo: it's not possible to call `call_user_func_array` to create new objects. And you should read those links in the last sentance. You shouldn't trade singletons (a fancy global var) for global vars or static classes (another fancy global var). You should trade them for [Dependency Injection](http://www.potstuck.com/2009/01/08/php-dependency-injection/). Give classes what they need, don't pull what you need from a class... – ircmaxell Feb 04 '11 at 17:29
  • @ircmaxell I like that Dependency Injection style, I haven't thought of that yet. However, the dependency injection pattern as shown in your link still uses a static class (the container class), and if you don't use a container class, you will have to use global vars for e.g. the database connection. So I don't see why it's better. – Tiddo Feb 04 '11 at 17:38
  • @Tiddo: You could do it with a manager (container class), or you could just pass around the dependencies as needed. I'd suggest taking a look at: [this related answer](http://stackoverflow.com/questions/130794/what-is-dependency-injection) – ircmaxell Feb 04 '11 at 17:45
  • Isn't "Dependency Injection" just a form of the [Factory Method](http://en.wikipedia.org/wiki/Factory_method)? – zzzzBov Feb 04 '11 at 17:50
  • @zzzz: No. They have nothing to do with one another except that they can make use of each other. DI is a way of passing around dependencies (usually at creation time), whereas FM is about creating objects. But they are different concepts (a FM can use DI, but doesn't need to). – ircmaxell Feb 04 '11 at 17:55
  • @ircmaxell as I said, a container is usually a pure static class (and in fact, if you only want one instance of some class, even with the DI pattern you use the singleton pattern, only applied differently ([singleton definition](http://whatis.techtarget.com/definition/0,,sid9_gci854555,00.html))). And passing around dependencies isn't really useful if you need to be able to access certain objects from anywhere in your application. – Tiddo Feb 04 '11 at 17:55
  • @ircmaxell, Alright, I mistakenly thought that incorporating a factory method was part of DI, rather than an example of how to simplify DI. – zzzzBov Feb 04 '11 at 17:57
  • @Tiddo: I'd argue that if you need to access certain objects everywhere, that's more of the need for DI, since DI loosens the coupling of the classes and hence making your code easier to maintain and debug. And there are tons of DI implementations out there, just google it. I just posted a few quick references. There are [tons](http://www.phparch.com/2010/03/static-methods-vs-singletons-choose-neither/) of [articles](http://na.isobar.com/2009/inversion-of-controldependency-injection-with-php/) out there showing why DI is better and how to use it... – ircmaxell Feb 04 '11 at 18:00
  • @ircmaxell after reading [this](http://www.phparch.com/2010/03/static-methods-vs-singletons-choose-neither/) link you sent, I understood better what you meant. Thank you, I've learned something useful today! – Tiddo Feb 04 '11 at 18:05
  • @Tiddo: Glad I could help! There's so much out there, that it can be hard to figure out what gets the point across best (especially when I already understand the point). I will bookmark that link for use in future references. Thanks! – ircmaxell Feb 04 '11 at 18:07
1

If you mean that you want some default settings in your instance, you can just set up a private settings array in the child:

class Simpleton extends Singleton
{
  private $settings;

  protected function __construct()
  {
    $this->settings = array(
      //default settings here
    );
  }
}

Don't try to incorporate parameters into a singleton. If you need a parametrized constructor, don't bother with a singleton.

The singleton should have default values, and then accessors/mutators for adjusting whichever settings should be adjustable.

zzzzBov
  • 174,988
  • 54
  • 320
  • 367
  • @zzzzBov It isn't really for default settings. to simplify a part of my situation: I am creating an application with different starting points (different webpages), but different starting points needs different databases. So I want to create 1 database connection class, but the database used depends on the page which is requested. I know I can use a seperate setDabase method in this case, but it is not really practical to do that in my situation, and I like it better to do that on initializing the singleton. – Tiddo Feb 04 '11 at 17:29
  • @Tiddo, don't use a singleton for database connectivity. Structurally speaking it would make sense to handle multiple connections to multiple databases. A singleton is not the correct design pattern for this case. – zzzzBov Feb 04 '11 at 17:42
  • @zzzzBov, I have multiple connections to multiple databases, but it rarely happens I have to access 2 databases at the same time. But I do need to access the same database in one run many times, and it isn't a really good idea to create new connections for every time I need to access that same database. It's better to have one connection per database, so I need some type of global access to the connections. – Tiddo Feb 04 '11 at 17:45
  • @Tiddo, then keep a reference to the connected database(s) in global scope. Unless you're working entirely from a closure (which would be absurd), it's impossible to *not* introduce new variables/functions to the global scope. I'm **not advocating polluting** the global scope with lots of variables, but *important* variables *should* be accessible globally. – zzzzBov Feb 04 '11 at 17:55
  • @zzzzBov I know I can make this in the global scope, but I'm trying to avoid that if possible;) ps. sorry I'm such a pain in the ass, I'm just trying to find a way to solve this in a nice way, which is useful for my application – Tiddo Feb 04 '11 at 17:59
  • @Tiddo, you're not a pain, I had thought about using a singleton for a database connection a while back. I found it was better to just make a normal Database class with a static array of instances. when constructed, the database connects and adds itself to the array, when destructed the database closes open connections and removes itself from the array. Part of the database constructor was a key which could be used to access the database from a `getInstace($key)` method. Of course the issue of matching keys needs to be addressed as well. I'm sure there's a better way than what I described. – zzzzBov Feb 04 '11 at 18:04
  • @zzz: I'd argue that no variables should be accessible globally (via variables, singletons, registries, etc). Global data is state-dependent and completely kills testability and maintainability. Instead declare what data you need to work in the interface and let the callee pass it in. It will create a lot looser and more robust coupling in your codebase... – ircmaxell Feb 04 '11 at 19:02
  • @ircmaxell, without closures, you will eventually need to define a function, or class in the global scope. If you've defined a static member with a static accessor on a class, you have allowed a variable to be accessed (albeit with fancy boxing). I agree that global variables have a tendency to kill testability/maintainability, but there are going to be a few exceptions. If you're not using an OOP paradigm in PHP, you will want a namespace/pseudo-namespace/closure which is, itself, in the global scope. – zzzzBov Feb 04 '11 at 19:50
  • I think of global variables/functions/classes/etc as similar to the foundation of a building. Don't build your house on sand, don't build your house on dirt: build your house on *one* **solid** foundation. If you start needing more than one foundation, then the structural integrity lessens (that is: in respect to variables, multiple classes and functions tend to be encapsulated as their own "building"). – zzzzBov Feb 04 '11 at 19:54