Dependency Injection prevent dependency on the dependency container - php

After puzzling around with the Dependency injection I'm worried about getting dependent on the DI container. So I thought I tie everything together with factories. But there seem to be an awful a lot of factories needed (One for every object(?)).
Now what happens is the following:
class houseFactory
{
protected $_di;
public function __construct(\DI $di)
{
$this->_setDI($di)
}
protected function _setDI(\DI $di)
{
$this->_di = $di;
}
public function createObject()
{
return $this->_di->create('house');
}
}
The houseFactory basicly wires the creation and calling code together without having to know how to create a house. But is it really oke to actually call for a new factory for every needed object?
It seems so for now because lets say house isn't so easily resolved by DI container alone but needs certain rules. For example there should be only one instance of it (also known as 'shared'). Those rules would go into the factory.
My questions are:
1) Is this acceptable?
I predict a huge collection of factories and my concern is that it's going to be an overkill/huge drawback/against what it is supposed to do.
2) When is it actually 'ok' to use the 'new' keyword?
I understand why moving the logic to create a new object with dependency is good practice. But it seems circular.
3) Where and how does the dependency injection container come into play in applications?
From what point of do you actually use/call the DI container? It seems like hardcoding the DI container into your application? or is that actually the point?
Hopefully my questions aren't to vague.
Thanks in advance.

The main purpose of DI is having the dependency injected, not injected the object that responsible to resolve / create the object. In your example, say that we want to use houseFactory class to get house class and use it (example the goHome method).
$house = $houseFactory->createObject();
$house->goHome();
The implementation not only makes your caller to dependent on the house class, but it is also dependent to houseFactory class, which is a useless dependency.
As TrueWill said, the only place an application responsible to do object creation is composition root. The composition root is a start point of every application. Unfortunately I don't think there is any composition root in pure php class, however there should be in php frameworks such as CI or Zend.
Why should we do object creation in composition root?
to make the scope of object creation become very small. Composition root usually only contains small logic to start the application. Maybe some other logic to handle general exception but not other else. Therefore it is safe to do object creation because no specific business logic exists there.
the composition root does not being created in other class. It is being called as application startup, so it is safe to create objects in a class there.
To understand more about composition root you can read at Mark Seeman's book or his blog.
Then how do we access the object created in composition root?
Some application has good support to DI, such as ASP.Net MVC, however some not like ASP.Net Webform. In applications that has good support, then you can inject the class directly from composition root. However if not, maybe you can access the composition root DI Container via static class, but only at page's constructor. So it minimize the access to composition root.
EDIT1:
Here is usage example of house class without DI Container written in php (maybe not working, haven't used DI in php).
class houseConsumer{
protected $_house;
public function __construct(\DI $house)
{
$this->_house($house)
}
public function doSomething(){
// do something
$this->_house->goHome();
}
}
class compositionRoot{
public function main(){
$diContainer = // define the DI Container
$houseConsumer = new houseConsumer($diContainer->create('house'));
$houseConsumer->doSomething();
}
}
This is the example in C# console:
public class HouseConsumer{
//constructor
public HouseConsumer(IHouse house){
this.house = house;
}
IHouse house;
public void DoSomething(){
house.GoHome();
}
}
//this is the composition root
public class Program{
public static void Main(string[] args){
List<object> diContainer = new List<object>();
//populate diComponent
HouseConsumer consumer = new HouseConsumer((IHouse)diContainer[0]);
consumer.DoSomething();
}
}
EDIT 2:
Is the DI container actually used outside the 'main' method? How do I create new objects outside the 'main' method? Lets say I am a few Objects deep. How do I create a house there?
What I have exampled above can be used in Asp.Net MVC, where the page is also automatically resolved using an IOC Container. Unfortunately, if you need the DI Container in pages like Asp.Net webforms, you should use static, but assign the objects in constructor only. Example (in C#, I don't know how to use static at php).
public class Page1 : Page{
public Page1(){
this.house = CompositionRoot.DiContainer.Create("house");
}
private IHouse house;
}
This implementation still safe, because the DIContainer only used at constructor level of a page. But make sure do not do this in Service class. Example:
public class Renter : IRenter{
public Renter(){
this.house = CompositionRoot.DiContainer.Create("house");
}
private IHouse house;
}
Is a bad design. The Renter class is dependent on DIContainer. To solve that, inject the IHouse class.
public class Renter : IRenter{
public Renter(IHouse house){
this.house = house;
}
private IHouse house;
}
Then the caller of Renter class should pass the house class. The example is to assign the IRenter class to DIContainer using poor man's.
Dictionary<Type, object> DiContainer = new Dictionary<Type, object>();
IHouse house = new House();
IRenter renter = new Renter(house);
DiContainer.Add(typeof(IHouse), house);
DiContainer.Add(typeof(IRenter), renter);
Then you use it in Page:
public class Page1 : Page{
public Page1(){
this.renter = (IRenter)CompositionRoot.DiContainer[typeof(IRenter)];
}
private IRenter renter;
}

Related

Better way to create objects with dependencies in PHP

I am currently working on a PHP project where I need to create objects of a class RelevantClass. I am using Dependency Injection via DI Container.
Underlying Structure
Let's suppose I have the following class structure:
class SomeServiceClass {
}
class EntityClass {
}
class RelevantClass {
/**
* #Inject
*/
private SomeServiceClass $service
public function __construct(EntityClass $entity){
$this->service = inject(SomeServiceClass::class)
$this->entity = $entity;
}
public function someMethod() : void
{
...
$this->service->doSomething($this->entity);
$this->entity->doSomething();
...
}
}
The issue here is that my EntityClassObject needs to be modified by my RelevantClass, as in I can't pass in an injected instance of EntityClass into the constructor of RelevantClass.
The Service class includes some helper methods and other functionality, so it can be injected directly.
Now my question is, how should I instantiate my RelevantClass class?
Approach 1
The way it works right now is that I simply call the constructor:
$entity = ... # Getting the entity from the database
$relevantObject = RelevantClass($entity); # here my service is instantiated by explicitly calling the di container
$relevantObject->someMethod();
That way, my Service is set by the constructor via injection container and my entity is passed through the constructor argument.
Approach 2
Although it is working, I am wondering if it were better practice if I structured my RelevantClass in a way that allows me to instantiate it via DI Container, too?
$entity = ... # Getting the entity from the database
$relevantObject = inject(RelevantClass::class); # this would inject my service class as well
$relevantObject->setEntity($entity);
$relevantObject->someMethod();
Briefly summed up, someMethod needs SomeServiceClass to perform some operations on an EntityClass instance. SomeServiceClass can be injected, while EntityClass cannot.
What is the way to go here? I am aware that both approaches woudl work, but which one of them would be best practice? The project I am currently working on barely uses the constructor to instantiate classes, so I am wondering, whether there are any inherent advantages to the second approach?

Scalable way of implementing the Registry pattern in PHP?

I was wondering if there's a good way to implement the registry pattern in PHP, let me be more clear:
I do know that a Registry is used when you need to keep track of the object you instantiate in order to reuse them and not re-instantiate them again from script to script, e.g. I have a Database class that I want to instantiate only once and then use for all my scripts and I do not want to re-instantiate it again and again. Another example could be a User class that represents an instance of the currently logged in user. I could not use a Singleton in this case, cause e.g. I need another User instance for example when I want to retrieve a friend of the currently logged in user etc.
So I came up with the idea that the Registry better suits this kind of needs in such cases.
I also know that there are two ways of implementing it, or better two ways in order to access the stored instances:
Explicitly or externally, meaning that the Registry should be called every time you need to recover an instance inside your scripts or you need to put an instance inside of it;
Implicitly or internally, meaning that you make kind of an abstract class with a getInstance() method that returns an instance with the get_called_class() late static binding feature, adds it to the registry and then return that instance from the registry itself taking care that if a $label parameter is passed to the getInstance() method, then that particular instance from the registry will be returned. This approach is kinda transparent to the consumer and in my opinion is cleaner and neater (I'll show both implementations, though).
Let's take a basic Registry (really simple implementation, just an example took from a book):
class Registry {
static private $_store = array();
static public function set($object, $name = null)
{
// Use the class name if no name given, simulates singleton
$name = (!is_null($name)) ? $name: get_class($object);
$name = strtolower($name);
$return = null;
if (isset(self::$_store[$name])) {
// Store the old object for returning
$return = self::$_store[$name];
}
self::$_store[$name]= $object;
return $return;
}
static public function get($name)
{
if (!self::contains($name)) {
throw new Exception("Object does not exist in registry");
}
return self::$_store[$name];
}
static public function contains($name)
{
if (!isset(self::$_store[$name])) {
return false;
}
return true;
}
static public function remove($name)
{
if (self::contains($name)) {
unset(self::$_store[$name]);
}
}
}
I know, Registry could be a Singleton, so you never have two Registry at the same time (who needs them someone could think, but who knows).
Anyway the externally way of storing/accessing instances is like this:
$read = new DBReadConnection;
Registry::set($read);
$write = new DBWriteConnection;
Registry::set($write);
// To get the instances, anywhere in the code:
$read = Registry::get('DbReadConnection');
$write = Registry::get('DbWriteConnection');
And internally, inside the class (taken from the book) when getInstance is called:
abstract class DBConnection extends PDO {
static public function getInstance($name = null)
{
// Get the late-static-binding version of __CLASS__
$class = get_called_class();
// Allow passing in a name to get multiple instances
// If you do not pass a name, it functions as a singleton
$name = (!is_null($name)) ?: $class;
if (!Registry::contains($name)) {
$instance = new $class();
Registry::set($instance, $name);
}
return Registry::get($name);
}
}
class DBWriteConnection extends DBConnection {
public function __construct()
{
parent::__construct(APP_DB_WRITE_DSN, APP_DB_WRITE_USER, APP_DB_WRITE_PASSWORD);
} }
class DBReadConnection extends DBConnection {
public function __construct()
{
parent::__construct(APP_DB_READ_DSN, APP_DB_READ_USER,APP_DB_READ_PASSWORD);
}
}
Apparently referring to the registry indirectly (second case) seems more scalable for me, but what if some day I would need to change the registry and use another implementation, I would need to change that calls to Registry::get() and Registry::set() inside the getInstance() method in order to suit the changes or is there a smarter way?
Did someone of you came across this problem and found an easy way to interchange different registries depending on the type of application on the complexity etc.?
Should be a configuration class the solution? Or is there a smarter way to achieve a scalable registry pattern if it is possible?
Thanks for the attention! Hope for some help!
First of all. It's great that you spotted the problem of your approach by yourself. By using a registry you are tight coupling your classes to the registry where you pull your dependencies from. Not only that, but if your classes have to care about how they are stored in the registry and get grabbed from it (in your case every class would also implement a singleton), you also violate the Single-Responsibility-Principle.
As a rule of thumb keep in mind: Accessing objects globally from within a class from whatever storage will lead to tight coupling between the class and the storage.
Let's see what Martin Fowler has to say about this topic:
The key difference is that with a Service Locator every user of a service has a dependency to the locator. The locator can hide dependencies to other implementations, but you do need to see the locator. So the decision between locator and injector depends on whether that dependency is a problem.
and
With the service locator you have to search the source code for calls to the locator. Modern IDEs with a find references feature make this easier, but it's still not as easy as looking at the constructor or setting methods.
So you see it depends on what you are building. If you have a small app with a low amount of dependencies, to hell with it, go on with using a registry (But you absolutely should drop a classes behavior to store itself into or getting grabbed from the registry). If that's not the case and you are building complex services and want a clean and straightforward API define your dependencies explicitly by using Type Hints and Constructor Injection.
<?php
class DbConsumer {
protected $dbReadConnection;
protected $dbWriteConnection;
public function __construct(DBReadConnection $dbReadConnection, DBWriteConnection $dbWriteConnection)
{
$this->dbReadConnection = $dbReadConnection;
$this->dbWriteConnection = $dbWriteConnection;
}
}
// You can still use service location for example to grab instances
// but you will not pollute your classes itself by making use of it
// directly. Instead we'll grab instances from it and pass them into
// the consuming class
// [...]
$read = $registry->get('dbReadConnection');
$write = $registry->get('dbWriteConnection');
$dbConsumer = new DbConsumer($read, $write);
Should be a configuration class the solution? Or is there a smarter way to achieve a scalable registry pattern if it is possible?
That approach is encountered very often and you maybe have heard something about a DI-Container. Fabien Potencier writes the following:
A Dependency Injection Container is an object that knows how to instantiate and configure objects. And to be able to do its job, it needs to knows about the constructor arguments and the relationships between the objects.
The boundaries between a service locator and a DI-Container seem to be pretty blurry but I like the concept to think about it like that: A Service Locator hides the dependencies of a class while a DI-Container does not (which comes along with the benefit of easy unit testing).
So you see, there is no final answer and it depends on what you are building. I can suggest to dig more into the topic since how dependencies are managed is a core concern of every application.
Further Reading
Why Registry Pattern is antipattern. And what is alternative for it.
Service Locator is an Anti-Pattern
Do you need a Dependency Injection Container?

PHP Dependency Injection

I'm trying to get my head around Dependency Injection and I understand it, for the most part.
However, say if, for some reason, one of my classes was dependent on several classes, instead of passing all of these to this one class in the constructor, is there a better, more sensible method?
I've heard about DI Containers, is this how I would go about solving this problem? Where should I start with this solution? Do I pass the dependencies to my DIC, and then pass this to the class that needs these dependencies?
Any help that would point me in the right direction would be fantastic.
Dependency Injection !== DIC
People should really stop confusing them. Dependency Injection is idea that comes from Dependency Inversion principle.
The DIC is "magic cure", which promises to let you use dependency injection, but in PHP is usually implemented by breaking every other principle of object oriented programming. The worst implementations tend to also attach it all to global state, via static Registry or Singleton.
Anyway, if your class depends on too many other classes, then in general , it signifies a design flaw in the class itself. You basically have a class with too many reasons to change, thus, breaking the Single Responsibility principle.
In this case, then dependency injection container will only hide the underlaying design issues.
If you want to learn more about Dependency Injection, i would recommend for you to watch the "Clean Code Talks" on youtube:
The Clean Code Talks - Don't Look For Things!
The Clean Code Talks - "Global State and Singletons"
If you have several dependencies to deal with, then yes a DI container can be the solution.
The DI container can be an object or array constructed of the various dependent object you need, which gets passed to the constructor and unpacked.
Suppose you needed a config object, a database connection, and a client info object passed to each of your classes. You can create an array which holds them:
// Assume each is created or accessed as a singleton, however needed...
// This may be created globally at the top of your script, and passed into each newly
// instantiated class
$di_container = array(
'config' = new Config(),
'db' = new DB($user, $pass, $db, $whatever),
'client' = new ClientInfo($clientid)
);
And your class constructors accept the DI container as a parameter:
class SomeClass {
private $config;
private $db;
private $client;
public function __construct(&$di_container) {
$this->config = $di_container['config'];
$this->db = $di_container['db'];
$this->client = $di_container['client'];
}
}
Instead of an array as I did above (which is simple), you might also create the DI container as an class itself and instantiate it with the component classes injected into it individually. One benefit to using an object instead of an array is that by default it will be passed by reference into the classes using it, while an array is passed by value (though objects inside the array are still references).
Edit
There are some ways in which an object is more flexible than an array, although more complicated to code initially.
The container object may also create/instantiate the contained classes in its constructor as well (rather than creating them outside and passing them in). This can save you some coding on each script that uses it, as you only need to instantiate one object (which itself instantiates several others).
Class DIContainer {
public $config;
public $db;
public $client;
// The DI container can build its own member objects
public function __construct($params....) {
$this->config = new Config();
// These vars might be passed in the constructor, or could be constants, or something else
$this->db = new DB($user, $pass, $db, $whatever);
// Same here - the var may come from the constructor, $_SESSION, or somewhere else
$this->client = new ClientInfo($clientid);
}
}
I've wrote an article about this problem.
The ideea is to use a combination of abstract factory and dependency injection to achieve transparent dependency resolving of (possible nested) dependencies. I will copy/paste here the main code snippets:
namespace Gica\Interfaces\Dependency;
interface AbstractFactory
{
public function createObject($objectClass, $constructorArguments = []);
}
The abstract factory implementation is:
namespace Gica\Dependency;
class AbstractFactory implements \Gica\Interfaces\Dependency\AbstractFactory, \Gica\Interfaces\Dependency\WithDependencyInjector
{
use WithDependencyInjector;
/**
* #param string $objectClass
* #param array $constructorArguments
* #return object instanceof $class
*/
public function createObject($objectClass, $constructorArguments = [])
{
$instance = new $objectClass(...$constructorArguments);
$this->getDependencyInjector()->resolveDependencies($instance);
return $instance;
}
}
The dependency injector is this:
namespace Gica\Dependency;
class DependencyInjector implements \Gica\Interfaces\Dependency\DependencyInjector
{
use \Gica\Traits\WithDependencyContainer;
public function resolveDependencies($instance)
{
$sm = $this->getDependencyInjectionContainer();
if ($instance instanceof \Gica\Interfaces\WithAuthenticator) {
$instance->setAuthenticator($sm->get(\Gica\Interfaces\Authentication\Authenticator::class));
}
if ($instance instanceof \Gica\Interfaces\WithPdo) {
$instance->setPdo($sm->get(\Gica\SqlQuery\Connection::class));
}
if ($instance instanceof \Gica\Interfaces\Dependency\WithAbstractFactory) {
$instance->setAbstractFactory($sm->get(\Gica\Interfaces\Dependency\AbstractFactory::class));
}
//... all the dependency declaring interfaces go below
}
}
The dependency container is the standard one.
The client code could look something like this:
$abstractFactory = $container->get(\Gica\Interfaces\Dependency\AbstractFactory::class);
$someHelper = $abstractFactory->createObject(\Web\Helper\SomeHelper::class);
echo $someHelper->helpAction();
Notice that dependencies are hidden, and we can focus on the main bussiness. My client code doesn't care or know that $someHelper need an Authenticator or that helpAction need an SomeObject to do its work;
In the background a lot of things happen, a lot of dependencies are detected, resolved and injected.
Notice that I don't use the new operator to create $someObject. The responsability of actual creation of the object is passed to the AbstractFactory
P.S. Gica is my nickname :)
I recommend you to use Singltones or Mutlitones. In these cases you will be always able to get objects via static class' methods.
The other way (couldn't find a correct pattern name, but it could be Registry) is to use one global static object to store multiple objects' instances. E.g. (simplified code, without any checks):
class Registry {
private static $instances = array();
public static function add($k, $v) {
$this->instances[$k] = $v;
}
public static function get($k) {
return $this->instances[$k];
}
}
class MyClass {
public function __construct() {
Registry::add('myclass', $this);
}
}

Is it good practice to have DI container replace a global $registry object?

I have started refactoring a small application to use a small DI container instead of having
$registry::getstuff(); calls in my classes I inject them in a container.
This has raised 2 questions,
Q1 -> I extend Pimple DI class and create a container with dependencies specific to each object that will need DI. I then feed the object the whole shebang, and decrontruct it it in the constructor assigning the DI's objects to the class properties of the object I'm building.
Should I be separating the object in the new object() call? I just found it easier like this but seeing I'm a one man team right now I just want to confirm I have proper methodology.
Q2 -> I find the $registry object I was passing around all over will be uneeded if I do this on a few of the main classes, is this a normal result of using DI, no more registry? I may have a singleton or two injected in the container but it looks as that is all I will need and even those could easily be eliminitated since the DI has a share() property that returns the same instance of the object, effectively removing the need for singletons. Is this the way to rid an app of needing registry/singletons, because if it is it's darn easy like this.
Q2 :
If you were passing around all over your $registry object.... then your Registry was not really what is called a Registry (as Fowler described it).
A Registry is more or less a global object (a "well-known") with get/set methods.
In PHP, two common prototypes for the implementations of a Registry would be
As a singleton
class RegistryAsSingleton
{
public static function getInstance (){
//the singleton part
}
public function getStuff ()
{
//some stuff accessed thanks to the registry
}
}
With static methods all over the place
class RegistryAsStatic
{
public static function getStuff()
{
}
}
Passing your Registry all over the place makes it, well, just an object: a container with no greater purpose than providing references to other objects.
Your DI container (using Pimple as you suggested in your OP) is kind of a Registry itself: It IS well known and enables you to get components from anywhere.
So yes, we can say that your DI container will remove the requirement and necessity of a registry by performing the same functionality.
BUT (there's always a but)
Registry are always guilty until
proven innocent
(Martin Fowler)
If you're using your DI Container to replace your Registry, this is probably wrong.
eg:
//probably a Wrong usage of Registry
class NeedsRegistry
{
public function asAParameter(Registry $pRegistry)
{
//Wrong dependency on registry where dependency is on Connection
$ct = $pRegistry->getConnection();
}
public function asDirectAccess ()
{
//same mistake, more obvious as we can't use another component
$ct = Registry::getInstance()->getConnection();
}
}
//probably a wrong replacement for Registry using DI Container
class NeedsContainer
{
public function asAParameter(Container $pRegistry)
{
//We are dependent to the container with no needs,
//this code should be dependent on Connection
$ct = $pContainer->getConnection();
}
public function asDirectAccess ()
{
//should not be dependent on container
$ct = Container::getInstance()->getConnection();
}
}
Why is this bad? Because your code is not less dependent than before, it still depends on a component (either a registry or a container) which does not provides a clear goal (we may think of interface here)
The Registry-pattern be useful in some cases because it's a simple and fairly inexpensive way to define components or data (e.g. global configuration).
A way to refactor the above example without relying on DI by removing the dependency would be:
class WasNeedingARegistry
{
public function asAParameter (Connection $pConnection)
{
$pConnection->doStuff();//The real dependency here, we don't care for
//a global registry
}
}
//the client code would be like
$wasNeedingARegistry = new WasNeedingARegistry();
$wasNeedingARegistry->setConnection($connection);
Of course, this may not be possible if the client code isn't aware of the connection, which is probably the reason why you probably ended using a Registry in the first place.
Now DI comes into play
Using DI makes our life better because it will handle the dependency and enables us to access the dependency in a ready-to-use state.
Somewhere in your code, you'll configure your components:
$container['connection'] = function ($container) {
return new Connection('configuration');
};
$container['neededARegistry'] = function ($container) {
$neededARegistry = new NeededARegistry();
$neededARegistry->setConnection($container['connection']);
return $neededARegistry;
};
Now you have everything you need to refactor your code:
// probably a better design pattern for using a Registry
class NeededARegistry
{
public function setConnection(Connection $pConnection)
{
$this->connection = $pConnection;
return $this;
}
public function previouslyAsDirectAccess ()
{
$this->connection->doStuff();
}
}
//and the client code just needs to know about the DI container
$container['neededARegistry']->previouslyAsDirectAccess();
The "client" code should be as isolated as possible. The client should be responsible for and inject its own dependencies (via set- methods). The client should not be responsible for handling its dependencies's dependencies.
class WrongClientCode
{
private $connection;
public function setConnection(Connection $pConnection)
{
$this->connection = $pConnection;
}
public function callService ()
{
//for the demo we use a factory here
ServiceFactory::create('SomeId')
->setConnection($this->connection)
->call();
//here, connection was propagated on the solely
// purpose of being passed to the Service
}
}
class GoodClientCode
{
private $service;
public function setService(Service $pService)
{
//the only dependency is on Service, no more connection
$this->service = $pService;
}
public function callService ()
{
$this->service->setConnection($this->connection)
->call();
}
}
The DI container will configure GoodClientCode with the Service that has already been properly configured with its Connection
As for the Singleton aspect, yes, it will enable you to get rid of them.
Hope this helps

Is this a sane implementation of constructor injection?

Following on from my question on service locators I have decided to use constructor injection instead. Please consider the following code:
<?php
interface IAppServiceRegistry {
public function getDb();
public function getLogger();
}
interface IFooServiceRegistry extends IAppServiceRegistry {
public function getFooBarBazModel();
}
class AppServiceRegistry
implements IAppServiceRegistry, IFooServiceRegistry
{
private $logger;
private $db;
private $fooBarBazModel;
public function getDb() {
// return db (instantiate if first call)
}
public function getLogger() {
// return logger (instantiate if first call)
}
public function getFooBarBazModel() {
if (!isset($this->fooBarBazModel)) {
$this->fooBarBazModel = new FooBarBazModel( $this->getDb() );
}
return $this->fooBarBazModel;
}
}
// Example client classes:
/**
* Depends on db, logger and foomodel.
*/
class Foo {
private $db;
private $logger;
private $fooModel;
public function __construct(IFooServiceRegistry $services) {
$this->db = $services->getDb();
$this->logger = $services->getLogger();
$this->fooModel = $services->getFooModel();
}
}
/**
* Depends on only db and logger.
*/
class BarBaz {
private $db;
private $logger;
public function __construct(IAppServiceRegistry $services) {
$this->db = $services->getDb();
$this->logger = $services->getLogger();
}
}
Then I would add new service factory methods to the registry as the application evolved, creating segregated interfaces wherever logically appropriate.
Is this approach sane?
Although I don't normally read php, I think I understand most of it. Technically, it looks fine, but then you write
I would add new service factory methods to the registry as the application evolved
That tends to hurt the idea of loose coupling because instead of a general-purpose Service Locator you now have a specialized Service Locator.
Injecting such a serivce registry into each class works against the Single Responsibility Principle (SRP) because once a class has access to the registry, it is too easy to request yet another dependency than originally conceived, and you will end up with a God Object.
It would be better to inject the required dependencies directly into the classes that need them. Thus, your Foo class' constructor should take a db, a logger and a fooModel, while the BarBaz class should take only a db and a logger parameter.
The next question may then be: What if I need a lot of different dependencies to perform the work? That would require a truly ugly constructor with lots of parameters, and this goes against other well-known OO practices.
True, but if you need lots of dependencies, you are probably violating the SRP and should try to split your design into finer-grained objects :)
This implementation is almost the same as the service locator you previously showed us.
A good question to ask is whether upon looking at the class of your objects, you know everything about the objects needs to get their job done. In your case you still don't know.
If Foo needs the db, logger and model, you make this clear by asking for these in the constructor.
Here's a good read on the matter:
http://misko.hevery.com/code-reviewers-guide/flaw-digging-into-collaborators/
I've been grappling with this kind of problem lately. Service Locator vs Depedency Injection.
I agree with Mark that the way to go is to inject individual fine-grained objects into the constructor, as required. The only drawback, as Mark has highlighted, is that when you build a complex object graph, you inevitably have to start somewhere. This means that your high-level objects will have lots of services (objects) injected into them.
The easy way round this is to use something to do the hard work for you. The prime example of this is Google's Guice which seems to me a very good way of going about things. Sadly it's written for Java! There are PHP versions about; I'm not sure any of them quite measure up to Guice at this point.
I've written a post on this topic which goes into more detail; which you may find interesting. It includes a simple implementation of a dependency injection framework.
The bottom line is that if you've got a class Foo with a number of requirements, you can create your class as such:
/**
* Depends on db, logger and foomodel.
*/
class Foo
{
private $db;
private $logger;
private $fooModel;
/**
* (other documentation here)
* #inject
*/
public function __construct(IDbService $db, ILoggerService $logger, $iModelService $model)
{
// do something
}
}
When you want a new Foo object, you simply ask the dependency injection framework to create you one:
$foo = $serviceInjector->getInstance('Foo');
The dependency injector will do the hard work, making sure it injects the dependencies. This includes any dependencies of the dependencies, if that makes sense. In other words it will sort it all out recursively up the tree.
Later, when you find you need an IBarService object, you can just add it to the Construtor, without altering any other code!

Categories