The patterns that will be discussed first, following the original GoF Design Patterns book approach, are the creational ones. Creating object graphs is an essential skill in object-oriented development and correctly addressing the bootstrap of the application and the object instantiation is part of the programming's complexity.
The first pattern we will introduce is the Abstract Factory one, also known as Provider or simply as Factory, in its variant which does not include explicit interfaces.
The major problem that creational patterns try to solve is that objects need collaborators: we often pass them in the constructor of a Client class to aid decoupling, as every class should know only what it really needs to get its job done. With the verb know I mean that they just know that the other part exist at all.
The fundamental principle of asking for things (Dependency Injection) substitutes the naive approach of looking for things or creating them.
A Client is simply the generic name of an high-level class which makes use of the collaborators, and occurs frequently in the design patterns Ubiquitous Language. These generic names are known as participants in a pattern, and allow developers to discuss a pattern without referring to a particular implementation.
Dependency Injection prescribes to ask for collaborators in the constructor of a Client, or via setters that accepts the collaborator as a parameter to store it for later use. Most of the times collaborators are created at the startup of the application/script, before their Client, to satisfy the dependency.
The fallacy here (which is not really a fallacy, since it works most of the time) is evident when the collaborators assume shorter lifecycles. Often objects are created as a result of business logic (a Post or Forum instance, for example, in the middle of a php script), and by the way with a shorter lifecycle of the object that creates them. As another example, they can be lazily created only when necessary.
In php's case, lazy creation is mandatory because there is no need to create all kinds of helper objects on every single http request. You certainly do not expect to define all your buttons, links and inputs at the startup in a single object graph: their creation is regulated by an analysis of the request parameters and of the database state.
Though, you may want to govern which kind of objects are created: if the names of the ConcreteProduct classes are hardcoded in the Client, you lose the flexibility of Dependency Injection, which provide advantages like real unit testing and decoupled design.
The AbstractFactory solution consists in encapsulating the creation process behind an AbstractFactory interface, which may be implemented by how many different ConcreteFactory classes are needed. One of this ConcreteFactory is then injected in the Client that will call its methods to create the objects, when ready.
- AbstractProduct: the interface for the collaborator
- ConcreteProduct: an implementation of the collaborator
- AbstractFactory: the interface for the factory that builds an AbstractProduct
- ConcreteFactory: an implementation of the AbstractFactory, to be injected in the Client
- Client: the final user class of the collaborator
I hope you now grasp how to build objects at the right time in your application, as lazily as you want, without depending on concrete classes.
Tomorrow we will discuss another creational pattern, the Builder one.
Don't forget to comment if you like the post or you feel something is missing. Feedback can drive the evolution of this series of articles.