SitePoint Sponsor

User Tag List

Page 2 of 2 FirstFirst 12
Results 26 to 34 of 34
  1. #26
    SitePoint Addict
    Join Date
    Sep 2006
    Posts
    232
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Nice one, thanks. I did some testing and I understood why you added XML and YAML support, registering dependencies can be a pain. I also tried using an array and ended up having a massive array, and it didn't look pretty. So far, I think your implementation covers many of the problems that a DI container faces when dealing with real case scenarios. I've just finished building a component for the Zend Framework and I'm planning to use your DI container to map the dependencies. Good stuff, thanks.

  2. #27
    SitePoint Addict
    Join Date
    Sep 2006
    Posts
    232
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ok, having implemented this, I can now see a little bit more clearly the advantages of registering or injecting the components into the container as opposed to auto detecting and loading them without the user knowing. If you search in the constructor and setter methods for classes and load them, you are basically injecting dependencies in a non transparent way. And this can be a pain when working with big frameworks. But if you register the components, you are always informing the user the dependencies an object has. For example:
    PHP Code:
    $di = new Zend_Image_Dependencies();
    $di->register('Driver_GD', new Zend_Image_Driver_GD());
    $di->register('Driver_IM', new Zend_Image_Driver_IM());

    $image = new Zend_Image($di);
    $image->getDriver('Driver_GD'); 
    The example above works better than this one:

    PHP Code:
    $image = new Zend_Image();
    $image->setDriver('Driver_GD', new Zend_Image_Driver_GD());
    $image->getDriver('Driver_GD'); 
    Because now I don't have setter methods spread all over the place with hardcoded hints checking if the class is an instance of a parent class or interface.

    Now, using external files to map dependencies, is that a goog way of doing it? You are auto detecting dependencies in the constructor and setter methods and loading them in the background. So, how does the user know what is being loaded? He has to check an external file, such as an xml or yaml file to figure out what's going on, right? And that's the bit that worries me. Because the way I see it, the user has 2 options:

    Imagine a tourists in Amsterdam trying to find the Van Gogh museum. There are 2 ways he can get there, following the signs or buying a map. Buying a map adds extra complexity to his trip, because he doesn't speak Dutch (or XML or YAML), he has to go and change some money, then find a shop and buy it. On the other hand, following the signs is a simpler option. Now, why are the signs there? Because the architects of the city knew this was going to happen and provided the tourist with a simple an quick solution to his problem.

    But that's just one problem. The other problem is the objects are instantiated somewhere else, in a non transparent way. The idea of a container is to contain objects right, and the idea of the dependency injection pattern is to inject objects to a container (please correct me if I'm wrong).

    Now, where does it say that we have to scan objects to find classes to create instances inside the container? That's not what I want in a framework, I want to be able to see the dependencies that are being instantiated and stored in the container. Like in the first example. Does it make sense what I'm saying?

  3. #28
    Resident Code Monkey Chris Corbyn's Avatar
    Join Date
    Nov 2005
    Location
    Melbourne, Australia
    Posts
    713
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by phpimpact View Post
    Ok, having implemented this, I can now see a little bit more clearly the advantages of registering or injecting the components into the container as opposed to auto detecting and loading them without the user knowing. If you search in the constructor and setter methods for classes and load them, you are basically injecting dependencies in a non transparent way. And this can be a pain when working with big frameworks. But if you register the components, you are always informing the user the dependencies an object has. For example:
    PHP Code:
    $di = new Zend_Image_Dependencies();
    $di->register('Driver_GD', new Zend_Image_Driver_GD());
    $di->register('Driver_IM', new Zend_Image_Driver_IM());

    $image = new Zend_Image($di);
    $image->getDriver('Driver_GD'); 
    The example above works better than this one:

    PHP Code:
    $image = new Zend_Image();
    $image->setDriver('Driver_GD', new Zend_Image_Driver_GD());
    $image->getDriver('Driver_GD'); 
    Because now I don't have setter methods spread all over the place with hardcoded hints checking if the class is an instance of a parent class or interface.

    Now, using external files to map dependencies, is that a goog way of doing it? You are auto detecting dependencies in the constructor and setter methods and loading them in the background. So, how does the user know what is being loaded? He has to check an external file, such as an xml or yaml file to figure out what's going on, right? And that's the bit that worries me. Because the way I see it, the user has 2 options:

    Imagine a tourists in Amsterdam trying to find the Van Gogh museum. There are 2 ways he can get there, following the signs or buying a map. Buying a map adds extra complexity to his trip, because he doesn't speak Dutch (or XML or YAML), he has to go and change some money, then find a shop and buy it. On the other hand, following the signs is a simpler option. Now, why are the signs there? Because the architects of the city knew this was going to happen and provided the tourist with a simple an quick solution to his problem.

    But that's just one problem. The other problem is the objects are instantiated somewhere else, in a non transparent way. The idea of a container is to contain objects right, and the idea of the dependency injection pattern is to inject objects to a container (please correct me if I'm wrong).

    Now, where does it say that we have to scan objects to find classes to create instances inside the container? That's not what I want in a framework, I want to be able to see the dependencies that are being instantiated and stored in the container. Like in the first example. Does it make sense what I'm saying?
    I like your analogy, and I take it on-board. One thing I'm not sure of is this idea of scanning objects... I'm not doing that.

    It feels like a wasted effort (and resource use) to instantiate all possible dependencies then find that some of them aren't even used. If you're going to create all those instances they may as way be passed to their dependent classes and not to the container.

    Also, passing the container to something like Zend_Image defeats the object because the you've added even more coupling.

  4. #29
    SitePoint Addict
    Join Date
    Sep 2006
    Posts
    232
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Umm, you are right. I think you are still a step ahead with your solution, because like I said before, it covers more problems that the ones I currently have. Maybe I need to test it with other components as well. Anyway, it will be nice to see how you implement it in Swift. Thanks Chris.

  5. #30
    SitePoint Addict
    Join Date
    Sep 2006
    Posts
    232
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ok, this is what I'm trying to do. I'm trying to add a DI container to the Zend Framework. So far, your solution Chris has been the most effective.

    Now, this is the challenge I'm facing. In ZF you have packages and sub-packages. For example: "Backend" is a sub-package of "Zend_Cache". Lets assume I create a package to import/export CSV files and it's called Zend_Csv. And I have 4 subpackages:

    Code:
    Zend_Csv_Database: To execute queries based on different adapters.
    Zend_Csv_Protocol: In case I want to download the file using FTP or HTTP.
    Zend_Csv_Log: In case I run a cron job and want to log the error messages.
    Zend_Csv_Mail: In case I want to receive the error message by email.
    I could do this:

    PHP Code:
    $csv = new Zend_Csv_Import(
        new 
    Zend_Csv_Database_Pdo(), 
        new 
    Zend_Csv_Protocol_Ftp(),
        new 
    Zend_Csv_Log_File(),
        new 
    Zend_Csv_Mail_Php());
        
    // Assuming the contructor is:

    function __construct(Zend_Csv_Database_Abstract $db
        
    Zend_Csv_Protocol_Abstract $protocol null
        
    Zend_Csv_Log_Abstract $Log null,
        
    Zend_Csv_Mail_Abstract $mail null) { 
    But the more sub-packages I add, the more uncomfortable it gets. Using setter methods sounds reasonable, but I don't want type hints hardcoded into the classes. So, this is what I'm doing at the moment:

    PHP Code:
    // load class and create new instance
    $di->loadClass(classNamename)->newInstance(args);
    // create new instance
    $di->classOf(name)->newInstance(args);
    // get single instance
    $di->getInstance(name); 
    And going back to the Zend example, I'm doing something like this:

    PHP Code:
    $csv $di->loadClass('Zend_Csv_Import')->newInstance(); 
    The DI class needs to work out all the dependencies. And here is my first question, what if I need to import 20 files and each new instance I create has different specifications?

    Does that mean that I have a default config file per package, but then every time the specification changes I have to manually load the dependencies? For example:

    PHP Code:
    // load dependencies
    $di->loadClass('Zend_Csv_Database_Pdo')->newInstance();
    $di->loadClass('Zend_Csv_Protocol_Ftp')->newInstance();
    $di->loadClass('Zend_Csv_Log_File')->newInstance();
    $di->loadClass('Zend_Csv_Mail_Php')->newInstance();

    $csv $di->loadClass('Zend_Csv_Import')->newInstance(); 
    [edited] Corrected the example
    Last edited by phpimpact; Nov 10, 2007 at 18:42.

  6. #31
    Resident Code Monkey Chris Corbyn's Avatar
    Join Date
    Nov 2005
    Location
    Melbourne, Australia
    Posts
    713
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'm confused now. I'm not sure where you're going with this

    EDIT | In your first constructor example it looks like you're type-hinting on class names which is rocky territory. Use interfaces.

    It also appears you'd be best with a registerAdapter() method or whatever name fits those CSV classes, or alternatively pass an array to the constructor.

  7. #32
    SitePoint Addict
    Join Date
    Sep 2006
    Posts
    232
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Sorry, my mistake. Yes, the sub-packages they all extend an abstract class. This is a map of what I have:

    PHP Code:
        /**
         * Components.
         * @var array
         */
        
    protected $_components = array(
            
    'Zend_Csv_Import' => array(
                
    'name'         => 'Import',
                
    'instanceof'   => 'Zend_Csv_Import',
                
    'dependencies' => array('Zend_Csv_Database_Pdo''Zend_Csv_Log_File''Zend_Csv_Protocol_Ftp'),
            ),
            
    'Zend_Csv_Database_Pdo' => array(
                
    'name'         => 'Database',
                
    'instanceof'   => 'Zend_Csv_Database_Abstract',
                
    'dependencies' => array(),
            ),
            
    'Zend_Csv_Log_File'     => array(
                
    'name'         => 'Log',
                
    'instanceof'   => 'Zend_Csv_Log_Abstract',
                
    'dependencies' => array(),
            ),
            
    'Zend_Csv_Protocol_Ftp' => array(
                
    'name'         => 'Protocol',
                
    'instanceof'   => 'Zend_Csv_Protocol_Abstract',
                
    'dependencies' => array(),
            ),
            
    'Zend_Csv_Mail_Php'     => array(
                
    'name'         => 'Mail',
                
    'instanceof'   => 'Zend_Csv_Mail_Abstract',
                
    'dependencies' => array(),
            ),
        ); 

  8. #33
    SitePoint Addict
    Join Date
    Sep 2006
    Posts
    232
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Here, if we asked for a car we'd get a CustomCar with a FiatEngine using ItalianParts. Something like:

    PHP Code:
    $car $factory->create('car'); 
    So, my questions are:

    1. Imagine I have a 'car' that can either have a FiatEngine or a RenaultEngine. How do you deal with optional dependencies in your XML file?
    2. How do I tell the FiatEngine object how much fuel the tank is going to have by default, assuming each Engine has X litres of fuel? So, how can I pass an integer number to the constructor of each Engine object? And an array? Or values that are dynamically generated. Example:

    Code:
        <constructor>
          <arg>
            <componentRef>italianParts</componentRef>
    		<value type="array">(dynamic generated array)</value>
          </arg>
        </constructor>
    edit | added the xml
    Last edited by phpimpact; Nov 10, 2007 at 18:56.

  9. #34
    SitePoint Addict
    Join Date
    Sep 2006
    Posts
    232
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I've just finished creating the reflection based DI container for the Zend Framework. I've created 2 classes, Zend_Dependencies_Abstract and Zend_Dependencies_Container. Zend_Dependencies_Abstract is extended by any package that has dependencies. The package extending this class sets the component specifications and maps the relations between the dependent and its dependencies. Dependencies can be static or dynamic. The architecture of Zend_Dependencies_Abstract is based on the following concepts:

    - Dependency injection is a technique, instances are passed in the constructor or via setter methods.
    - A container holds instances of classes and can be global or local.
    - A reflection based DI container is not the same as a DI container, so it uses a different technique to inject dependencies.
    - A container with dependencies always belongs to one dependent.
    - The container is not responsible for minimizing the coupling between classes, the classes are.
    - The container provides an easy way of re-configuring a package to use custom implementations of components.

    I will be testing this container with 2 of our newest packages hoping it will improve our development methodologies. Also, I'm interested in knowing how a reflection based DI container integrates into the Zend Framework and the advantages it offers over the container that we are currently using.


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
  •