3

I have a custom framework where i have a class/method which uses my own Cache class.

Currently it is tightly coupled. So a method instantiates the Cache class like this:

public function someMethod ( )
{
    $cache = new Cache\HHCache();
}

I want ro remove the tight coupling but that's actually where i'm a bit stuck.

I thought it would be a good idea to create some sort of ServiceProvider class. But i'm not sure if this is really the right approach.

To start i have HHConfig file which has a static property in which a cache class is defined. In short:

class HHConfig
{
    static $_cacheClass = '\Core\Cache\HHCache';
}

So basically i have a class like this, which is part of the Core functionality of my framework:

interface IHHServiceProvider
{
    public function getService ( );
}

Then i have another class which implements this interface.

class HHCacheProvider implements IHHServiceProvider
{
    public static function getService ( )
    {
        $class = HHConfig::$_cacheClass;
        return new $class();
    }
}

So now someMethod can use the HHCacheProvider class to get an instance of a Cache class.

public function someMethod ( )
{
    $cache = HHCacheProvider::getService ( );
}

My IHHServiceProvider isn't really like the typical Provider class since you can't really register any Services to it. It simply looks in the HHConfig class what "class" to load and returns in instance of that.

So somehow this method doesn't feel right to me, but i do think it shows what i want to achieve. In what ways can i improve this?


Please note that i'm not looking for a simple Dependency Injection pattern for this. Because i don't want to inject my Cache class to every constructors class. I need a non tight coupling way of getting an instance of the HHCache class somehow from within a method.

Some sort of provider class that can be part of my framework seems like the right direction.

Prisoner
  • 27,391
  • 11
  • 73
  • 102
w00
  • 26,172
  • 30
  • 101
  • 147
  • Why *not* just inject a `CacheFactory` ? Whatever you do that doesn't involve injection you aren't reducing coupling, you're just shifting the coupling to some other relationship. – DaveRandom Apr 10 '13 at 13:15
  • @DaveRandom Yes i realise that, but as long as it can be a generic class that is part of the "core" of my framework then it isn't so bad. I want developers to be able to "overwrite" the default `HHCache` class with their own implementation. Using DI is of course the best way to reduce coupling. But i'm really looking more for a `Provider` like pattern. -- That way a "developer" can simply change the `HHCache` class in `HHConfig` and specify what `Cache` class to use instead. The Cache class in return must implement an inteface guarantueeing it has the needed methods ofcourse. – w00 Apr 10 '13 at 13:22
  • A ServiceProvider is in fact some kind of singleton and singletons are evil: http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/ So I strongly recommend DI or at least a factory method, maybe in a common base class or something. – Francois Bourgeois Apr 10 '13 at 14:15
  • Especially since it will be part of the framework, you should inject the Cache. A DI Container is the best solution here, you can config the actually Cache implementation as a singleton. Your proposed solution is tightly coupled to some service and hard to test in isolation. Actually it looks more of a service locator pattern rather than provider. It seems to me you are not a fan of a DI Container, can you tell us why? To me it's the most obvious and elegant solution – MikeSW Apr 11 '13 at 05:40
  • @MikeSW I think you (and the others above you) are right. Some sort of DI is the way to go, wether that is through some sort of Factory class or DIC. Could you (and the other if still reading) add it as an answer so i can accept something later on. – w00 Apr 11 '13 at 08:43

2 Answers2

2

Note: "provider" means nothing. There is not pattern by that name.

Instead of making some magical "privider", you should take a look at factory pattern. Basically the idea is a follows:

  1. You inject a factory in classes that will use some services (assuming that Cache is not the only form of service that you aim for).

  2. The class request from factory the service that it needs:

    • if service has been already initialized once, it just returns an instance to your
    • else it creates new instance, stores it and returns you to "consumer"

The simplest code example would be something like this:

class ServiceFactory
{
    private $storage = [];

    public function create( $name )
    {
        if ( false === array_key_exists( $name, $this->storage ))
        {
            $instance = new $name;
            $this->storage[$name] = $instance;
        }

        return $this->storage[$name];
    }

}

This is an extremely simplified example, but even in this case, if you inject an instance of this factory in any number of objects, they all will have access to same pool of instances.

If you ever decide to look into concept of DI Containers, then factories are also the place where it is appropriate to utilize them, without degrading them to as service locator anti-pattern.

.. and few lectures that you might find valuable:

tereško
  • 58,060
  • 25
  • 98
  • 150
  • what is the purpose of saving instance into storage? Is it only for the performance? Otherwise it looks like singleton pattern or in other words - looks like global state. – Andrew Oct 21 '14 at 08:38
  • @Andrew , please watch the second video that was linked. – tereško Oct 21 '14 at 10:49
  • I did. My question doesn't change. If I save object instance in the storage and use this factory inside one class (which may change object properties data) and then use same factory (and object) inside another class - I have global state, the object will cary it's data from one class to another. No separation. Not good. ? – Andrew Oct 21 '14 at 11:08
  • You have no idea what "global state" is. – tereško Oct 21 '14 at 12:24
  • ok, let it be. Bet lets not talk about me, lets talk about your answer. What are the advantages (besides the performance) of storing instance in the storage? – Andrew Oct 21 '14 at 12:53
  • @tereško: Andrew is right. Your factory is more like a DI-Container. A gereric factory object creates fresh objects and just returns but does not save them. – bloodstix Mar 11 '16 at 12:55
1

As per OP request

Especially since it will be part of the framework, you should inject the Cache. A DI Container is the best solution here, you can config the actually Cache implementation as a singleton. Your proposed solution is tightly coupled to some service and hard to test in isolation. Actually it looks more of a service locator pattern rather than provider.

If you're using a Factory that won't replace the DI Container. THe point of DI is that the code shouldn't be coupled to an outside static service. Unless you have a very good reason, any object should use only the injected (via constructor or as method argument) dependencies.

MikeSW
  • 16,140
  • 3
  • 39
  • 53