This article is part of a series on design patterns for framework independent code, especially to write extensions for Magento 1 and Magento 2 alike.


All Parts:


You have probably heard of the concept of Dependency Injection, but there is lots of confusion about what it is and what it isn’t, so I’ll clarify what I am talking about:

Basically, Dependency Injection means that if your classes depend on others, they don’t create these objects themselves, but receive them from a higher level object, either as constructor parameter (“constructor injection”) or with a setter method (“setter injection”).

So instead of

We have

This way it decouples object creation from object usage, making components more flexible and testable.

What Dependency Injection IS NOT:

  • Dependency Inversion: This principle is the “D” in “SOLID” and states
    1. High-level modules should not depend on low-level modules. Both should depend on abstractions.
    2. Abstractions should not depend on details. Details should depend on abstractions.

    Simplified, this means “program against an interface, not a concrete implementation”. In our example above, the WorkDay would be an interface and whoever instantiates an Employee object decides which WorkDay implementation will be used.

  • Inversion of Control (IoC) Container: This is a related, but different pattern, where a container object manages all objects with injected dependencies. It often doubles as a Service Locator based on interfaces, i.e. a global registry and factory of objects. You can ask the container for an interface and it creates a concrete object, or returns a global instance. The ObjectManager in Magento 2 is such a container. And the methods Mage::getModel(), Mage::getSingleton() etc. of Magento 1 are Service Locators without IoC. You can also have IoC containers without this global antipattern, Spryker for example has one distinct container per module.
  • XML or Annotation Magic: This is an implementation detail of IoC containers, where the definition, which classes to use, is a configuration, somewhere outside the code.

Those patterns and techniques use Dependency Injection but it’s not like they are married. So this is not what I am going to focus on here.

How I use it

I prefer constructor injection, as you might know it from Magento 2, mainly for these reasons:

  1. I don’t want my objects to be in an invalid state at any time. Especially I don’t want to even think about “Call to a member function on a non-object” errors.
  2. Once initialized, I don’t want the dependencies to be exchanged. This makes the code more predictable.

Now instead of using some container that would bind me to a framework again, I just create my objects as I need them explicitly, for example:

where I usually put code that creates objects with “new” into classes, that are only responsible for object creation, using creational patterns (see below). A possible exception are simple value objects, where the creation does not need to be separated:

Value Object – “a small object such as a Money or date range object. Their key property is that they follow value semantics rather than reference semantics. […] value objects should be entirely immutable. If you want to change a value object you should replace the object with a new one and not be allowed to update the values of the value object itself” — Martin Fowler on Value Objects

Note: Value objects don’t have their own behavior and there won’t ever be the need to replace or mock them. Otherwise, they are probably not value objects.

Creational Patterns: Factory

A factory is used to create complex objects of a certain type, and sometimes it’s also used to decide which concrete implementation of this type (interface) to choose.

Example

If you need to create object hierarchies with different implementations, you can also use the Abstract Factory pattern:

Abstract Factory – “Provide an interface for creating families of related or dependent objects without specifying their concrete classes.” — Definition in the “Gang Of Four” Design Patterns

Creational Patterns: Builder

Remember, how I wrote before that, once initialized, I don’t want the dependencies to be exchanged? We can take this even further towards a concept called Immutable Objects, where objects don’t change its state after they are initialized. There’s a good read on it on Yegor Bugayenko’s blog and although it refers to Java, most arguments hold true for PHP as well. An example for immutable objects are the Configuration Value Objects from the first part of this blog series.

But what if we don’t have all parameters available at once? When dealing with immutable objects, I found the Builder pattern extremely useful.

Builder – “Separate the construction of a complex object from its representation. By doing so the same construction process can create different representations” — Definition in the “Gang Of Four” Design Patterns

A Builder contains one or several default parameters for the object it is going to build and allows to change them with setters until the object is actually created, using the build() method.

Example:

This builder can be used like this, to use the default configuration:

Or like this to use a different host and port:

But it shows its strength if you don’t instantiate it immediately. You can instantiate it without building the object right away:

Then make it available with a getter and configure it from outside:

And only build the object as soon as you need it:

Note that $solrService is never initialized with an invalid or incomplete state because the builder contains all default parameters. If you need additional validation, the builder is also a good place for it. Builders can contain other builders to configure related objects that will be created alongside. The build() method then also calls the build() methods of the associated builders.

By the way, I also use builders to create test data, as described here (a good, practical introduction to test data builders).

That’s cool, I hear you saying, but how do you actually use your fancy AbstractFactoryFactoryBuilders™ in a Magento extension? The next part covers the connection between library and module again, introducing the Bridge Pattern.

Fabian Schmengler

Author: Fabian Schmengler

Fabian Schmengler is Magento developer and trainer at integer_net. His focus lies in backend development, conceptual design and test automation.

Fabian was selected repeatedly as a Magento Master in 2018 based on speaking engagements, active participation on StackExchange and contributions to the Magento 2 core.

More Information · Twitter · GitHub