Object containers (DIC, Service Locator, IoC)

Here is my code as an example of inversion of control

class DatabaseContainer {
    protected $config = array();
    private $adapter;
    private $query;

public function __construct($dsn, $username, $password, $driverOptions=null) {
	$this->config = compact('dsn','username','password','driverOptions');
	$this->adapter = new PDOAdapter($dsn, $username, $password, $driverOptions);
	$this->query = new QueryAdapter($this->adapter, new MySQLQueryBuilder());
}


public function getAdapter() {
	return $this->adapter;
}

public function getQueryAdapter() {
	return $this->query;
}

public function getOptions() {
	return $this->options;
}
}
I know that this violates Single Responsibility Principle because it acts as an container and also responsible for creating objects. I know it can be solved by injecting the factory object. But, I think it's expensive to create object just for a simple instantiating. And, it also hides dependencies. **But, what's the disadvantage of hiding dependencies ?**

Then, I googled for better solution, and found that there is dependency injection container. I know that it doesn’t violate SRP, because it just acts as a container and doesn’t create object but injected with objects by get and set methods.

So, what’s the better solution ?

And, what’s the difference between Inversion of Control and service locator ?

Should I just use container or call the variable ?

Depending on the complexity of the application, it makes it more difficult to understand how things are “connected”.

DIC should be taken over a service locator for sure.

From Martin Fowler:

Service Locator vs Dependency Injection

The fundamental choice is between Service Locator and Dependency Injection. The first point is that both implementations provide the fundamental decoupling that’s missing in the naive example - in both cases application code is independent of the concrete implementation of the service interface. The important difference between the two patterns is about how that implementation is provided to the application class. With service locator the application class asks for it explicitly by a message to the locator. With injection there is no explicit request, the service appears in the application class - hence the inversion of control.

Not sure what you mean here, but it depends on your application. The more complex it gets, the more likely you’ll want to use a DIC. This is a pretty good answer to the question, what are the advantages and disadvantages of using a DIC.

Primary advantage: encourages attention to the Dependency Inversion Principle, which helps reduce the overall cost of changing the system.

Primary disadvantage: highlights design problems and causes many programmers to blame dependency injection for the design problems.

Other Advantages:

  • more parts of the system are pluggable-replaceable, which makes it
    easier to test parts in isolation and makes it easier to rearrange
    generic parts into new applications
  • puts pressure on the design to separate behavior into highly cohesive units, which has the side
    effect of creating smaller, more-reusable components
  • puts pressure on the design to introduce abstraction layers, which makes it easier to
    reason about layers of the system independently
  • puts pressure on the design to make contracts between components more precise, which makes
    it easier to know how each part of the system depends on its
    immediate collaborators, which makes it easier to isolate the causes of problems
  • makes indirection easier so that the programmer can more easily introduce new abstractions

Other Disadvantages:

  • puts pressure on the design to separate behavior into highly cohesive
    units, which makes cohesion problems highly visible, and someone who
    doesn’t know how to improve cohesion will not know what to do about
    it
  • programmers accustomed to holding large amounts of detail in their
    head will find it more difficult to follow the code, due to the
    separation of responsibilities
  • more types, especially in languages that have explicit interface types, like Java and C# programmers
    accustomed to service locator pattern find the style strange
  • programmers unaccustomed to designing by contract find the style
    verbose; instead they put all the details together in one place where
    they can easily become jumbled together
  • makes indirection easier without necessarily making abstraction easier, so the programmer
    might add indirection without care
2 Likes

Scott answered the other questions very well but this is better explained with a simple code example. Essentially the difference is that with a service locator, the object that has the dependencies knows about the service locator, with DI/IoC, the object knows nothing about it.

Service Locator:

class A {
    public $b;

   public function __construct(Registry $registry) {
        $this->b = $registry->get('B');
   }
}

new A ($registry);

Whereas with DI, the class with the dependency knows nothing about the service locator:

class A {
    public $b;

   public function __construct(B $b) {
        $this->b = $b;
   }
}

new A ($registry->get('B'));

As you can see, the same $registry object can be used to implement both patterns, it’s the way that the objects interact that differentiates the two.

1 Like

Using DI Container, where should I instantiate the container and inject the objects ?
Is it in the constructor of basecontroller class ?
Or, should I just code it in global (outside class) ?

The container will contain essential things such as database classes, datetime classes, router, etc…
Sometimes, I’m a bit confused where to put the code, since the code is most used in the application and hard to classify

Since the DIC builds a “globalization” of “services”, objects that perform global tasks, you should instantiate your DIC upon initialization of your application or at the latest, before the first service is needed.

Be careful about your use of the DIC. I noticed you added “router” in the list of things to add to the container. Is the router really something you need to share across different concerns or layers of your application? Usually, the DIC is needed for “cross cutting concerns”, tasks, which you need in multiple places in your application. Mailing, Logging, Persistence, Caching, Authorization, etc. Routing, I would think is, in most instances, its own layer within your application.

Scott

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.