Three more design patterns, extending from a Laracon EU talk two years ago
Level 50 on Laracasts!
Works at Enrise
Let's talk design patterns!
"What are design patterns?" Solutions for commonly-occurring problems. Building blocks
Laravel has a lot of design patterns (e.g. factory, presenter, singleton, bridge, repository)
Last year, spoke about Factory, Builder and another that I missed
Today, Singleton, Observer, Bridge patterns
Singleton Pattern
This is a creational pattern. You don't care about how it's created, just that it IS created.
Start with an example. Examples are going to be food-based..."raise your hands if you've made a pizza. raise your hands if you use a new oven every time you make a pizza"
"Every bite, you use a knife and fork. You don't get a new knife and fork for every bite"
Ensure that a class has only one instance, provides global access to that instance
Saves on system resources, share data.
Example is class PizzaOven
. Only need one pizza oven!
How is it used in Laravel? DatabaseServiceProvider for db.factory/ConnectionFactory, db/DatabaseManager. Laravel handles it for us. DatabaseManager class doesn't indicate that it's a singleton
In Illuminate/Container/Container.php, singleton binds abstract, concrete, and shared (true)
If concrete isn't a closure, concrete is the closure given the abstract and concrete
Abstract binding is an array
When resolving an abstract thing in the container, Get the alias, and if it's already set, then return it. Otherwise, gets a concrete, instantiate an instance, register as a singleton, then return the object
Another example: LogServiceProvider
When you use dependency injection, it asks if the class already exists, and if not, it creates it and gets you squared away
Observer Pattern
AKA publish/delegation patterns
Increases communication between objects
Examples:
- Whenever you order a pizza online, you get a notification that the order was submitted, and the restaurant gets a notification for cooking the pizza
- I want a pizza. It's ready! Person says "hey, it's ready". You only act when the pizza's ready, not ask every minute if the pizza's ready
Defines a one-to-many dependency between objects so that when one object's state changes, all its dependents' listeners (subscribers) are notificated and updated automatically
Observable (the subject) and Observer (the listener)
<uml diagram describing Observable interface with observers and add/notify methods>
StartProcessOrder. add pushes a new observer, notify loops through the observers and calls their update method
We've probably already seen this when using events and listeners
$orderProcess = new StartProcessOrder();
$orderProcess->add(new SendDeliveryTime());
$orderProcess->add(new NotifyBaker());
$orderProcess->notify();
In Laravel, EventServiceProvider has a listen property with keys as events and value arrays as what should get processed
Dispatcher clause has listen function with events and listener. Handles wildcard listen calls!
Stores callback in container. Laravel stores in container, so also Singleton!
How does Laravel trigger this when something happens? We have an event helper (or can call dispatch)
// Example
event(new Registered($user));
Also used for model events (e.g. creating, created, updated), uses that same dispatch clause
Bridge Pattern
This is a structural pattern, describes inheritance and relationships between objects. Solves complex composition
Examples:
- Let's say we have a product and promotions. Pizza and Black Friday Sale. Chicken Wings and Valentine's Day. Can independently work together (can do a Valentine's Day promotion on pizza)
They're unique, but they can work together or independently
Decouples an abstraction from its implementation so that the two can vary independently
UML:
- Product has implementation.
- Pizza, ChickenWings implements Product
- Promotion has its own methods, then BlackFriday and ValentinesDay implements Promotion
- Product can relate to Promotion
Pizza and ChickenWings can have their own methods.
Promotion the abstract class exposes applyDiscount(float $Price), while each promotion calculates
$product = new ChickenWings(new BlackFriday, 12.50, 'Wings');
$product->applyDiscount();
How does Laravel use it? Migrations!
MysqlBuilder implements Builder
MySqlGrammar, SqlServerGrammar, PostgresGrammar, SqliteGrammar all implement Grammar (compileTableExists, compileEnableForeignKeyConstraints)
Builder.php constructor sets grammar by getSchemaGrammar() on the passed-in connection
Builder may throw a LogicException in its method of dropAllTables, but MyqlBuilder can override that with its own implementation. SQLiteBuilder can have its own.
Looks like Adapter pattern, but bridge pattern is designed up-front to let the abstraction and implementation vary independently.
Recap:
- Singleton Pattern: Don't get a new pizza oven for each new pizza you make. Instantiate a database connection once.
- Observer Pattern: When you place an order, notify the restaurant and yourself. Events
- Bridge Pattern: Have promotions and products separate, but optionally related. Database builder
No silver bullet, learn by doing and trying
Writing a book! laravelsecrets.com @stefanbauerme @bobbybouwmann