6

Today i updated my project and i got this warning :

Deprecated: ServiceManagerAwareInterface is deprecated and will be removed in version 3.0, along with the ServiceManagerAwareInitializer. Please update your class X to remove the implementation, and start injecting your dependencies via factory instead

I have some main Base classes that implements the ServiceManagerAwareInterface and multiple classes that extends these base classes.

So is it a good practice to create on extra factory class for each of these classes ? or is it better to use 1 AbstractFactory for each module and initiate the classes in there ?

Dose using the AbstractFactory impact performance ?

Whats the best practice for injecting a single(or 2) shared dependency in many classes?

UPDATE : Even tho that i accepted @AlexP's answer but i have some concerns about providing dependencies throw constructor. Imagine this scenario : I have a controller with couple of actions, lest say ActionA needs ServiceZ and ActionB needs ServiceY and ServiceX, and ServiceX is also depends on ServiceM and ServiceN. Now every time i am calling ActionA my controller in initiated with all of these services, but ActionA only needs 1 service where my controller is loaded with 5 services.... is this a good practice ? is this the right way do to it ? wouldn't this have bad performance, since on every request services are being initiated that we won't use at all during that request ?

Right now i am allowing each service/controller to take care of its own needs and load services when it needs them.

This way i won't have to initiate multiple services that i wont use and i don't need o know the services dependencies to use them. I know this is not accepted as best practice but the code is clean and i prefer to sacrifice "Best Practice" to have better performance.

Appreciate anyone's input on this.

edigu
  • 9,380
  • 5
  • 53
  • 77
Exlord
  • 4,250
  • 2
  • 26
  • 38

1 Answers1

5

A factory per service is normally an ideal goal; however there are many cases when you have similar classes that have similar dependencies and creating that number of factories for each one would be unnecessary and difficult to maintain.

An abstract factory solves the multiple factories issue by matching each service it can create by name and then return a new instance using some custom configuration.

This introduces some issues.

  • Calls to $serviceManager->get() or $serviceManager->has() will require the abstract factory to check it can create the service using its canCreateServiceWithName() method; with multiple abstract factories this could add a considerable performance overhead.

  • The abstract factory has no concept of a service 'alias'; functionality offered by the service manager. The implementation requires you match on the $requestedName of the service. This is an issue if you have calls for services using both the the full service name and an alias.

  • Abstract factories are tightly coupled to the ZF2 framework.

The roadmap for the framework is very much focused on addressing these issues, ZF3 still offers the abstract factories however it also introduces some improvements in factory design to encourage reusability which you can already take advantage of.

Note that the factory now accepts an additional required argument, $requestedName; v2 already passed this argument, but it was not specified in the interface itself. Because factories now can expect to receive the service name, they may be re-used for multiple services, largely replacing abstract factories in version 3.

So we can already use standard factories multiple times for similar services (in ZF2).

A very simple example on how to create similar service with one factory.

'service_manager' => [
    'factories' => [
        'My\Service\Foo' => 'My\Factory\SharedFactory',
        'My\Service\Bar' => 'My\Factory\SharedFactory',
        'My\Service\Baz' => 'My\Factory\SharedFactory',
    ],
],

namespace My\Factory;

class SharedFactory
{
    public function __invoke($serviceLocator, $name, $requestedName)
    {
        if (! class_exists($requestedName)) {
            throw new ServiceNotCreatedException("$requestedName could not be found!");
        }

        return new $requestedName(
            $serviceLocator->get('SomeDependacy1'),
            $serviceLocator->get('SomeDependacy2')
        );
    }
}

You could easily extend this to load custom service configuration using the $requestedName argument to add even greater flexibility.

AlexP
  • 9,808
  • 1
  • 21
  • 41
  • Tanks for the answer, didn't know that the factories provide the service name, i updated the question, take a look if u have the time. – Exlord Mar 21 '16 at 08:06
  • 2
    Your update is really a new question of its own. Constructor injection can load a massive object graph into memory, which is wasteful. You can reduce the overhead with fewer dependencies, making your classes more specific e.g a controller with one action method. This means more factories however. Another option is [Lazy services](http://framework.zend.com/manual/current/en/modules/zend.service-manager.lazy-services.html) which help by replacing expensive object instantiation with proxy objects that return the real object when used. – AlexP Mar 22 '16 at 00:09