1
votes

Consider the following class structure:

enter image description here

Framework code (blue zone)

  • LOG class - simple logging class.
  • DATABASE class - gets LOG class injected. (for lets say, logging errors or SQL queries).
  • REDIS class - also gets LOG glass injected.
  • BUSINESS logic classes - gets all of those above constructor injected.

.

Application code: (yellow zone):

  • Instantiate and use the framework code.

Current framework constructor code is something like:

class Database{
    public function __constuctor (Logger $log, $databaseHost, $databaseUser, $databasePass, $databaseName){...}
}
class Redis{
    public function __constuctor (Logger $log, $redisHost, $redisUser, $redisPass){...}
}
class ACME{
    public function __constuctor (Database $db, Redis $redis, $otherStuff, ...){...}
}

In the application we do something like:

 $logger = new Logger();
 $db     = new Database($logger, 'db_host','db_user','db_user','db_pass');
 $redis  = new Redis($logger, 'redis_host','redis_user','redis_pass');

 $acme   = new Acme($db,$redis, ...other_stuf...);

The constructors gets pretty long and ugly and I am tempted to create a dependency container (DI) and to pass it as a single constructor parameter. .

Consider the principle: "The object should not be aware of the dependency container which contains it", I feel that is not OK, because this will just replace the current multiple dependencies with a new one - the dependency container (DIC) itself.

What I feel good is that I can use the injection of the DIC in the Application section (the yellow part of the image), but its NOT OK to inject the DIC in the base framework code (blue part).

Am I wrong?

Is it OK to inject the DIC?

Isn't it better to use a service locator pattern here?

1
Have you tried googling that topic? You're not the first one to ask this questionNico Haase
yes, tried a lot. Also here, in SO. The recommendations in this case i found are : 1) to split the application into smaller parts to reduce constructor parameter length and dependencies (not suitable for me). 2) use service locator or singleton (static methods) - also considered a bad practice for some reason. Actually, everyone recommend ti use DI, but no one gives a best practice how to DI correct and handle a lot references that needs to be injected779IDEAS
What does "a lot of references" mean? Probably, you have some classes that hardly violate SRP and need to be split up into smaller, more maintainable classes with less dependencies?Nico Haase

1 Answers

1
votes

In the application we do something like:

 $logger = new Logger();
 $db     = new Database($logger, 'db_host','db_user','db_user','db_pass');
 $redis  = new Redis($logger, 'redis_host','redis_user','redis_pass');

 $acme   = new Acme($db,$redis, ...other_stuf...);

This is sometimes referenced as poor man's dependecy injection

What you would rather want (as I guesss) should be something like:

$coolContainer = new SomeCoolDependencyInjectionContainer();
// ... do some configuration on the container
$app = $coolContainer->get('app'); // or whatever its usage interface is
$app->run(); // or any other way to launch the app

The container knows how to construct the application and all its components (the knowledge comes out of either configuration that you provide for the container or it's autowiring capabilities). Please note that instantiation happens outside of application. Application is supposed to get everyting already constructed.

So the bottom line is: if you're using some DI container make sure it knows what logger to use for each component that needs one (actually you might wanna do specific loggers for different parts of the app).

You might find the following links interesting (if not read yet, of course):

https://martinfowler.com/articles/injection.html

http://blog.ploeh.dk/2011/07/28/CompositionRoot/

Update on dependecies:

Usually when deciding on dependencies quantity I keep in mind:

  • Uncle Bob's rule for no more than 3 arguments for a function / method which I personally propagate to constructors
  • single responsibility principle
  • general considerations upon proper code structure

Out of my practice it comes out that even though these ideas are somewhat general and generic following them makes my code cleaner. Cleaner -- meaning more readable and understandable on one hand, and more maintainable on ther other, that being even more important, because it lets go faster and perform refactoring or redesign with less efforts. So basically I believe that best practices here are not problem specific, but rather reflect considerations of somewhat higher level of generality.

Still, concerning final number of dependencies a class comes up with, I don't follow rule of 3 on "no matter what" basis. Occasionly I can have even four. Usually that happens if it's an auxiliary class has no real logic inside. Well, it surely has logic, but not that kind of logic. So generally if I see it's not really worth of efforts I let it stay either until some real issue arises or just forever.