SitePoint Sponsor

User Tag List

Page 1 of 3 123 LastLast
Results 1 to 25 of 66
  1. #1
    SitePoint Zealot
    Join Date
    Jul 2005
    Posts
    194
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Independecy Injection and Service Descriptors

    Could anyone on this forum be so kind and explain what the two terms: Independecy Injection and Service Descriptors exactly are? I didn't fully grasp the paper from Martin Fowler about this topic.

  2. #2
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    That's Dependency Injection. Not Independency.
    You probably mean Service Locator (not Descriptor) aswell. Service Locator is an alternative to Dependency Injection. It's closely related to the Registry pattern. (Edit: Probably, it's even better related to the Factory patterns. The ServiceLocator is really just a collection of factories.)

    In short ; Each time you use the new statement to create an instance of an object, you introduce dependency. The solution to this problem (as it may be) is to use an Inversion of Control pattern. Dependency Injection and Service Locator are two different methods of applying Inversion of Control.

    Consider this :
    PHP Code:
    class Car
    {
        var 
    $engine;
        function 
    Car() {
            
    $this->engine = new Engine();
        }
        function 
    getEngine() {
            return 
    $this->engine;
        }

    In the above example, the class Car depends on the class Engine. That may be fine for this tiny example, but it makes your design less flexible. This is especially noticeable when you try to write unittests for the Car.

    This could be solved with a Service Locator by something like this :
    PHP Code:
    class ServiceLocator
    {
        function 
    createEngine() {
            return new 
    Engine();
        }
    }
    class 
    Car
    {
        var 
    $engine;
        function 
    Car($serviceLocator) {
            
    $this->engine $serviceLocator->createEngine();
        }
        function 
    getEngine() {
            return 
    $this->engine;
        }

    As you will notice, the dependency is now moved out of the Car class and into a seperate object (the ServiceLocator). The ServiceLocator may easily be substituted with a Mock, and thus you could test Car seperately from Engine.

    Dependency Injection is a different strategy for archiving the same thing. The simplest form of Dependency Injection is Constructor Injection. Consider this :
    PHP Code:
    class Car
    {
        var 
    $engine;
        function 
    Car(IEngine $engine) {
            
    $this->engine $engine;
        }
        function 
    getEngine() {
            return 
    $this->engine;
        }
    }

    class 
    Engine implements IEngine
    {
    }

    class 
    DependencyInjector
    {
        function 
    CreateInstance($class) {
            
    // ...
        
    }
    }

    $car DependencyInjector::CreateInstance('Car'); 
    In the function CreateInstance, the DependencyInjector will inspect the class Car thorugh reflection, and see that Car expects an instance of IEngine. It will know that the class Engine implements IEngine and thus it will create an instance of Engine and pass it to Car.
    The result is again that Car has no dependency on Engine - instead it has a dependency on the interface IEngine - and dependencies on interfaces are not a problem.
    Dependency Injection is a bit tricky, so don't be scared of if you don't grasp it completely at first. I know that I didn't.
    Last edited by kyberfabrikken; Jul 31, 2005 at 05:05.

  3. #3
    SitePoint Addict timvw's Avatar
    Join Date
    Jan 2005
    Location
    Belgium
    Posts
    354
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Imho there is nothing that can make dependency go away, but you can move the dependency a little around..

    - With the Service Locator you only move the dependency from Car to ServiceLocator. The problems, that there is a class that depends on another one still exists..

    - And also in the DependencyInjector the problem remains that there is a class depends on another one..

    - Even if you have code that instantiates an Engine, and then pass the reference to the constructor of the Car there will be a dependency. But in this case it is in your "Main" class.

    - How would you solve the following: A car without an engine is pretty useless, but an engine without a car is pretty useless too. So they both need an instance of eachother.

  4. #4
    SitePoint Zealot
    Join Date
    Jul 2005
    Posts
    194
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Well, using the ServiceLocator will of course make it possible only change one class instead of all kind of other spread over the system. Little time saver etc.

  5. #5
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Dependency Injection is a bit tricky, so don't be scared of if you don't grasp it completely at first. I know that I didn't.
    I didn't either, though the examples you've posted are excellent examples, much thanks for taking the time to post them Kyber

  6. #6
    SitePoint Zealot
    Join Date
    Jul 2005
    Posts
    194
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yes, they are nice and quite useful too Looks like I did quite nice with my service class, below code was my first ry before this posting. I suppose I need to alter it a bit

    PHP Code:
    class DefaultServiceRegistry implements IServiceRegistry {
        private static 
    $_instances;
        private static 
    $_instance// implementate it all as a singleton
        
        /**
         * Get services provided by the repository
         *
         * @return    list of provided services
         */
        
    public function getServices() {
            return array(
                         
    'DatabaseService',
                        
    'ContentService'
                     
    );
        }
        
        
    /**
         * is Service provided?
         *
         * @param     serviceName    name of the service to test provision of
         * @return    true => provided, false => not provided
         */
        
    public function isServiceProvided$serviceName ) {
            if ( !isset( 
    self::$_instances$serviceName ] ) ) {
                return 
    false;
            }
            
            return 
    true;
        }
        
        
    /**
         * is Service provided?
         *
         * @param     serviceName    name of the service to retrieve meta data from
         * @return    the service meta data
         */
        
    public function getServiceDescriptor$serviceName ) {
            if ( !isset( 
    $serviceName ) ) {
                throw new 
    IllegalParameterException"serviceName shouldn't be empty or null" );
            }
            
            
    $serviceDescriptor $serviceName 'Descriptor';
            return new 
    $serviceDescriptor();
        }
        
        
    /**
         * Get Service interface
         *
         * @param     serviceName    name of the service to retrieve
         * @return    the service interface
         */
        
    public function getService$serviceName ) {
            if ( !isset( 
    self::$_instances ) ) {
                
    self::$_instances = array();
            }
            
            if ( isset( 
    self::$_instances$serviceName ] ) ) {
                return 
    self::$_instances$serviceName ];
            }
            
            
    $serviceDescriptor self::getServiceDescriptor$serviceName );
            
    $serviceInterface $serviceDescriptor->getServiceImplementation();
            
            
    $instance =&  new $serviceInterface();
            
    self::$_instances$serviceName ]    =& $instance;
            return 
    $instance;
        }
        
        public function 
    getDatabaseService() {
            return 
    self::getService'DatabaseService' );
        }

        
    //
        
    public static function getInstance() {
            if ( !isset( 
    self::$_instance ) ) { 
                
    self::$_instance = new DefaultServiceRegistry() ; 
            } 
       
             return 
    self::$_instance;     
         }


  7. #7
    SitePoint Addict timvw's Avatar
    Join Date
    Jan 2005
    Location
    Belgium
    Posts
    354
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by wdeboer
    Well, using the ServiceLocator will of course make it possible only change one class instead of all kind of other spread over the system. Little time saver etc.
    I agree on the fact that passing a reference to a class (actually, the important thing is the interface it exposes) allows you to reduce the dependency.

    PHP Code:
    $engine = new Engine;
    $car = new Car($engine); 
    But if you want to change your ServiceLocator you still have to modify all the calls to the ServiceLocator constructor.. So the problem has only been moved..

    PHP Code:
    $servicelocator = new ServiceLocator();
    $car = new Car($servicelocator); 
    Why do i have a feeling that the ServiceLocator has been made redundant?

  8. #8
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by timvw
    - How would you solve the following: A car without an engine is pretty useless, but an engine without a car is pretty useless too. So they both need an instance of eachother.
    Well you can quite happily have an Engine without a Car, so maybe that's not such agood example? How about a Brother and a Sister? It doesn't make sense to have one without another there for sure...

    Brother <---> Sister

    This is a codependency, and a bad thing to have in an object ownership model (worse in a data model). Most cycles can be removed by adding an intermediate...

    Brother <--- Parent ---> Sister

    Now if you want to extract the relation of Siblingness you can do so, but you can create and destroy Brothers and Sisters without compication.

    yours, Marcus

    p.s. I wrote a demo of a DI tool:
    http://www.sitepoint.com/forums/showthread.php?t=256673

    As usual it's just a play thing. Check out the PHP port of Pico to see the job done properly.
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  9. #9
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by timvw
    Imho there is nothing that can make dependency go away, but you can move the dependency a little around..
    Well. It's really about isolating the dependency in order to make it interchangeable. In the first Car-example, there is no way to provide a MockEngine. The servicelocator makes thi spossible.

    Quote Originally Posted by timvw
    With the Service Locator you only move the dependency from Car to ServiceLocator. The problems, that there is a class that depends on another one still exists..
    You're right in asmuch as the ServiceLocator pattern makes you code depend on the a ServiceLocator, but it doesn't rely on the ServiceLocator.
    My example #2 might have been clearer like this :
    PHP Code:
    interface IServiceLocator
    {
        function 
    createEngine();
    }

    class 
    ServiceLocator implements IServiceLocator
    {
        function 
    createEngine() {
            return new 
    Engine();
        }
    }
    class 
    Car
    {
        var 
    $engine;
        function 
    Car(IServiceLocator $serviceLocator) {
            
    $this->engine $serviceLocator->createEngine();
        }
        function 
    getEngine() {
            return 
    $this->engine;
        }

    Quote Originally Posted by timvw
    And also in the DependencyInjector the problem remains that there is a class depends on another one..

    Even if you have code that instantiates an Engine, and then pass the reference to the constructor of the Car there will be a dependency. But in this case it is in your "Main" class.
    No. With Dependency Injection, the Car depends on an interface rather than a concrete class. That's the difference. It's subtle, but important.

    Quote Originally Posted by timvw
    How would you solve the following: A car without an engine is pretty useless, but an engine without a car is pretty useless too. So they both need an instance of eachother.
    That has nothing to do with the topic. But in that case, I would make the car have a constructor, which expects an engine. Using a ServiceLocator that get's troublesome, but with Dependency Injection you retain the option.

  10. #10
    SitePoint Addict timvw's Avatar
    Join Date
    Jan 2005
    Location
    Belgium
    Posts
    354
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken
    Well. It's really about isolating the dependency in order to make it interchangeable. In the first Car-example, there is no way to provide a MockEngine. The servicelocator makes thi spossible.
    But what happens if you want to test the ServiceLocator? You will meet the dependency again.

    Quote Originally Posted by kyberfabrikken
    You're right in asmuch as the ServiceLocator pattern makes you code depend on the a ServiceLocator, but it doesn't rely on the ServiceLocator.
    I prefer the code below (Instead of passing an object that has an interface capable of generating an object that implements the engine interface)

    PHP Code:
    $engine = new enginimplementation;
    $car = new car($engine); 
    Quote Originally Posted by kyberfabrikken
    No. With Dependency Injection, the Car depends on an interface rather than a concrete class. That's the difference. It's subtle, but important.
    If you pass an engine to a car, the car doesn't (at least it shouldn't) care about the concrete implementation (class) of the engine. All it cares about is the interface (the public methods) that are exposed by the engine class.

  11. #11
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by timvw
    But what happens if you want to test the ServiceLocator? You will meet the dependency again.
    The ServieLocator will depend on a concrete class, but it's the only class doing so. Besides - The test of ServiceLocator would simply be to check the type of the returned object.

    Perhaps a more realistic example, will shed some light on the testing-problem.
    Say you are making a script that takes an incomming request, and if it meets some criteria, it'll send an email to somewhere. The class would look somewhat like this :
    PHP Code:
    class FooController
    {
        var 
    $mailer;
        function 
    FooController() {
            
    $this->mailer = new Mailer();
        }
        function 
    execute(&$request) {
            if (
    $this->isValidEmail($request->get('email'))) {
                
    $this->mailer->sendMailTo($request->get('email'));
            }
        }

    Now - the above example is rather tricky to test, since feeding execute() with a Mock'ed request will actually send off a mail to somewhere, contributing to the worlds ammount of spam. A servicelocator would solve this, the following way :
    PHP Code:
    class FooController
    {
        var 
    $locator;
        function 
    FooController(&$serviceLocator) {
            
    $this->locator =& $serviceLocator;
        }
        function 
    execute(&$request) {
            if (
    $this->isValidEmail($request->get('email'))) {
                
    $mailer =& $this->locator->getMailer();
                
    $mailer->sendMailTo($request->get('email'));
            }
        }

    Much better now. We can now write a test of FooController by mocking ServiceLocator and Mailer.

    Quote Originally Posted by timvw
    I prefer the code below (Instead of passing an object that has an interface capable of generating an object that implements the engine interface)

    ...

    If you pass an engine to a car, the car doesn't (at least it shouldn't) care about the concrete implementation (class) of the engine. All it cares about is the interface (the public methods) that are exposed by the engine class.
    Yes. That'll solve the problem too. But in this case, you expect the user to actively pass an instance, which is a burden for him/her. If you can live with that (and you often can), you can do without DI.

  12. #12
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Off Topic:

    PHP Code:
    ... throw new IllegalParameterException( ... ) ... 


    I'm sure I've seen that somewhere...?? Another exceptional name for an exception that I now use is called ClassViolationException( ... ) if a class doesn't exist for example?

    Maybe I could start a thread on this subject.

  13. #13
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken
    In the first Car-example, there is no way to provide a MockEngine.
    I thought you explained DI/inversion of control very well above. If I can make a minor note about testing, if you move the engine instantiation into its own method you can partially mock the Car class, knocking out the _Engine() method below, and setting a return reference to a mock Engine object:

    PHP Code:
       class Car
       
    {
           var 
    $engine;
           function 
    Car() {
               
    $this->engine =& $this->_Engine();
           }
           function 
    getEngine() {
               return 
    $this->engine;
           }
           function &
    _Engine()
           {
               return new 
    Engine;
           }
       }
       
       
    Mock::generatePartial('Car''PartialCar', array('_Engine'));
       
    Mock::generate('Engine');
       
       function 
    testFoo
       
    {
           
    $engine =& new MockEngine($this);
           
    $car =& new PartialCar($this);
           
    $car->setReturnReference('_Engine'$engine);
           
    $car->Car();
           
    // etc
       

    The Engine dependency isn't necessarily a bad thing if there really is only one engine. In that case it's actually better to keep it locked up in the Car scope.

    However cars normally do have a choice of engines so, in this case, some kind of inversion control probably should be built in from the start.

  14. #14
    SitePoint Zealot
    Join Date
    Jul 2005
    Posts
    194
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Dr Livingston
    Off Topic:

    PHP Code:
    ... throw new IllegalParameterException( ... ) ... 

    Maybe I could start a thread on this subject.
    I think I stole that name from you, I liked it I also got some other exceptions names, you will see what my hobby is: UnexpectedGoofyException (goofy is now my POC-implementation of the PHP language based on VM), ScroogeRangeMismatchException (too much instances of a singleton ) etc.

  15. #15
    SitePoint Enthusiast
    Join Date
    Jul 2005
    Location
    United Kingdom
    Posts
    86
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Dr Livingston
    Off Topic:

    PHP Code:
    ... throw new IllegalParameterException( ... ) ... 


    I'm sure I've seen that somewhere...?? Another exceptional name for an exception that I now use is called ClassViolationException( ... ) if a class doesn't exist for example?

    Maybe I could start a thread on this subject.
    PHP 5.1 has InvalidArgumentException in the SPL

  16. #16
    SitePoint Addict timvw's Avatar
    Join Date
    Jan 2005
    Location
    Belgium
    Posts
    354
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kyberfabrikken
    The ServieLocator will depend on a concrete class, but it's the only class doing so. Besides - The test of ServiceLocator would simply be to check the type of the returned object.
    My point was and remains that the dependency still exists. The only thing one can (and should) do is move it "up" to minimise the dependencies as much as possible.

    Quote Originally Posted by kyberfabrikken
    Perhaps a more realistic example, will shed some light on the testing-problem... [snip problem]
    I am aware that it's a pita to test, but with ($engine = new enginethingie; $car = new car($engine) you can also make a MockEngine and test the car.

    Quote Originally Posted by kyberfabrikken
    Yes. That'll solve the problem too. But in this case, you expect the user to actively pass an instance, which is a burden for him/her. If you can live with that (and you often can), you can do without DI.
    How do you think that the ServiceLocator would be instantiated? It would be exactly the same burden.

    I can agree that a ServiceLocator can help people to reduce the dependency to a level that allows unittesting, withouth having to think much. So, it can be a good thing after all. The only problem is that it usually isn't the minimal dependency.


    (Btw: I did like your code samples, because they did provide a clear explanation of the techniques. I like them more than the ones M.F. uses)

  17. #17
    SitePoint Addict been's Avatar
    Join Date
    May 2002
    Location
    Gent, Belgium
    Posts
    284
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by McGruff
    However cars normally do have a choice of engines so, in this case, some kind of inversion control probably should be built in from the start.
    Actually, you've already done that by introducing the _Engine() method. Other classes can extend Car and override _Engine() to supply themselves with specific engines. TemplateMethod is definitely another way of IoC
    Per
    Everything
    works on a PowerPoint slide

  18. #18
    SitePoint Addict
    Join Date
    Apr 2004
    Location
    Melbourne
    Posts
    362
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    One thing that I'm not clear on with DI is the number of classes that may implement the same interface, and the method of choosing which to use. Say I add a rotary engine class to the example above:
    PHP Code:
    class Car
    {
        var 
    $engine;
        function 
    Car(IEngine $engine) {
            
    $this->engine $engine;
        }
        function 
    getEngine() {
            return 
    $this->engine;
        }
    }

    class 
    Engine implements IEngine
    {
    }

    class 
    RotaryEngine implements IEngine
    {
    }

    class 
    DependencyInjector
    {
        function 
    CreateInstance($class) {
            
    // ...
        
    }
    }

    $car DependencyInjector::CreateInstance('Car'); 
    How do I make sure my car ends up with a rotary instead of a V8? Or does the injector require the use of one class per interface?

  19. #19
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi.

    You have to register classes/objects with the injector. This creates a pool from which it can instantiate. The main point is that component writers don't have to worry about this, they just assume they will be passed the component interfaces they need in their constructors. The framwork authors just need to call the creation methods on the constructor as needed, but only need to work with interfaces. The applcation does not need to know anything about construction at all, it just has to supply a list of class names to the injector.

    It's this extreme degree of decoupling that gives DI it's power. It's also complete overkill for small applications.

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  20. #20
    SitePoint Addict timvw's Avatar
    Join Date
    Jan 2005
    Location
    Belgium
    Posts
    354
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    That is exactly the point of DI: you decide in the CreateInstance method what happens...

    A related problem: How do i choose which parameters are used for the constructor method.

    Instead of changing the DI everytime when you want other classes/parameters you could also pass them to the CreateInstance method.

    PHP Code:
    $car DI::CreateInstance('Car''RotaryEngine' => $myvariables); 
    But then you are almost back at the beginning where you type the:

    PHP Code:
    $engine = new RotaryEngine($myvariables);
    $car = new Car($engine); 

  21. #21
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by timvw
    A related problem: How do i choose which parameters are used for the constructor method.
    Quote Originally Posted by Tanus
    How do I make sure my car ends up with a rotary instead of a V8? Or does the injector require the use of one class per interface?
    Well, my example DependencyInjector was a bit naive. A real-world implementation would have a registry of the interfaces it can create, aswell as know (through reflection) which parameters the constructor would expect.
    PHP Code:
    DI::RegisterImplementation('IEngine''RotaryEngine');
    (...)
    $car DI::CreateInstance('Car'); 
    The whole point of using DI is that you are "spared" from choosing which parameters to use. You'll of course still have need for creating objects in the standard way (new Foo($bar) ) from time to other - you don't have to use DI for every creation in your application. That would be a design-decission.

    You should have a look at picocontainer to get an idea of how the thing works. Some real-world code-samples may make more sense.
    You should also have a look at lastcraft's php-implementation, called phemto. I believe that the php-version of pico-container is based off of phemto?

  22. #22
    SitePoint Addict timvw's Avatar
    Join Date
    Jan 2005
    Location
    Belgium
    Posts
    354
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Now, if you want to make it really flexible don't call the registerImplementation methods yourself... Instead implement a configurationloader that does the registering for you.

    Now all you have to do is edit the config file (interfacename, classname, parameters)..

  23. #23
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by timvw
    Now, if you want to make it really flexible don't call the registerImplementation methods yourself
    That's what I tried to imply with the (...)

    You should of course not call RegisterImplementation from within the object which creates the instance. That would kind of defy the whole point. You would do that outside - for example through some configurationfile as you suggested.

  24. #24
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Instead implement a configurationloader that does the registering for you.
    On the article of Dependency Injection on Martin Fowlers website, there is talk of configurable DI on there if I remember?

    http://martinfowler.com/articles/inj...ichOptionToUse

    Scroll down the page to read the part of this article on configuration

  25. #25
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Oh. I must be bored. I just threw this rather simple Dependency Injector together to demonstrate the concept. It's php4 (since that's my native tounge), so that'll be the first actual implementation of the pattern in php4 I've seen yet. (phemto is for php5).
    Naturally reflection and type-hinting would simplify the registerConstructor() class a bit, since the parameters could be decided automagically.
    PHP Code:
    class DependencyInjector
    {
        
    /**
          * @private
          */
        
    var $implementations = Array();

        
    /**
          * @private
          */
        
    var $constructors = Array();

        
    /**
          * Registers a class to be used for a given interface
          * @param    string    $interface     The interface to implement
          * @param    string    $class         The class implementing the interface
          */
        
    function registerImplementation($interface$class) {
            
    $interface strtolower($interface);
            
    $class strtolower($class);
            
    $this->implementations[$interface] = $class;
        }

        
    /**
          * @private
          */
        
    function getImplementation($interface) {
            
    $interface strtolower($interface);
            return 
    $this->implementations[$interface];
        }

        
    /**
          * Registers the interfaces that a given class expects in it's constructor
          * This is needed since php4 doesn't have interfaces
          * @param    string    $class         Name of the class to register
          * @param    string    $interface,... Names of interfaces to use when instantiating this class
          */
        
    function registerConstructor($class) {
            
    $class strtolower($class);
            
    $parameters func_get_args();
            
    array_shift($parameters);
            
    $this->constructors[$class] = $parameters;
        }

        
    /**
          * Creates an instace of the requested class
          * @param    string    $class         The class to instantiate
          */
        
    function & createInstance($class) {
            
    $class strtolower($class);
            if (!isset(
    $this->constructors[$class])) {
                
    trigger_error("Class '$class' not registered"E_USER_WARNING);
                return;
            }
            
    $parameters = Array();
            foreach (
    $this->constructors[$class] as $interface) {
                
    $parameters[] =& $this->createInstance($this->getImplementation($interface));
            }
            
    $eval "\$instance =& new $class(";
            for (
    $i=0$l count($parameters); $i $l; ++$i) {
                if (
    $i 0) {
                    
    $eval .= ", ";
                }
                
    $eval .= "\$parameters[$i]";
            }
            
    $eval .= ");";
            eval(
    $eval);
            return 
    $instance;
        }
    }

    // EXAMPLE

    /**
      * @implements IEngine
      */
    class Engine
    {
    }

    class 
    Car
    {
        var 
    $engine;

        function 
    Car(&$engine) {
            
    $this->engine =& $engine;
        }

        function & 
    getEngine() {
            return 
    $this->engine;
        }
    }

    // Registration. This would normally happen at the beginning of your application.
    $di =& new DependencyInjector();
    $di->registerConstructor('Car''IEngine');
    $di->registerConstructor('Engine');
    $di->registerImplementation('IEngine''Engine');




    // Instantiation. This would normally happen inside your application.
    $car =& $di->createInstance('Car');
    echo 
    "<pre>";
    var_export($car); 


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •