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

I was told that the singleton is hard to verify.

I was told that static methods / objects are not suitable either.

So basically the only solution is 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 of my other frameworks) uses singleton Logger to log messages (and many other helpers).

With DI, my code would look like this:

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 requires a lot of queries. It seems to me that this is too redundant to write $logger in the constructor every time, especially if you think that PreparedQuery requires many other dependencies in the constructor.

The only solution to avoid the singleton that I found is to use a method (or just a simple function) in the main application, which stores all references to auxiliary objects (Service Locator / Container) but this does not solve the problem of hiding dependencies

So, in your experience besides DI, what good pattern to use?

Decision:

For each creator of interest, PHPunit explains how to solve the Singleton problem (and how to solve the problem of testing static methods using PHP 5.3)

Pretty interesting to read if you ask me.

+8
oop php design-patterns
source share
5 answers

Logging is usually an example where static singletones are in order. In any case, you do not need to scoff at your registration?

+1
source share

Please do not use global .
You need to pass $ logger to the constructors or use the Service Container (also known as the object manager, Service Locator, Resource Manager) instead.
A variation from the Symfony concept http://symfony.com/doc/current/book/service_container.html
You can create your own object manager, and its methods should not be static.

+3
source share

Well, in this case, I would build a builder (or factory). Thus, your factory will introduce you a dependency. This way you can also avoid your global bindings:

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

So you do once:

 $factory = new PreparedQueryFactory($logger); 

Then anytime you need a new request, just call:

 $query = $factory->create(); 

Now this is a very simple example. But if you need to, you can add all kinds of complex logic. But the fact is that by avoiding new in your code, you are also avoiding dependency management. So instead, you can pass factory (ies) around as needed.

The advantage is that all this is 100% verifiable, since everything is entered everywhere (as opposed to using global variables).

You can also use the registry (otherwise called the Service Container or DI Container), but make sure you insert the registry.

+3
source share

The above answers give you some ideas. I will introduce one more: implement the plugin architecture. The registrar becomes a plugin that you can enable / disable / change whenever you want.

A simplified example:

 class Logger implements Observer { public function notify($tellMeWhatHappened) { // oh really? let me do xyz } } class Query implements Observable { private $observers = array(); public function addObserver(Observer $observer) { $this->observers[] = $observer; } public function foo() { // great code foreach ($this->observers as $observer) { $observer->notify('did not work'); } } } 

This removes Logger from the constructor. This is what I prefer if it is not essential for the functioning of the object.

+1
source share

In my understanding, Misko Hevery talks about DI and the new operator, the problem is that you are not too far advanced in implementing DI.

As Hevery always says, you should not mix business logic with object design. However, in two lines of your example, the first ( $query = new PreparedQuery($logger); ) constructs the object, and then the second ( $query->prepare(/* ... */); ) is the business logic.

Obviously, the purpose of this code is to prepare the query, and instead of worrying about how to build PreparedQuery , it should just query it in the class constructor. Or, if he should be able to create many PreparedQueries, he should request a prototype (which he will clone whenever he needs a new one) or a factory object. The thing is, PreparedQuery has a logger and needs to be taken care of somewhere else.

The principle of "asking for what you need" in the constructor is easy to understand in principle, although I am still trying to decide for myself what it means in practice in different situations and how to implement it completely at the top (the "main method" or equivalent). However, I think this principle speaks of the general problem that you are experiencing. This new operator should not be where it is in the first place.

0
source share

All Articles