Design Patterns. Part 1: Creational and Structural Patterns.
About design patterns, all programmers have heard. Although... Based on the number of jokes about php developers and seeing some pieces of code, maybe some of them (php developers) have no idea what it is.
But we are adults, so it's time for a serious conversation. Let's talk about the patterns that are used in php development and are found in existing frameworks. Generally, I wrote this note more for myself, so as not to forget, the repository was used as a basis here. Plus, you can also read a more fundamental work (this is a book that contains descriptions of 23 classic patterns).
I will describe the patterns not in alphabetical order, but as it seemed more logical to me.
Do not confuse so-called architectural patterns with design patterns. The latter is a particular case of the former. Examples of architectural patterns: MVC, HMVC, MVP, MVVM, etc. They are not the subject of this note.
Oftentimes, a more complex pattern may include one or more simpler ones.
First, about factories.
Factories are intended for creating objects (of a different class than the factory itself (just in case)).
Simple Factory. Suppose we have classes for a bicycle and a scooter, which implement some common interface. The concrete factory class ConcreteFactory knows about all the objects it can create (constructor code) and implements a method to create an object by alias (createVehicle($type)).
Factory Method. This is a variation on the previous pattern: what if different types of objects need to be created differently? The FactoryMethod class again contains a method for creating create($type), but, unlike in the previous examples, the class is abstract. The create($type) method calls the unimplemented method createVehicle($type), which must be implemented in descendants (for example, GermanFactory).
Abstract Factory. The next complication, when we not only do not know how to create an object, but also what object to create in general. (The AbstractFactory class does not contain an implementation of the creation method, the methods are implemented in the descendants (for example, JsonFactory)).
Note that in the last two patterns, the base factory classes no longer know about all the possible objects that can be created, this is already decided by the descendants.
Static Factory. In general, static is bad, there's no need to say much about it, this pattern in terms of meaning is very similar to Simple Factory (although the implementation is different), but all possible objects are created in one single static method of the factory class.
We're done with factories =) Next:
Builder. Designed for building complex objects. As can be seen from the example, builders for different objects implement the same interface, and each one contains methods for building the corresponding object. But the builder does not build the object itself, this is done by the Director, the build method creates and returns the desired object.
Prototype. The essence is very simple: instead of creating an object, we call the cloning operator. They say that it is beneficial to use this for representing a set of records, for example, in ORM. But since profiling showed that creating is faster than cloning (example for profiling), I see no need for a detailed consideration of the pattern. (I suspect that new versions of php have optimized object creation, tested on versions 5.6.8, 5.4.4)
Creating 6000000 new objects... 6.680617 s Cloning 6000000 objects........... 8.660648 s
Apparently, it makes sense to use the pattern only if there is heavy logic in the constructor that is not used during cloning, for example, if field values are filled in the constructor and we can simply copy them.
Pool. Designed for performance optimization: instead of creating and deleting objects, they are put/taken from the pool. It makes sense to use it if objects take a long time to initialize.
Adapter. Used to adapt the use of one class by another without changing the code of the used class. In the example, the adapter for an e-book (EBookAdapter) implements the PaperBookInterface interface, thus "adapting" the e-book for use as a paper book.
Bridge. The implementation is very similar to the previous one, but the meaning is slightly different. Used to separate abstraction from implementation. As already mentioned, the adapter is used to adapt existing code to another interface, while the bridge is useful if different implementation options are planned (example: Symfony Doctrine Bridge).
Composite. A relatively simple pattern used to process a set of objects in the same way (in the example, the render method is called). You can imagine it (primitively, for yourself) as a set of objects, which are iterated through and the same method is called for each such object. Example - rendering forms in Symfony.
DataMapper. Used as a layer that performs bidirectional data transfer between the application and storage. But, unlike ActiveRecord, the class representing the data does not know what to do with it. This is handled by another class (UserMapper in the example). Example: Doctrine ORM.
Decorator. Used if you need to dynamically add new functionality to an instance. An important point is that classes that extend functionality according to their logic must implement the same interface as the decorator itself. In the examples, the decorator's methods first call the methods of the decorator, and then do additional processing.
Dependency Injection. Let's say we have a certain class (Connection, following the example). For the operation of its instance, an object Parameters is needed. Obviously, we could simply create it and configure it in the Connection constructor. But according to the Dependency Injection pattern, to inject a dependency, that is, to inject an instance, Parameters into Connection, a third entity is needed (Service Container, for example). It is used for greater flexibility of applications and makes the code more logical, if used properly, it reduces memory consumption. For example, Parameters can exist in a single instance, instead of creating its own in each object. Example - Symfony Service Container
Facade. Designed to simplify manipulation with an object, or, in other words, to hide the implementation, to provide a convenient interface for the end user of the class/library.
FluentInterface. Makes the code readable and intuitively understandable. Implements a chain of calls. For this, each called method returns $this. Examples: Symfony Query Builder, jQuery.
Proxy. A fairly common technique. For example, you just want to extend a class (Record), but at the same time the logic of the parent should be executed. To do this, in the RecordProxy, simply override the method and also call the parent's method.
Registry. Implements a storage. It's simple: there are methods set($key, $value) and get($key) for storing and retrieving instances by key.