Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is DI the only solution to Singleton and/or static objects?

I have been told that Singletons are hard to test.

  • http://misko.hevery.com/2008/08/17/singletons-are-pathological-liars/
  • http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/

I have been told that Static methods/objects are no good either.

  • http://misko.hevery.com/2008/12/15/static-methods-are-death-to-testability/

So basically the only solution appears to be dependency injection.

But ... I really can't get used to DI, take this example:

In my framework I have a class that manages SQL. This class (and many other of my framework) uses a singleton Logger to logs message (and many other helpers).

With DI my code would turn to:

global $logger; //> consider i have been instanciated it at the start of my fw

$query = new PreparedQuery($logger);
$query->prepare() etc.

Now that doesn't seem too bad. But consider a page that needs many queries I believe it's pretty redundant to have to write everytime $logger in the constructor, especially if you consider if PreparedQuery needed many other dependencies in the constructor.

The only solution to avoid singleton that I have found is to use a method (or just a simple function) in the main application that stores every references to this helper objects (Service Locator/Container) but this doens't solve the problem of hiding the dependencies

So in your experience other than DI what is a good pattern to use?

Solution:

For everyone interesetd the creator of PHPunit explains how to solve the Singleton problem (and how to solve static methods testing problem with PHP 5.3)

  • (singleton) http://sebastian-bergmann.de/archives/882-Testing-Code-That-Uses-Singletons.html
  • (static) http://sebastian-bergmann.de/archives/883-Stubbing-and-Mocking-Static-Methods.html

Pretty interesting read if you ask me.

like image 607
dynamic Avatar asked May 16 '11 21:05

dynamic


People also ask

Is dependency injection always singleton?

The use of singletons and dependency injection is not mutually exclusive. A singleton can implement an interface, therefore can be used to satisfy a dependency on another class. The fact that it is a singleton does not force every consumer to obtain a reference through it's "GetInstance" method/property.

Can singleton class have static method?

However, unlike a static class that can have only static objects, a singleton class can have both static and non-static objects. Hence from the perspective of memory management, you can take advantage of garbage collection for the managed objects when you're using a singleton class.

Should all singleton methods be static?

Singletons may or may not have state and they refer to objects. If they are not keeping state and only used for global access, then static is better as these methods will be faster. But if you want to utilize objects and OOP concepts (Inheritance polymorphism), then singleton is better.

Which one should I choose static or singleton pattern?

A Singleton can implement interfaces, inherit from other classes and allow inheritance. While a static class cannot inherit their instance members. So Singleton is more flexible than static classes and can maintain state.


2 Answers

Please, don't use global.
You need to pass $logger in constructors or pass Service Container instead (also known as Objects manager, Service Locator, Resources Manager).
Variant from Symfony framework http://symfony.com/doc/current/book/service_container.html
You can create your own Objects Manager, and his methods should not be static.

like image 58
OZ_ Avatar answered Nov 09 '22 09:11

OZ_


Well, in this case, I would build a builder (or factory) instead. So your factory would inject the dependency for you. That way you can also avoid your globals:

class PreparedQueryFactory {
    protected $logger = null;
    public function __construct($loggger) {
        $this->logger = $logger;
    }
    public function create() {
        return new PreparedQuery($this->logger);
    }
}

That way, you do once:

$factory = new PreparedQueryFactory($logger);

Then any time you need a new query, just call:

$query = $factory->create();

Now, this is a very simple example. But you could add all sorts of complex logic if you need. But the point is, by avoiding new in your code, you also avoid managing the dependencies. So instead, you can pass the factory(ies) around as needed.

The benefit, is that all of this is 100% testable, since everything is injected everywhere (as opposed to using globals).

You can also use a registry (otherwise known as Service Container, or DI Container), but be sure you're injecting the registry in.

like image 38
ircmaxell Avatar answered Nov 09 '22 09:11

ircmaxell