DI - How to refactor constructor with lots of dependencies - php

I'm fairly new to dependency injection, and I've come upon a situation where I have a class which is basically just functional programming -- one method in it (createPayment) requires 9 dependencies, and the other method (paypalApiEndpoint) only requires 1 of those dependencies:
class Paypal
{
private $restAPIClient;
private $db;
private $logger;
private $paypalGateway;
private $paymentGateway;
private $ordersGateway;
private $usersGateway;
private $resourcesGateway;
private $configGateway;
public function __constructor($restAPIClient, $db, $logger, // ...6 more, each for the private vars)
{
$this->restAPIClient = $restAPIClient;
$this->db= $db;
$this->logger= $logger;
// ... 6 more
}
// this method uses all 9 dependencies from the constructor
public function createPaypalPayment()
{
// insert purchase info into `payment` db table. Requires $paymentGateway and $db
// make paypal API call. Requires $restAPIClient, $db, $paypalGateway, $logger
// based on API call results, update `orders`, `users`, `payment`, `resources`, and `config` db tables. Requires $db, $logger, $paymentGateway, $ordersGateway, $usersGateway, $resourcesGateway, $configGateway
}
// this method only requires 1 of the dependencies, $paypalGateway
public function paypalApiEndpoint()
{
// get a value from our db table `paypal`, and return it. Requires $paypalGateway
}
}
In many places throughout my codebase I only need to use the paypalApiEndpoint method. The problem is, in order to use that method I have to first create 9 objects (the dependencies of class Paypal), then I can use those to create the Paypal object, and then finally I can use the simple paypalApiEndpoint method. Needing to create 10 objects in order to this very simple method seems overboard.
Is there a better way?

// this method only requires 1 of the dependencies, $paypalGateway
public function paypalApiEndpoint()
{
// get a value from our db table `paypal`, and return it. Requires $paypalGateway
}
If the only requirement is the paypalGateway, then move the implementation to the paypalGateway class or an extension.
That doesn't mean you would have to remove the method from the Paypal class:
public function paypalApiEndpoint()
{
return $this->paypalGateway->paypalApiEndpoint();
}
Further, your constructor has lots of parameters.
I believe it would be better to have a Builder class with all the dependencies, using setters, that would be injected as an argument:
https://jlordiales.me/2012/12/13/the-builder-pattern-in-practice/
Or implement composites of related data reducing the parameters number:
https://refactoring.guru/smells/data-clumps
Taking into account that the Paypal constructor has lots of parameters:
https://refactoring.guru/smells/long-parameter-list
If there are several unrelated data elements, sometimes you can merge
them into a single parameter object via Introduce Parameter Object.
https://refactoring.guru/introduce-parameter-object
By consolidating parameters in a single class, you can also move the
methods for handling this data there as well, freeing the other
methods from this code.
(...) see whether there is any point in moving a part of the method
(or sometimes even the whole method) to a parameter object class. If
so, use Move Method or Extract Method.
This means that if you only need to invoke paypalApiEndpoint, you may use only the paypalGateway class for your purposes, with less dependancies than the Paypal class.

You don't have to inject all your dependencies in the constructor. You can have setter injectors for optional parameters.
public function setRestAPIClient($restAPIClient) {
$this->restAPIClient = $restAPIClient;
};
In your example, paypalApiEndpoint is a good candidate to be turned into either a class constant and/or a static method that would then allow you to use it via:
class PayPal {
private static $paypalGateway = null;
public static setPaypalGateway($paypalGateway) {
self::paypalGateway = $paypalGateway;
}
public __construct(.... all params) {
self::paypalGateway = $paypalGateway;
$this->otherDependency = $otherDependency;
}
}
For static use you just have to call the static setter 1 time, then use as much as you want within script execution.
Paypal::setPaypalGateway($ppgateway);
$endpoint = Paypal::paypalApiEndpoint();
Last but not least, you might want to look into use of a DI Container Component.
Some DIC's now feature autowiring which can determine what they need to load at calltime using typehints.
There is a standard PSR standard for how DI Containers should work and Symfony2's DIC, Laravel's DIC and PHP-DI for example, all conform to those standards and can easily be used to handle class loading within your application.
As mentioned, PHP-DI is another DIC component you might want to look at as well.

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?

Dependency Injection: pulling required components when they are actually needed

The gist behind DI is to relieve a class from creating and preparing objects it depends on and pushing them in. This sounds very reasonable, but sometimes a class does not need all the objects, that are being pushed into it to carry out its function. The reason behind this is an "early return" that happens upon invalid user input or an exception thrown by one of the required objects earlier or the unavailability of a certain value necessary to instantiate an object until a block of code runs.
More practical examples:
injecting a database connection object that will never be used, because the user data does not pass validation (provided that no triggers are used to validate this data)
injecting excel-like objects (PHPExcel e.g.) that collect input (heavy to load and instantiate because a whole library is pulled in and never used, because validation throws an exception earlier than a write occurs)
a variable value that is determined within a class, but not the injector at runtime; for instance, a routing component that determines the controller (or command) class and method that should be called based on user input
although this might be a design problem, but a substantial service-class, that depends on a lot of components, but uses only like 1/3 of them per request (the reason, why i tend to use command classes instead of controllers)
So, in a way pushing in all necessary components contradicts "lazy-loading" in the way that some components are created and never used, that being a bit unpractical and impacting performance. As far as PHP is concerned - more files are loaded, parsed and compiled. This is especially painful, if the objects being pushed in have their own dependencies.
i see 3 ways around it, 2 of which don't sound very well:
injecting a factory
injecting the injector (an anti-pattern)
injecting some external function, that gets called from within the
class once a relevant point is reached (smtg like "retrieve a
PHPExcel instance once data validation finished"); this is what i
tend to use due to its flexibility
The question is what's the best way of dealing with such situations / what do you guys use?
UPDATE:
#GordonM here are the examples of 3 approaches:
//inject factory example
interface IFactory{
function factory();
}
class Bartender{
protected $_factory;
public function __construct(IFactory $f){
$this->_factory = $f;
}
public function order($data){
//validating $data
//... return or throw exception
//validation passed, order must be saved
$db = $this->_factory->factory(); //! factory instance * num necessary components
$db->insert('orders', $data);
//...
}
}
/*
inject provider example
assuming that the provider prepares necessary objects
(i.e. injects their dependencies as well)
*/
interface IProvider{
function get($uid);
}
class Router{
protected $_provider;
public function __construct(IProvider $p){
$this->_provider = $p;
}
public function route($str){
//... match $str against routes to resolve class and method
$inst = $this->_provider->get($class);
//...
}
}
//inject callback (old fashion way)
class MyProvider{
protected $_db;
public function getDb(){
$this->_db = $this->_db ? $this->_db : new mysqli();
return $this->_db;
}
}
class Bartender{
protected $_db;
public function __construct(array $callback){
$this->_db = $callback;
}
public function order($data){
//validating $data
//... return or throw exception
//validation passed, order must be saved
$db = call_user_func_array($this->_db, array());
$db->insert('orders', $data);
//...
}
}
//the way it works under the hood:
$provider = new MyProvider();
$db = array($provider, 'getDb');
new Bartender($db);
//inject callback (the PHP 5.3 way)
class Bartender{
protected $_db;
public function __construct(Closure $callback){
$this->_db = $callback;
}
public function order($data){
//validating $data
//... return or throw exception
//validation passed, order must be saved
$db = call_user_func_array($this->_db, array());
$db->insert('orders', $data);
//...
}
}
//the way it works under the hood:
static $conn = null;
$db = function() use ($conn){
$conn = $conn ? $conn : new mysqli();
return $conn;
};
new Bartender($db);
I've been thinking about this problem a lot lately in planning of a major project that I want to do as right as humanly possible (stick to LoD, no hard coded dependencies, etc). My first thought was the "Inject a factory" approach as well, but I'm not sure that's the way to go. The Clean Code talks from Google made the claim that if you reach through an object to get the object you really want then you're violating the LoD. That would seem to rule out the idea of injecting a factory, because you have to reach through the factory to get what you really want. Maybe I've missed some point there that makes it okay, but until I know for sure I'm pondering other approaches.
How do you do the function injection? I'd imagine you're passing in a callback that does the instantiation of the object you want, but a code example would be nice.
If you could update your question with code examples of how you do the three styles you mentioned it might be useful. I'm especially keen to see "injecting the injector" even if it is an antipattern.
One idea that did occur was that of a proxy object. It implements the same interface(s) as the actual object you want to pass in, but instead of implementing anything it just holds an instance of the real class and forwards method calls on to it.
interface MyInterface
{
public function doFoo ();
public function isFoo ();
// etc
}
class RealClass implements MyInterface
{
public function doFoo ()
{
return ('Foo!');
}
public function isFoo ()
{
return ($this -> doFoo () == 'Foo!'? true: false);
}
// etc
}
class RealClassProxy implements MyInterface
{
private $instance = NULL;
/**
* Do lazy instantiation of the real class
*
* #return RealClass
*/
private function getRealClass ()
{
if ($this -> instance === NULL)
{
$this -> instance = new RealClass ();
}
return $this -> instance;
}
public function doFoo ()
{
return $this -> getRealClass () -> doFoo ();
}
public function isFoo ()
{
return $this -> getRealClass () -> isFoo ();
}
// etc
}
Because the proxy has the same interface as the real class, you can pass it as an argument to any function/method that type hints for the interface. The Liskov Substitution Principle holds for the proxy because it responds to all the same messages as the real class and returns the same results (the interface enforces this, at least for method signitures). However, the real class doesn't get instantiated unless a message actually gets sent to the proxy, which does lazy instantiation of the real class behind the scenes.
function sendMessageToRealClass (MyInterface $instance)
{
$instance -> doFoo ();
}
sendMessageToRealClass (new RealClass ());
sendMessageToRealClass (new RealClassProxy ());
There is an extra layer of indirection involved with the proxy object, which obviously means that there is a small performance hit for every method call you make. However, it does allow you to do lazy instantiation, so you can avoid instantiating classes you don't need. Whether this is worth it depends on the cost of instantiating the real object versus the cost of the extra layer of indirection.
EDIT: I had originally written this answer with the idea of subclassing the real object so you could use the technique with objects that don't implement any interfaces such as PDO. I had originally thought that interfaces were the correct way to do this but I wanted an approach that didn't rely on the class being tied to an interface. On reflection that was a big mistake so I've updated the answer to reflect what I should have done in the first place. This version does mean, however, that you can't directly apply this technique to classes with no associated interface. You'll have to wrap such classes in another class that does provide an interface for the proxy approach to be viable, meaning yet another layer of indirection.
If you want to implement lazy loading you basically have two way to do it (as you have already written in the topic):
instead of injecting an instance of object you might need, you inject a Factory or a Builder. The difference between them is that instance of Builder is made for returning one type of object (maybe with different setups), while Factory makes different types of instances ( with same lifetime and/or implementing same interface ).
utilize anonymous function which will return you an instance. That would look something like this:
$provider = function() {
return new \PDO('sqlite::memory:');
};
Only when you call this anonymous function, the instance of PDO is created and connection to database established.
What I usually do in my code is combine both. You can equip the Factory with such provider. This, for example, lets you have a single connection for all the objects which where created by said factory, and the connection is create only, when you first time ask an instance from Factory.
The other way to combine both methods (which i have not used, though) would be to create full blow Provider class, which in constructor accepts an anonymous function. Then the factory could pass around this same instance of Provider and the expensive object (PHPExcel, Doctrine, SwiftMailer or some other instance) is only created once a Product from that Factory first time turns to the Provider (couldn't come up with better name to describe all objects created by same factory) and requests it. After that, this expensive object is shared between all Products of Factory.
... my 2 cents
I chose lazy-injection (i.e. injecting a Proxy class):
class Class1 {
/**
* #Inject(lazy=true)
* #var Class2
*/
private $class2;
public function doSomething() {
// The dependency is loaded NOW
return $this->class2->getSomethingElse();
}
Here, the dependency (class2) is not injected directly: a proxy class is injected. Only when the proxy class is used that the dependency is loaded.
This is possible in PHP-DI (dependency injection framework).
Disclaimer: I work in this project

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);
}
}

Construction of a singleton class

An ethical question here.
I'm planning on using several manager classes in my new project that will be performing various tasks across the whole project. These classes are singletons, but require construction based on parameters.
As to when/where this construction has to happen, I have mixed feelings. I have these options so far:
Option A
It's easy to just pass these parameters to the getInstance method while having a default null value. On the very first call the parameters will be used, and any additional calls completely ignore them.
While this works, doing so feels rather unlogical, for the following reasons:
It makes documentation unclear. getInstance' first parameter must be of type Collection, but can be null... what's going on here?
You can argue that writing a line about this in the description will clear it up, but I'd prefer clarification to be unneccesary.
It feels faulty to pass getInstance any construction parameters. This is due to the fact that the method name does not explicity hint towards construction, making it unclear it will happen.
Option B
I'm thinking about a setup method. This method takes all parameters, calls the class constructor, and changes the internal class state to initialized.
When calling the getInstance method prior to setup, it will throw a NotInitializedException. After setup has been called, any additional calls to setup will result in a PreviouslyInitializedException.
After setup has been called, getInstance becomes available.
Personally, this option appeals more to me. But it feels excessive.
What option do you prefer? And why?
I would probably try and ditch the singleton approach and pass manager classes around to whatever needs them.
$manager = new Manager( $collection, $var, $var2 );
$other_class = New OtherClass( $manager );
//or
$other_class = New OtherClass;
$other_class->manager = $manager;
//or
$other_class = New OtherClass;
$other_class->setManager( $manager );
Use dependency injection to pass the Manager object around. Don't use Singleton pattern. It's a common consensus that using it creates a global state and makes your API deceptive.
PHP Global in functions (jump to answer)
Singletons are pathological liars
Inject the Manager instance to any class that needs it via the constructor. Each class should not try to instantiate Manager by themselves, the only way the classes get an instance of the Manager is by getting it from constructor.
class NeedsManager
{
protected $manager;
public function __construct(Manager $manager)
{
$this->manager = $manager;
}
}
You don't need to enforce one instance of Manager. Just don't instantiate it more than once. If all of your classes that need an instance of Manager get what they need from the constructor and never tries to instantiate it on their own, it will assure that there's just going to be one instance in your application.
How about option 3. If they are true singletons, set up properties files for their parameters for use with a no-arg getInstance.
If that doesn't fit, you might be misusing the singleton pattern.
You are looking at using a Factory design pattern. Factories are objects that act as fancy constructors for other objects. In your case, you will move setup and getInstance to the factory. The wiki article's pretty good- http://en.wikipedia.org/wiki/Factory_method_pattern
class SingletonFoo {
//properties, etc
static $singleton = NULL;
private function __constructor(){}
static function getInstance(){
if(NULL === self::$singleton) {
self::$singleton = new SingletonFoo();
}
return self::$singleton;
}
}
class FooFactory {
static $SingletonFoo = null;
static function setup($args){
if( !(NULL === self::$SingletonFoo)){
throw new AlreadyInstantiatedException();
}
self::$SingletonFoo = SingletonFoo::getInstance();
//Do stuff with $args to build SingletonFoo
return self::$SingletonFoo;
}
static function getInstance(){
if(NULL === self::$SingletonFoo) {
throw new NotInstantiatedException();
}
return self::$SingletonFoo;
}
}
Don't use Singleton, use Resources Manager (or Service Container, or DI Container):
class ResourceManager
{
protected static $resource;
public static function setResource($resource)
{
if (!empty(self::$resource)) //resource should not be overwritten
{
if ($resource!=self::$resource) return false;
else return true;
}
self::$resource = $resource;
return true;
}
public static function getResource()
{
return self::$resource;
}
}
Resource Manager allows you to set any custom classes for unit-testing (like dependency injection), you can just get needed resources without requesting them in constructor (I like DI, but sometimes it's just more handy to use empty constructors).
Ready-to-use variant: http://symfony.com/doc/current/book/service_container.html (I don't like to move logic from code to configs, but in stand-alone module it looks acceptable).

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