Design Patterns in Magento 2 – Factory Method

We’re pleased to present the second article in our series dedicated to Design Patterns in Magento 2. The goal of this series is to overview Magento 2 architecture with help of the design patterns explanation. The sequence of our posts follows the logical progression of patterns’ usage. So the theme of the current post was selected for a reason. In the first article, we described one of the established patterns in Magento 2 – Object manager. This particular article focuses on the Factory Method.

In Magento 2, Factory uses Object manager, and Object manager uses Factory. Before going further, we recommend reading the previous blog post that will help in forming a more solid understanding of both patterns and their applicability approach in Magento 2.

Content

  1. General Overview
  2. Generated Factories
    1. Factory Constructor
    2. Factory Create Method
  3. Factory Method Usage
  4. Summary

General Overview

We can assume that the Factory method is one of the most commonly used ones in creational design patterns. Such traction is based on the necessity of flexible object initialization by a contract (interface). According to the GOF (The “Gang of Four”: Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides) the Factory method is:

“The Factory Method pattern defines an interface for creating an object, but lets the subclasses decide which class to instantiate. The Factory method lets a class defer instantiation to subclasses, which is useful for constructing individual objects for a specific purpose without the requestor knowing the specific class being instantiated.

GOF – Factory Method Pattern.

“The Factory Method pattern defines an interface for creating an object, but lets the subclasses decide which class to instantiate. The Factory method lets a class defer instantiation to subclasses, which is useful for constructing individual objects for a specific purpose without the requestor knowing the specific class being instantiated.” – GOF – Factory Method Pattern.

Simply speaking, the software application has a defined contract for specific object initialization and Factory method is a tool that guarantees this contract’s fulfillment for different classes. This helps in decoupling of both objects and their construction. SOLID in all its beauty.

In Magento 2 the Factory method plays an essential role for all layers of the whole application. Let’s review this in more details in the next sections.

Generated Factories

The Data object is a part of the Service layer of the Magento 2 architecture and represents entities – such as Customer or Catalog.

According to their interface declaration, the Factory method is the best approach to generate new instances of those objects. But this means that for each Data entity interface, the developer has to create a separate factory implementation. Sounds like an overhead from the developer perspective. Doesn’t it? Magento 2 has a nice feature for such case, and we will review it in detail.

One of the core Magento 2 features is code generation. In the official documentation, there is an excellent article that explains the benefits of code generation — highly recommended reading. We will not dig too deep into this material, but will say that code generation simplifies Data object factory generation.
The magic of the factories’ generation can be clearly seen in the Magento\Framework\ObjectManager\Code\Generator\Factory. This class has a predefined structure with required parts of the factory. Let’s review this generator class and CustomerInterfaceFactory in detail.

Factory Constructor

The code below describes the factory constructor. It consists of two parameters: Object manager and class name of the Data object entity. That is all that Factory method implementation in the Magento 2 needs in the Magento 2.

    /**
     * Get default constructor definition for generated class
     *
     * @return array
     */
    protected function _getDefaultConstructorDefinition()
    {
        return [
            'name' => '__construct',
            'parameters' => [
                ['name' => 'objectManager', 'type' => '\\' . \Magento\Framework\ObjectManagerInterface::class],
                ['name' => 'instanceName', 'defaultValue' => $this->getSourceClassName()],
            ],
            'body' => "\$this->_objectManager = \$objectManager;\n\$this->_instanceName = \$instanceName;",
            'docblock' => [
                'shortDescription' => ucfirst(static::ENTITY_TYPE) . ' constructor',
                'tags' => [
                    [
                        'name' => 'param',
                        'description' => '\Magento\Framework\ObjectManagerInterface $objectManager',
                    ],
                    ['name' => 'param', 'description' => 'string $instanceName'],
                ],
            ]
        ];
    }

Here is an example of an automatically generated Factory:

    /**
     * Factory constructor
     *
     * @param \Magento\Framework\ObjectManagerInterface $objectManager
     * @param string $instanceName
     */
    public function __construct(
        \Magento\Framework\ObjectManagerInterface $objectManager,
        $instanceName = '\\Magento\\Customer\\Api\\Data\\CustomerInterface'
    ) {
        $this->_objectManager = $objectManager;
        $this->_instanceName = $instanceName;
    }

Factory Create Method

According to the design pattern name and its purpose, the Factory method implementation needs only one public method – create. In Magento 2’s create method, Object Manager creates a new instance of the requested object based on a class name and required list of arguments. We described the process of creation and the purpose of the Object manager in the previous article – Design Patterns in Magento 2 – Object manager

Factories create method in the Factory class generator combines from the next parts:

    /**
     * Returns list of methods for class generator
     *
     * @return array
     */
    protected function _getClassMethods()
    {
        $construct = $this->_getDefaultConstructorDefinition();

        // public function create(array $data = array())
        $create = [
            'name' => 'create',
            'parameters' => [['name' => 'data', 'type' => 'array', 'defaultValue' => []]],
            'body' => 'return $this->_objectManager->create($this->_instanceName, $data);',
            'docblock' => [
                'shortDescription' => 'Create class instance with specified parameters',
                'tags' => [
                    ['name' => 'param', 'description' => 'array $data'],
                    [
                        'name' => 'return',
                        'description' => $this->getSourceClassName()
                    ],
                ],
            ],
        ];

        return [$construct, $create];
    }

A $create array in the example above contains:

  • name of the method – create
  • method parameters – a not required data array that consists of initialized class properties and their values 
  • body – “the core” of the Factory: code responsible for the new object instance generation
  • Dockblock and tags – contains additional information

Let’s shift our focus on the body of the method and review it on the automatically generated example:

    /**
     * Create class instance with specified parameters
     *
     * @param array $data
     * @return \Magento\Customer\Api\Data\CustomerInterface
     */
    public function create(array $data = [])
    {
        return $this->_objectManager->create($this->_instanceName, $data);
    }

Code above shows that in Magento 2, the core of the Factory method is the Object manager’s usage together with the create method. It takes an instance name and combines it with some parameters in order to initiate a completely new object.

Factory Method Usage

Factory method usage is aimed at the new object initialization by following a specific contract of decoupling objects. Like in all other software applications architecture, Magento 2 uses such an approach for handling object generations. The most frequently used aspect is the Data object initialization on the service layer.

For example, some Data objects are declared in the Api/Data/Data-object-name-Interface. To generate a new instance of this object, Magento 2 provides the following algorithm: use Api/Data/Data-object-name-Interface + suffix Factory. This will tell the Magento 2 framework to use (or generate if not existing) a new factory class and will allow using it in the application. The inner structure of the factory has already been described above.

Regarding limitations, there are none from the Magento 2 perspective. The Factory Method is simple to use and doesn’t have any negative effects on application layers.

Summary

In summary, we want to say that Magento 2 architecture developed with Factory method usage, follows best practices in the OOP (Object Oriented Programming) world and SOLID principles. Automatically generated factories are the way the pattern is commonly used by platform for Data object initialization. The Factory method also allows decoupling objects and provides a layer between the business code and Object manager.

It is worth mentioning that automatically generated factories are not the only place where such approach is widely used. Our next article will be dedicated to the Command pattern, where we will highlight the importance of Factory method usage and other patterns to achieve a well-structured and flexible software architecture. Stay tuned to learn how the reviewed patterns (Object manager, Factory method) can do magic together with the Command pattern.