SitePoint Sponsor

User Tag List

Page 2 of 2 FirstFirst 12
Results 26 to 41 of 41
  1. #26
    SitePoint Addict
    Join Date
    Feb 2006
    Posts
    281
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Findus View Post
    What sort of problems do you get directly with this?
    Problems like:

    PHP Code:
    <?php  
    class ModuleHeader extends Module {
        function 
    ModuleHeader(&$locator) {
            
    $this->url      =& $locator->get('url');
            
    $this->language =& $locator->get('language');
            
    $this->customer =& $locator->get('customer');
        } 

        function 
    fetch() {
            
    $this->language->load('module/header.php');
            
            
    // Can not pass locator into new template
            
    $view = new Template(); // <-- $locator goes here 

            
    $view->set('text_home'$this->language->get('text_home'));
            
    $view->set('text_account'$this->language->get('text_account'));
            
    $view->set('text_login'$this->language->get('text_login'));
            
    $view->set('text_logout'$this->language->get('text_logout'));
            
    $view->set('text_cart'$this->language->get('text_cart')); 
            
    $view->set('text_checkout'$this->language->get('text_checkout'));

            
    $view->set('home'$this->url->href('home'));

            
    $view->set('account'$this->url->ssl('account'));

            if (!
    $this->customer->isLogged()) {
                  
    $view->set('login'$this->url->ssl('account_login'));
            } else {
                  
    $view->set('logout'$this->url->href('account_logout'));
            }

            
    $view->set('cart'$this->url->href('cart'));

            
    $view->set('checkout'$this->url->ssl('checkout_shipping'));

            return 
    $view->fetch('module/header.tpl');
          }
    }
    ?>
    I cannot pass the locator into new classes or create a class using factory method.

    I have this problem with modules such as boxes, payment, shippping etc..

    I don't want to pass it through fetch method because it may require some perameters.

  2. #27
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    > I keep runing into problems passing around a locator.

    To me, once you hit upon this problem, then there isn't so much an issue to do with the container it's self, but the design of your architecture. More often than not, this is what I've learnt.

    Another issue in regards to a static Singleton is that you'll find it harder to test for, so it's just a case of passing it around that gains you more flexibility in other areas that are not as apparent.

  3. #28
    SitePoint Addict
    Join Date
    Feb 2006
    Posts
    281
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    This is the only fix I can think of:


    PHP Code:
    <?php  
    class ModuleHeader extends Module {
        function 
    ModuleHeader() {
            
    $this->url      =& $this->locator->getUrl();
            
    $this->language =& $this->locator->getLanguage();
            
    $this->customer =& $this->locator->getCustomer();
            
            
    parent::Module();
        } 

        function 
    fetch() {
            
    $this->language->load('module/header.php');
            
            
    // Can not pass locator into new template
            
    $view = new Template($this->locator); // <-- $locator goes here 

            
    $view->set('text_home'$this->language->get('text_home'));
            
    $view->set('text_account'$this->language->get('text_account'));
            
    $view->set('text_login'$this->language->get('text_login'));
            
    $view->set('text_logout'$this->language->get('text_logout'));
            
    $view->set('text_cart'$this->language->get('text_cart')); 
            
    $view->set('text_checkout'$this->language->get('text_checkout'));

            
    $view->set('home'$this->url->href('home'));

            
    $view->set('account'$this->url->ssl('account'));

            if (!
    $this->customer->isLogged()) {
                  
    $view->set('login'$this->url->ssl('account_login'));
            } else {
                  
    $view->set('logout'$this->url->href('account_logout'));
            }

            
    $view->set('cart'$this->url->href('cart'));

            
    $view->set('checkout'$this->url->ssl('checkout_shipping'));

            return 
    $view->fetch('module/header.tpl');
          }
    }
    ?>

  4. #29
    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 blueyon View Post
    I think I get the idea now!

    Instead of just using a registry, factory or service locator. You have a mix of all 3.

    Something like:

    (..)

    Is this what you mean kyberfabrikken?
    I'm not entirely sure, what your code in that post is supposed to do, so I don't really know if that was what I meant. Let me try with a more concrete explanation.

    Most classes have dependencies on something, of which there will normally be just one in the application. Let's say, you're using a table data gateway pattern, so you have a UserGateway, which can load User objects from your database. The gateway needs a connection to the database, say a DatabaseConnection object. I'd then make the UserGateway take the DatabaseConnection as argument to the constructor. Eg.:
    PHP Code:
    class DatabaseConnection
    {
    // ...
    }

    class 
    UserGateway
    {
      protected 
    $db;

      function 
    __construct($db) {
        
    $this->db $db;
      }

    So far, so good. Now let's assume that I figure out, that I want my gateway to use an identity map, to keep track of referential integrity. I implement the identity map as a separate object.
    PHP Code:
    class IdentityMap
    {
      function 
    get($type$pkey) {
        
    // ..
      
    }

      function 
    put($type$pkey$record) {
        
    // ..
      
    }

    The gateway needs the identity map, so we change the constructor:
    PHP Code:
    class UserGateway
    {
      protected 
    $db;
      protected 
    $idmap;

      function 
    __construct($db$idmap) {
        
    $this->db $db;
        
    $this->idmap $idmap;
      }

    This poses a problem however. Since we have changed the constructor, we have hereby changed the interface of our gateway. We will now need to search our code for places, where the gateway is instantiated, and add the new dependency. This makes it tedious to alter the constructors signature. The solution is to encapsulate the constructor in a factory:
    PHP Code:
    class Assembler
    {
      function 
    createUserGateway() {
        return new 
    UserGateway(
          
    $this->createDatabaseConnection(),
          
    $this->createIdentityMap());
      }

      function 
    createDatabaseConnection() {
        return new 
    DatabaseConnection();
      }

      function 
    createIdentityMap() {
        return new 
    IdentityMap();
      }

    The constructor is now hidden away, so you can add or remove dependencies in a single central place. This implementation gives us a new problem though; Assume, that you add another gateway, say a Product (So your application has two datatypes -- Users and Products). You can add another table data gateway, ProductGateway. However, presumably, you only want to have one instance of each class, in your application. So the following wouldn't work, since you would end up with two instances of DatabaseConnection and IdentityMap, each:
    PHP Code:
    class Assembler
    {
      function 
    createUserGateway() {
        return new 
    UserGateway(
          
    $this->createDatabaseConnection(),
          
    $this->createIdentityMap());
      }

      function 
    createProductGateway() {
        return new 
    ProductGateway(
          
    $this->createDatabaseConnection(),
          
    $this->createIdentityMap());
      }

      function 
    createDatabaseConnection() {
        return new 
    DatabaseConnection();
      }

      function 
    createIdentityMap() {
        return new 
    IdentityMap();
      }

    The solution then, is to add a registry to the factory. A registry is simply a storage of instances. So, before instantiating a class, you would check the registry to see if there is already one, and use it if there is. You can implement the factory and registry as separate classes, or merge them into one. Since the registry is really just a container, we'll take the latter approach, and implement the registry as an associative array, on the Assembler class:
    PHP Code:
    class Assembler
    {
      protected 
    $registry = Array();

      function 
    get($className) {
        if (!isset(
    $this->registry[$className])) {
          
    $this->registry[$className] = $this->create($className);
        }
        return 
    $this->registry[$className];
      }

      function 
    create($className) {
        
    $methodName 'create'.$className;
        return 
    $this->$methodName();
      }

    // ...

    The get() method will return a class from the internal array (the registry), if it has already been created. If it hasn't, it will call the create() method, which delegates to createClassname. Thus, if you call get('database') twice, the following will happen:
    First the method createDatabase() will be called. The result is stored in $registry, and then returned. The second time around, it already exists in the $registry, so it's just returned.
    We not need to modify out factories slightly:
    PHP Code:
    class Assembler
    {
      protected 
    $registry = Array();

      function 
    get($className) {
        if (!isset(
    $this->registry[$className])) {
          
    $this->registry[$className] = $this->create($className);
        }
        return 
    $this->registry[$className];
      }

      function 
    create($className) {
        
    $methodName 'create'.$className;
        return 
    $this->$methodName();
      }

      function 
    createUserGateway() {
        return new 
    UserGateway(
          
    $this->get('DatabaseConnection'),
          
    $this->get('IdentityMap'));
      }

      function 
    createProductGateway() {
        return new 
    ProductGateway(
          
    $this->get('DatabaseConnection'),
          
    $this->get('IdentityMap'));
      }

      function 
    createDatabaseConnection() {
        return new 
    DatabaseConnection();
      }

      function 
    createIdentityMap() {
        return new 
    IdentityMap();
      }

    And that concludes -- You now have an assembler, which is capable of creating UserGateway and ProductGateway, without exposing the dependencies to the user, and without getting more instances than you need. And you didn't use any global variables, or static calls. All dependencies are passed in the constructor, so this is a form of dependency injection.

    You still need to write a factory for each class in your application. There are two approaches to this problem. Either you try to automate your way out of it. For example, both gateways have similar signatures, so there's some potential for code reuse there. You could let them extend from a common baseclass, and make the assembler intelligent enough to use this, if no direct factory exists. You could also use a configuration language of sorts, and as a variant of this, you could use typehints and reflection.
    The other option is to stop here. The amount of classes in any application is limited, and since you just need to write one factory for each, it's a rather affordable task, in exchange for the gain in simplicity and code clarity.

    There is one problem left now. The above code works, when there is only a single instance, which can be resolved at creation time. Sometimes, you need to create multiple instances, during the run time of the application. Let's say for example, that the UserGateway returns User objects. Each of these are created at a later time, than the gateway. To solve this, you could pass the Assembler to the gateway. The gateway can then use the factory as it needs:
    PHP Code:
    class UserGateway
    {
      protected 
    $db;
      protected 
    $assembler;

      function 
    __construct($db$assembler) {
        
    $this->db $db;
        
    $this->assembler $assembler;
      }

      function 
    getUser($id) {
        
    $result $this->db->pexecute(
          
    "select * from users where user_id = :user_id",
          Array(
            
    ':user_id' => $id));
        return 
    $this->assembler->createUser($result->fetch());
      }

    Note that, since we're passing in the assembler to the gateway, we could have saved passing the DatabaseConnection too. I chose to do it this way, to make the dependency more explicit. If you had in fact just passed the assembler, you would be using a locator pattern, rather than dependency injection.

    I hope that shed some light.
    Last edited by kyberfabrikken; Jul 12, 2007 at 13:45.

  5. #30
    SitePoint Zealot
    Join Date
    Jun 2004
    Location
    Norway - Oslo
    Posts
    198
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'll skip the quotes... What kyberfabriken says is the exact way i do it, except i keep the database in the "assembler" (locator). So
    PHP Code:
    class ModuleHeader extends Module {
      public function 
    __construct($locator) {
        
    $this->locator $locator;
      }
      public function 
    fetch() {
        
    $tpl = new Template($this->locator);
      }

    Of course, if Template have simpler dependencies i'd rather do something like:
    PHP Code:
    $tpl = new Template($this->locator->getSomeDependency()); 
    Using the locator adds a depedency to the locator, and the interface of the objects provided by the locator, so when an object have a single depedency your better off passing in just that one.
    Raymond Julin
    Developer: Hardware.no, Amobil.no, Gamer.no, Prisguide.no ...
    Owner: Kulturo.no

  6. #31
    SitePoint Addict
    Join Date
    Feb 2006
    Posts
    281
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    kyberfabrikken,

    This is a very good example. Thank you very much!

  7. #32
    SitePoint Enthusiast
    Join Date
    Feb 2004
    Location
    Montreal
    Posts
    77
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Why do dependencies need to be passed into the constructor? Perhaps the constructor's signature helps to describe it's interface, but I do not think that having an Assembler know the intricacies of an object's dependencies is necessary.

    Instead, what I propose is to pass the registry into the constructor of such lazy-loaded objects.

    It is then the object's responsibility to load (from the registry) its dependencies in the constructor. That way, the object itself is the only class that has knowledge of its dependencies.

    This system factors well into dynamic loading of classes at run-time. Circular dependencies can be determined elegantly at run-time as well. If anyone is interested, I can post sample code when I get back from work.

  8. #33
    SitePoint Addict
    Join Date
    Feb 2006
    Posts
    281
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by b1ind View Post
    Why do dependencies need to be passed into the constructor? Perhaps the constructor's signature helps to describe it's interface, but I do not think that having an Assembler know the intricacies of an object's dependencies is necessary.

    Instead, what I propose is to pass the registry into the constructor of such lazy-loaded objects.

    It is then the object's responsibility to load (from the registry) its dependencies in the constructor. That way, the object itself is the only class that has knowledge of its dependencies.

    This system factors well into dynamic loading of classes at run-time. Circular dependencies can be determined elegantly at run-time as well. If anyone is interested, I can post sample code when I get back from work.
    Please post some sample code. It's always good to look at how other people implement things.

  9. #34
    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 b1ind View Post
    Why do dependencies need to be passed into the constructor? Perhaps the constructor's signature helps to describe it's interface, but I do not think that having an Assembler know the intricacies of an object's dependencies is necessary.

    Instead, what I propose is to pass the registry into the constructor of such lazy-loaded objects.
    That is called a service locator (Or sometimes just a locator). It works well, as you have noted, but it has two weaknesses. The first is, that you form a dependency on the locator, from your class. That is, you add a dependency, which wasn't there before. This is not a huge thing in it self, but it's still a price to pay in terms of complexity.
    The other problem, which I think is far more problematic, is that the dependencies are less explicit. You can't know which dependencies a given class has until at runtime (Or by manually analysing the code). That makes the code less obvious to use.

    One hybrid pattern, is to use a dependency injection container, like the one I described in the above post, but implement the factory as a static method on the class it self. That way, the default factory is in the class, but the user of the class will not call this directly. To pull this off, you will need to make the assembler call to the class' factory, passing itself (Acting as service locator), so the factory can pull the dependencies needed. Eg.:
    PHP Code:
    class Assembler
    {
      protected 
    $registry = Array();

      function 
    get($className) {
        if (!isset(
    $this->registry[$className])) {
          
    $this->registry[$className] = $this->create($className);
        }
        return 
    $this->registry[$className];
      }

      function 
    create($className) {
        
    $factory = Array($className'CreateInstance');
        return 
    call_user_func($factory$this);
      }
    }

    class 
    UserGateway
    {
      protected 
    $db;
      protected 
    $idmap;

      function 
    __construct($db$idmap) {
        
    $this->db $db;
        
    $this->idmap $idmap;
      }

      static public function 
    CreateInstance($locator) {
        return new 
    self(
          
    $locator->get('DatabaseConnection'),
          
    $locator->get('IdentityMap'));
      }
    }

    $container = new Assembler();
    $users $container->get('UserGateway'); 
    If you further extend the factory signature to accept the classname, you can reuse factories of a baseclass in its descendants:
    PHP Code:
    class Assembler
    {
      protected 
    $registry = Array();

      function 
    get($className) {
        if (!isset(
    $this->registry[$className])) {
          
    $this->registry[$className] = $this->create($className);
        }
        return 
    $this->registry[$className];
      }

      function 
    create($className) {
        
    $factory = Array($className'CreateInstance');
        return 
    call_user_func($factory$this$className);
      }
    }

    abstract class 
    Gateway
    {
      protected 
    $db;
      protected 
    $idmap;

      function 
    __construct($db$idmap) {
        
    $this->db $db;
        
    $this->idmap $idmap;
      }

      static public function 
    CreateInstance($locator$className) {
        return new 
    $className(
          
    $locator->get('DatabaseConnection'),
          
    $locator->get('IdentityMap'));
      }
    }

    class 
    UserGateway extends class Gateway
    {
      
    $protected $table 'users';
    }

    $container = new Assembler();
    $users $container->get('UserGateway'); 
    And there you have it; A fully capable dependency injection container in 16 lines of code. I guess I'll take that bet now, Marcus.

  10. #35
    SitePoint Enthusiast
    Join Date
    Feb 2004
    Location
    Montreal
    Posts
    77
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    That is very interesting solution kyber. I have something very similar, but it differs slightly, and fundamentally in the implementation of the static factory method. I think the idea can be expanded slightly to offer the benefits of both worlds.

    Disclaimer: Not having read any of the books normally quoted, excuse my naming decisions.

    First, an interface is defined for objects that can be used:
    PHP Code:
    interface Injectable {
        public static function 
    create(Locator $locator);

    Then, users are given an abstract base class that can be extended for the hybrid behavior. Note that this option removes some of the benefits gained by kyber's method, but gains in simplicity.
    PHP Code:
    // Abstract (optional) base class that uses the hybrid system
    abstract class CongreteInjectable implements Injectable {
        
    // Default constructor accepts the locator
        // This is the hybrid part :)
        
    public function __construct(Locator $locator) {}

        
    // This function can be overridden to use kyber's more explicit
        // dependency system
        
    public static function create(Locator $locator) {
            return new 
    self($locator);
        }

    Voilą!


    PS:
    Although I have not 'read the books', I don't think that this could be called dependency injection per se. It is more of a dependency pulling system, whereas dependency injection is more of a push system.

    Not having found a reasonable antonym for injection, could you call this dependency acquisition? I don't know if it has quite the same ring, but nevertheles...

  11. #36
    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 b1ind View Post
    First, an interface is defined for objects that can be used:
    You can use an interface to make the contract explicit, if you like. I don't use that, because I decouple factory and assembler a little bit further, using a callback as the factory. With an interface, you make it tedious to attach make the factory user-definable. Most of the time, you will of course use the default (Class::CreateInstance) factory, but a container should allow for re-wiring. I use a callback as factory, which makes it easy to plug new factories in at runtime.

    Quote Originally Posted by b1ind View Post
    PHP Code:
    // Default constructor accepts the locator
    // This is the hybrid part :)
    public function __construct(Locator $locator) {} 
    There isn't much hybrid over this. It's a clear-cut locator pattern. The static method is redundant though, you can just have the assembler call the constructor directly, if that's what you want. That also saves you from creating an abstract baseclass.

  12. #37
    SitePoint Wizard stereofrog's Avatar
    Join Date
    Apr 2004
    Location
    germany
    Posts
    4,324
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by b1ind View Post
    Although I have not 'read the books', I don't think that this could be called dependency injection per se. It is more of a dependency pulling system, whereas dependency injection is more of a push system.

    The advantage of injection ("push") compared to ServiceLocator ("pull") is that class itself is independent on the particular wiring technique and isn't required to export specific functions to support it. Ideally, you could plug any (foreign) class into your setup without need to change or extend it ("plug and play", you know

    We can roughly classify different wiring techniques as "pure injectors", "mixed" and "pure locators".

    With "pure injectors" a class is complete unaware on what's going on, it's only required to have a sensible constructor declaration. Pure injector techniques include:

    1. use reflection to automatically wire dependencies based on typehinting in constructor declaration (this is how phemto works)

    2. use reflection to wire dependencies based on parameters' names (see, e.g. http://forums.devnetwork.net/viewtop...=398042#398042 )

    3. hardcode dependencies directly in the locator
    PHP Code:
    class Locator {
       function 
    get($class) {
          return 
    $this->{"get$class"}();
       }
       function 
    getGateway() {
          return new 
    Gateway($this->get('db'), $this->get('idmap'));
       }
    ... 
    Mixed approaches (where constructor is independent, but a class must provide an additional entry point for dependency resolution) are:

    4. class provides a factory method (see kyber's code)

    5. class provides an explicit resolver method
    PHP Code:
    class Gateway {
       function 
    getDeps() {
           return array(
    'db''idmap');
    ...

    class 
    Locator {
       function 
    get($class) {
          
    $deps call_user_func(array($class'getDeps'));
          foreach(
    $deps as $d)
             
    $params[] = $this->get($d);
          
    $ref = new ReflectionClass($class);
          return 
    $ref->newInstanceArgs($params); 
       }
    ... 
    And finally, a pure ServiceLocator (6) is what you wrote: constructor get a Locator as a parameter and pulls its dependencies out of it.

    Which method to choose strongly depends on your particular application. For a small program with limited number of known classes, SL will work ok, however a bigger system with loads of foreign classes would require (1) or even (3).

    It may be also useful to experiment with mixing approaches, e.g. (1) or (2) mixed with (5) looks quite interesting.

    Hope this was helpful.

  13. #38
    SitePoint Enthusiast
    Join Date
    Feb 2004
    Location
    Montreal
    Posts
    77
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ahh, very informative. I can see where the literature would have helped me. I think that I will personally continue with a locator-only approach.

    I am curious, however, why DI becomes so essential in bigger projects. I am sure that I am not the only one wondering this. Is it because you cannot expect a constant constructor signature for externally-controlled dependencies?

    Quote Originally Posted by kyberfabrikken
    There isn't much hybrid over this. It's a clear-cut locator pattern. The static method is redundant though, you can just have the assembler call the constructor directly, if that's what you want. That also saves you from creating an abstract baseclass.
    The idea was that using the abstract base class, both became possible transparently. Granted, the interface is not necessary; it was meant as a definition of the contract.

    Quote Originally Posted by kyberfabrikken
    You can use an interface to make the contract explicit, if you like. I don't use that, because I decouple factory and assembler a little bit further, using a callback as the factory. With an interface, you make it tedious to attach make the factory user-definable.
    Can you please explain this a bit more?

  14. #39
    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)
    Excellent wrap up, Stereofrog.

    Quote Originally Posted by b1ind View Post
    Can you please explain this a bit more?
    I'm unsure if you mean to explain how I use callbacks, or why an interface makes for an inflexible solution? I'll try to answer both, since they are connected.

    Now, when I said that an interface made the solution rigid, I was being imprecise. It's not the interface, which is rigid, it's the object oriented style. Eg. the contract which gets explicit with an interface, but still exists when there isn't a formal interface.

    Assuming that you wanted to be able to plug factories into the assembler at runtime, you could go an object oriented route, and define a factory interface:
    PHP Code:
    interface Factory
    {
      function 
    createInstance($class$locator);

    Then extend the assembler to hold a map of factories, instead of hardcoding the call to CreateInstance() in:
    PHP Code:
    class Assembler
    {
    ...
      protected 
    $factories = Array();

      function 
    register($classNameFactory $factory) {
        
    $this->factories[$className] = $factory;
      }

      function 
    create($className) {
        return 
    $this->factories[$class]->createInstance($className$this);
      }

    You now have a container with factories attached dynamically (at runtime). That gives you great flexibility, however this implementation would require you to write a new class for each component you want to have a factory for. That's rather bureaucratic, so we need something a bit more smooth.

    These factories have a simple protocol -- In fact they have just one method. This is a pattern, which is known as a command object, and it is the object oriented variant of a lambda. As long as it's stateless, it can be replaced with a delegated function call instead. The advantage then, is that you can group multiple such factory function together in a single class, instead of having multiple classes, each with one method. Behold:
    PHP Code:
    class Assembler
    {
    ...
      protected 
    $factories = Array();

      function 
    register($className$factory) {
        
    $this->factories[$className] = $factory;
      }

      function 
    create($className) {
        
    assert(is_callable($factory));
        return 
    call_user_func($this->factories[$class], $className$this);
      }

    And in case the usage isn't totally obvious, here goes:
    PHP Code:
    class MyFactory
    {
      function 
    CreateUserGateway($class$locator) {
        return new 
    UserGateway(
          
    $locator->get('DatabaseConnection'),
          
    $locator->get('IdentityMap'));
      }

      function 
    CreateDatabaseConnection($class$locator) {
        return new 
    DatabaseConnection();
      }

      function 
    CreateIdentityMap($class$locator) {
        return new 
    IdentityMap();
      }
    }
    $locator = new Assembler();
    $factory = new MyFactory();
    $locator->register('UserGateway', Array($factory'CreateUserGateway'));
    $locator->register('DatabaseConnection', Array($factory'CreateDatabaseConnection'));
    $locator->register('IdentityMap', Array($factory'CreateIdentityMap'));

    $users $locator->get('UserGateway'); 
    Or in a functional style:
    PHP Code:
    $locator = new Assembler();
    $locator->register(
      
    'UserGateway',
      
    create_function(
        
    '$class, $locator',
        
    'return new UserGateway(
           $locator->get("DatabaseConnection"),
           $locator->get("IdentityMap"));'
    ));
    $locator->register(
      
    'DatabaseConnection',
      
    create_function(
        
    '$class, $locator',
        
    'return new DatabaseConnection(...);'));
    $locator->register(
      
    'IdentityMap',
      
    create_function(
        
    '$class, $locator',
        
    'return new IdentityMap(...);'));

    $users $locator->get('UserGateway'); 
    You might want to look up is_callable, call_user_func and create_function, if you aren't familiar with these lesser known PHP features.

  15. #40
    SitePoint Enthusiast
    Join Date
    Feb 2004
    Location
    Montreal
    Posts
    77
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thank you again kyber and stereofrog, your responses are more than comprehensive.

    I am now back from work, and as promised, here is my code:

    PHP Code:
    class FALocator {
        protected 
    $objects = array();
        protected 
    $loading = array();
        
        final public function 
    get($name) {
            if (
    in_array($name$this->loading))
                throw new 
    FAApplicationException("Circular dependency in objects: " implode('->'$this->loading) .".");
            
            
    array_push($this->loading$name);            
            
    $object $this->load($name);
            
    array_pop($this->loading);
            
            return 
    $object;
        }
        
        protected function 
    load($name) {
            
    $class FALoader::load($nameFALoader::OBJECT);
            
            return 
    call_user_func_array(array($class'create'), array($this$class));
        }

    As you have explained, it is a pure locator style. The only thing 'new' in there is the simply detection of circular dependencies.

  16. #41
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    From what I remember, WACT in SVN has an implementation much like what has been posted here over the last couple of days.


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
  •