Other problems with Singleton

In: Singleton

13 Mar 2010

So, again I will talk about Singleton and why you should be carefull with them. Everytime I think or discuss singleton the following things come in to my mind :

- Global State;
- Hard to test (refer to unit test post);
- Singletonite (I see Singletons everywhere);
- Violating the Single Responsibility Rule.

I could spent a few pages arguing and / or explaining those issues. Instead of that, I would recommend you to watch Miško Hevery’s presentations.

“Ok great,  I am  convinced. Singleton are bad. What are the alternatives ?”

Let’s suppose we have a class named article, that has a save() method allowing to persist this instance of an article (in a database or any kind of persistency layer).

/**
 * Singletons are my best friends
 */
class article
{
    public $title;
    public $text;

    public function save()
    {
        $storage = storage::getInstance();
        if(false === $storage->save())
        {
            logger::getInstance()->log("Sorry !");
        }
    }
}

$myArticle = new article();
$myArticle->title = 'I love sushi';
$myArticle->text = 'I really do...';
$myArticle->save();

What are the problems with the previous code ?

Well, first you can’t mock the storage object and therefore this class can be hard to unit test. Secondly, the save method has a dependency with two objects, those dependencies are hidden within the save method and as a consumer of this API, you would expect to be able to know what the collaborator and / or the dependencies of a class are without having to look at the implementation of some methods.

So, let’s get rid of all singletons !

/**
 * No Singleton, use collaborators instead
 */
class article
{
    protected $storage;
    protected $logger;

    public function __construct($storage, $logger)
    {
        $this->storage = $storage;
        $this->logger = $logger;
    }

    public function save()
    {
        if(false === $this->storage->save())
        {
            $this->logger->log("Sorry !");
        }
    }
}

// Having to instantiate "manually" the storage and logger
// is a actually a bit a pain
$storage = new storage($param1, $param2, ...);
$logger = new logger($paramA, $paramB, ...);

$myArticle = new article($storage, $logger);
$myArticle->title = 'I love sushi';
$myArticle->text = 'I really do...';
$myArticle->save();

We just need to pass the storage and logger objects into the constructor of the article object and now the article class is easier to test (easier to mock objects).


  • Digg
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks

Comment Form

Who am I?

My name is Bashar Al-Fallouji, I work as a Service Architect at Origin Energy (Sydney, Australia).

I am particularly interested in Web applications, Open Source Development, Software Engineering, Information Architecture, Unit Testing, XP/Agile development, etc.

On this blog, you will find mostly technical articles and thoughts around PHP, OOP, OOD, Unit Testing, etc. I am also sharing a few open source tools and scripts.

  • dipan: Hi Bashar It's really awesome that you wrote this code. IT'll save tones of time of all developer. [...]
  • Bashar: Glad that you liked it ! [...]
  • Angel S. Moreno: well, there goes wasting a couple of hours of development and a couple of days of testing. I owe you [...]
  • Bashar: Thats right, the setSaveFile create a files containing an associative array of classname => filen [...]
  • Loggy: Jim's clarification in particular was pretty useful although I did have to dig down into the tree to [...]