SitePoint Sponsor

User Tag List

Results 1 to 9 of 9
  1. #1
    SitePoint Guru bronze trophy
    Join Date
    Dec 2003
    Location
    Poland
    Posts
    930
    Mentioned
    7 Post(s)
    Tagged
    0 Thread(s)

    Sharing database object without singleton

    Realizing that singletons are not good for writing testable code I have done some reading and found out that a much better pattern is dependency injection. I've seen some code samples that looked logical but they didn't involve the case of a database object, which basically I need to have only one instance of in my whole application.

    So far I have come to this conclusion if an object of class A is dependent on an object of class B:

    1. Class A should require object B in the constructor.
    2. The code that uses object A uses a factory to create object A so that it is not involved in the complexities of instantiating
    3. The factory instantiates object B and passes it to the constructor of object A and returns object A.

    The problem with that is that the factory is not a singleton so it creates a new instance of classes each time. So if B is our database class that A depends on, then the factory will create a new instance of the database object each time it is called - unless it uses a singleton.

    Does that mean that DI and Factory cannot be used to solve the problem? From what I have read the proper use of DI prevents me from having to pass objects down all the layers in my application just because one method somewhere deep needs it - but I can't see how this can work for objects that really need to have only one instance like database connection. Am I doomed to passing Db around from one object to another multiple times or can I achieve it in an elegant way without using statics?

  2. #2
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,314
    Mentioned
    19 Post(s)
    Tagged
    1 Thread(s)
    First, before we get into factories, the way it would work is that we would create one database object that would be shared by each instance of A.

    PHP Code:
    $b = new B();

    $a1 = new A($b);

    $a2 = new A($b); 
    Now when we introduce a factory, we need to implement this idea of shared instances.

    PHP Code:
    class BFactory
    {
        static protected 
    $shared = array();
        
        public function 
    getB()
        {
            if (isset(
    self::$shared['b'])) {
                return 
    self::$shared['b'];
            }
         
            
    $b = new B();
            
            
    self::$shared['b'] = $b;
         
            return 
    $b;
        }

    This still looks singleton-ish, and in a way it is. The important part is that the singleton/shared instance logic is no longer baked into the B class. B can be instantiated any number of times, and it's only our specific application that decides it wants just one instance.

    That B can be instantiated any number of times may actually turn out to be important. For example, there are cases where an application's data might be spread across a couple databases. Our newly architected B and BFactory let's us accommodate that quite easily.

    PHP Code:
    class BFactory
    {
        static protected 
    $shared = array();
        
        public function 
    getDefaultB()
        {
            if (isset(
    self::$shared['default_b'])) {
                return 
    self::$shared['default_b'];
            }
         
            
    $b = new B('default connection info');
            
            
    self::$shared['default_b'] = $b;
         
            return 
    $b;
        }
        
        public function 
    getAnotherB()
        {
            if (isset(
    self::$shared['another_b'])) {
                return 
    self::$shared['another_b'];
            }
         
            
    $b = new B('other connection info');
            
            
    self::$shared['another_b'] = $b;
         
            return 
    $b;
        }

    "First make it work. Then make it better."

  3. #3
    SitePoint Guru bronze trophy
    Join Date
    Dec 2003
    Location
    Poland
    Posts
    930
    Mentioned
    7 Post(s)
    Tagged
    0 Thread(s)
    Jeff, thanks for explaining it so well, the code examples are very clear and help a lot. This idea of singleton-ish factory came to my mind but I wasn't sure this was the way to go. Yes, I already know the class itself shouldn't decide to be a singleton and when the responsibility for keeping one instance is moved to the factory it makes much more sense.

    Lastly, I have two more questions about factories:

    1. Does it matter whether factory is used statically or as instantiated object? I have come up across many examples of factories implemented with static methods. Because they are very simple constructs I don't think it matters much when used normally but avoiding static methods could potentially help with testing, am I right?

    2. Is it better to have one central factory per application that serves all kinds of objects, or is it better to have a separate factory for each type of object?

  4. #4
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,314
    Mentioned
    19 Post(s)
    Tagged
    1 Thread(s)
    Quote Originally Posted by Lemon Juice View Post
    1. Does it matter whether factory is used statically or as instantiated object? I have come up across many examples of factories implemented with static methods. Because they are very simple constructs I don't think it matters much when used normally but avoiding static methods could potentially help with testing, am I right?
    Yup, I think you're right about that. In fact, in hindsight, I wish I hadn't used a static property in my BFactory code above. But I was copying other sample code, and it was too late to edit my post. :/

    Quote Originally Posted by Lemon Juice View Post
    2. Is it better to have one central factory per application that serves all kinds of objects, or is it better to have a separate factory for each type of object?
    As your application grows bigger, I think it definitely becomes better to have one central factory per application. After all, separate factories would need to know about each other anyway. For example, an AFactory would need to know about BFactory. The idea of "one central factory that serves all kinds of objects" is actually called a dependency injection container, and it's become a staple among the newer and larger frameworks.
    "First make it work. Then make it better."

  5. #5
    SitePoint Guru bronze trophy
    Join Date
    Dec 2003
    Location
    Poland
    Posts
    930
    Mentioned
    7 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Jeff Mott View Post
    Yup, I think you're right about that. In fact, in hindsight, I wish I hadn't used a static property in my BFactory code above. But I was copying other sample code, and it was too late to edit my post. :/
    I wasn't actually asking about the static property but about calling the whole factory statically. So this:
    PHP Code:
    $factory = new BFactory();
    $b $factory->getDefaultB(); 
    versus:
    PHP Code:
    $b BFactory::getDefaultB(); 
    Is one of the above preferred?

    And why do you wish you hadn't used the static property? How else would you guarantee a single instance of B? Without a static property you would have to make sure you have a single instance of the factory being available in all scopes where you are calling it, which gets us to create one more layer in a form of a singleton to keep our factory instance the same everywhere - a bit superfluos, isn't it? Or is there another way?

    Quote Originally Posted by Jeff Mott View Post
    As your application grows bigger, I think it definitely becomes better to have one central factory per application. After all, separate factories would need to know about each other anyway. For example, an AFactory would need to know about BFactory. The idea of "one central factory that serves all kinds of objects" is actually called a dependency injection container, and it's become a staple among the newer and larger frameworks.
    Thanks, it makes sense!

    BTW, I have found Pimple, a DI container for PHP 5.3. It looks simple and the use of closures seems quite clever, do you think it's a good one to use?

  6. #6
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,314
    Mentioned
    19 Post(s)
    Tagged
    1 Thread(s)
    Quote Originally Posted by Lemon Juice View Post
    I wasn't actually asking about the static property but about calling the whole factory statically. So this:
    PHP Code:
    $factory = new BFactory();
    $b $factory->getDefaultB(); 
    versus:
    PHP Code:
    $b BFactory::getDefaultB(); 
    Is one of the above preferred?
    I lean toward the former, but I'm striving to find a specific, practical reason to justify that preference. Right now the best I have is a somewhat general rationale for avoiding static data, which is that our objects behave more predictably when they're all instance-based.

    If a class is all instance-based, then when we create a new object, we know we're getting a clean slate, and any methods we call on that new object will behave in a predicable way. But if a class uses any static data, then even when we create a brand new object, it might still carry some state from other, past objects, and as a result, it might behave differently.

    Quote Originally Posted by Lemon Juice View Post
    And why do you wish you hadn't used the static property? How else would you guarantee a single instance of B? Without a static property you would have to make sure you have a single instance of the factory being available in all scopes where you are calling it, which gets us to create one more layer in a form of a singleton to keep our factory instance the same everywhere - a bit superfluos, isn't it? Or is there another way?
    I wish I had avoided the static property for the reason above, but it's possible that avoiding static is only feasible when we're working with one central factory, rather than lots of separate factories. If it is feasible, then I suppose all the factories would have to be instantiated at the core/entry point/kernel of your application. Even if we switched to the "one central factory" approach, we would still need to create it at the core/entry point/kernel.

    Quote Originally Posted by Lemon Juice View Post
    BTW, I have found Pimple, a DI container for PHP 5.3. It looks simple and the use of closures seems quite clever, do you think it's a good one to use?
    I do think that's a good one. I'm generally impressed with most everything that Fabien Potencier writes.
    "First make it work. Then make it better."

  7. #7
    SitePoint Guru bronze trophy
    Join Date
    Dec 2003
    Location
    Poland
    Posts
    930
    Mentioned
    7 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Jeff Mott View Post
    I wish I had avoided the static property for the reason above, but it's possible that avoiding static is only feasible when we're working with one central factory, rather than lots of separate factories. If it is feasible, then I suppose all the factories would have to be instantiated at the core/entry point/kernel of your application.
    I don't know if I fully understood what you meant but I imagine that when we get rid of the static property then yes, the factory would need to be instantiated at the entry point. But what next? I'd have to use the same instance of the factory in every place, so how do I get to it from different scopes? This would need a singleton with a static property in it to get me this one instance of the factory everywhere. Isn't it then better to have the singleton-ish behaviour built into the factory itself and not have to use a separate singleton for the factory?

    Quote Originally Posted by Jeff Mott View Post
    I do think that's a good one. I'm generally impressed with most everything that Fabien Potencier writes.
    Thanks. I have also found Dice - TomB's DI container. I think I'll have a hard time deciding

  8. #8
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,314
    Mentioned
    19 Post(s)
    Tagged
    1 Thread(s)
    Quote Originally Posted by Lemon Juice View Post
    I'd have to use the same instance of the factory in every place, so how do I get to it from different scopes?
    You actually shouldn't need to get to it from different scopes. The factories (or your one central factory) should be handling all the creation and injection of dependencies. So if A depends on B, which depends on C, which depends on D, then the way it should play it is: the core of your application knows it needs A, so it asks the factory for it. The factory knows that A needs B, so the factory creates B. The factory knows that B needs C, so the factory creates C. And the factory knows that C needs D, so the factory creates D. Regardless of how deep this chain may go, none of the classes within that chain need to know about the factories. They just get their dependencies, with no knowledge about where those dependencies come from.

    If it would help, instead of talking about A, B, C, and D, you could post some real class names you're working with and their real dependencies, and we can work with that.
    "First make it work. Then make it better."

  9. #9
    SitePoint Guru bronze trophy TomB's Avatar
    Join Date
    Oct 2005
    Location
    Milton Keynes, UK
    Posts
    996
    Mentioned
    9 Post(s)
    Tagged
    2 Thread(s)
    Quote Originally Posted by Lemon Juice View Post
    Thanks. I have also found Dice - TomB's DI container. I think I'll have a hard time deciding
    If you have any specific questions about it, feel free to ask


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
  •