The factory pattern
The factory pattern is one of the most common design patterns used to create objects. According to the degree of abstraction, it can be divided into simple factory pattern, factory method pattern and abstract factory pattern.
Simple factory mode
define
The simple factory pattern is also known as the static factory method pattern. A factory object determines which instances of a product class are created.
The class diagram
I decided to quit the front end and went home to open a Starbucks coffee shop. When a customer came to my coffee shop and ordered a cup of coffee, he told me what kind of coffee he wanted, and I created the coffee according to the customer’s decision. As it was a small business, I did everything myself.
Coffee: Coffee
Subcategories: AmericanCoffee, LatteCoffee, CappuccinoCoffee.
code
// Abstract classes can only be used to inherit coffee classes
abstract class Coffee {
/* Add the public modifier to the argument for shorthand purposes without declaring name: this. Name =name */ is not used in the string constructor
constructor(public name: string){}}// Subclass Americano
class AmericanCoffee extends Coffee {}
// Subclass lattes
class LatteCoffee extends Coffee {}
// Subclass cappuccino
class CappuccinoCoffee extends Coffee {}
Simple factories return instances of different subclasses based on the parameters passed in
class CafeFactory {
static order(name: string) {
switch (name) {
case 'American':
return new AmericanCoffee(Americano);
case 'Latte':
return new LatteCoffee('Latte');
case 'Cappuccino':
return new CappuccinoCoffee('Cappuccino');
default:
throw new Error('There is no such coffee.'); }}}// Call the factory function to test
console.log(CafeFactory.order('American')); //AmericanCoffee {name: AmericanCoffee}
console.log(CafeFactory.order('Latte')); //LatteCoffee {name: "latte "}
console.log(CafeFactory.order('Cappuccino')); //CappuccinoCoffee {name: "CappuccinoCoffee "}
console.log(CafeFactory.order('meinianda')); //Uncaught Error: no such coffee is available
Copy the code
This is a simple factory that returns instances of different subclasses based on the parameters passed in.
advantages
With just one correct argument, you can get the object you need without knowing the details of how it was created.
disadvantages
If there are too many types of products, the switch case judgment will become too many, and this function will become very bloated and difficult to maintain.
If you want to add or delete a product category, you need to modify the judgment logic code, which does not conform to the open-close principle
Therefore, simple factories can only be used when the number of objects created is small and the object creation logic is not complex.
The sample
The $(selector) in jQuery source code is a simple factory
$(‘div’) $(‘div’) $(‘div’) $(‘div’) $(‘div’) $(‘div’
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.prototype.init( selector, context );
};
Copy the code
Implement a simple factory function modeled after jQuery
// Union type
interface jQuery {
[index: number] :any;
}
class jQuery {
length: number;
constructor(selector: string) {
// Get the DOM element and use array. from to turn the class Array into an Array
let elements = Array.from(document.querySelectorAll(selector));
let length = elements ? elements.length : 0;
this.length = length;
for (let i = 0; i < length; i++) {
this[i] = elements[i]; }}html(htmlText: string | undefined) {
// If the parameter is passed, it is assigned, otherwise it is valued
if (htmlText) {
for (let i = 0; i < this.length; i++) {
this[i].innerHTML = htmlText; }}else {
return this[0].innerHTML; }}}interface Window {
$: any;
}
// A simple factory is an instance of a class returned by a function
window$=function (selector: string) {
return new jQuery(selector);
};
Copy the code
Factory method pattern
define
The factory method pattern is also known as the polymorphic factory pattern. In the factory method pattern, the core factory class is no longer responsible for all product creation, but the factory subclass does the specific creation.
The factory method pattern is intended to defer the actual creation of objects to subclasses, so that the core class becomes abstract. We can think of a factory method as a factory class that instantiates an object.
The class diagram
Opening a shop means making more money than doing anything. With the expansion of the scale and variety of Starbucks, and the increasing number of customers, I was too busy for myself at this time, so I decided to settle down as the boss and collect the money. So I took care of several waiters, each responsible for one variety. Customers place orders with me, and THEN I assign them to corresponding waiters for production.
And a simple factory
In the simple Factory pattern, the product is created by the Factory.
In the Factory method pattern, instead of the Factory Factory creating the product, the concrete Factory is created first, and then the concrete Factory creates the product.
code
//Description: The factory method pattern assigns the task of creating the product to the specific factory class
// Abstract classes can only be used to inherit coffee classes
abstract class Coffee {
constructor(public name: string){}}// Subclass Americano
class AmericanCoffee extends Coffee {}
// Subclass lattes
class LatteCoffee extends Coffee {}
// Subclass cappuccino
class CappuccinoCoffee extends Coffee {}
// Abstract coffee factory class
abstract class CafeFactory {
// Abstract methods do not need implementation
abstract createCoffee(): Coffee;
}
// Americano Factory
class AmericanFactory extends CafeFactory {
createCoffee() {
return new AmericanCoffee(Americano); }}// Latte factory
class LatteFactory extends CafeFactory {
createCoffee() {
return new LatteCoffee('Latte'); }}// Cappuccino factory
class CappuccinoFactory extends CafeFactory {
createCoffee() {
return new CappuccinoCoffee('Cappuccino'); }}let americanFactory = new AmericanFactory();
console.log(americanFactory.createCoffee()); //AmericanCoffee {name: AmericanCoffee}
let latteFactory = new LatteFactory();
console.log(latteFactory.createCoffee()); //LatteCoffee {name: "latte "}
let cappuccinoFactory = new CappuccinoFactory();
console.log(cappuccinoFactory.createCoffee()); //CappuccinoCoffee {name: "CappuccinoCoffee "}
Copy the code
You can also combine simple factory patterns to simplify your code
// Combine the simple factory mode
class Factory {
static order(name: string) {
switch (name) {
case 'American':
return new AmericanFactory().createCoffee();
case 'Latte':
return new LatteFactory().createCoffee();
case 'Cappuccino':
return new CappuccinoFactory().createCoffee();
default:
throw new Error('There is no such coffee.'); }}}// Call the factory function to test
console.log(Factory.order('American')); //AmericanCoffee {name: AmericanCoffee}
console.log(Factory.order('Latte')); //LatteCoffee {name: "latte "}
console.log(Factory.order('Cappuccino')); //CappuccinoCoffee {name: "CappuccinoCoffee "}
console.log(Factory.order('meinianda')); //Uncaught Error: no such coffee is available
Copy the code
advantages
Factory method pattern each specific factory class only completes a single task, the code is concise, if you want to add a product category, only need to add a product factory, in line with the open-closed principle, has very good scalability.
disadvantages
If a specific product class needs to be modified, the corresponding factory class will most likely need to be modified. When multiple product classes need to be modified at the same time, the modification of the factory class can become quite cumbersome.
For each additional product, a corresponding plant is added, increasing the amount of additional development.
The sample
A factory method used to generate dom, modeled after the React source code
function createElement(type: any, config: any) {
// This is bound to null and the first argument is bound to type
return { type.props: config };
}
function createFactory(type) {
// This is not used in the source code, so this is bound to null
const factory = createElement.bind(null.type);
return factory;
}
let factory = createFactory('h1');
let element = factory({ id: 'h1'.className: 'title' });
Copy the code
Abstract Factory pattern
define
The abstract factory pattern, also known as the factory of other factories. The client can be provided with an interface that allows it to create product objects in multiple product families without specifying the specifics of the product
Start with a diagram to understand product families and product levels
My cafe has three types of coffee: American, latte and cappuccino. Since I wisely gave up the front end and chose Starbucks and made a lot of money, I decided to open another coffee shop called Luckin. The category of coffee is the same as Starbucks.
Product level: the inheritance structure of products, which can be understood as the same product of different families.
Product family: a group of products produced by the same factory, located in different product hierarchy, can be understood as different products of the same house, is a group of related or interdependent objects.
Role of
Abstract factory: Provides an interface for creating products, including multiple abstract methods for creating products (coffee factory)
Concrete factory: Implement the interface defined by the abstract factory to complete the creation of a concrete product (Starbucks factory and Luckin factory)
Abstract product: The definition of the product. The abstract factory contains as many methods to create the product as there are abstract products (Americano, latte, cappuccino).
Concrete products: Implementation of abstract products (Starbucks Americano, Luckin Latte)
The class diagram
The CafeFactory consists of three abstract methods for making coffee. The starbucks factory and luckin factory produce their own Americano, latte and cappuccino, respectively.
Differences with the factory method pattern
The factory method pattern is for the same class or level of products, while the abstract factory pattern is for multiple product designs
code
// An abstract class can inherit from an abstract class
abstract class Coffee {}
// Abstract products
abstract class AmericanCoffee extends Coffee {}
abstract class LatteCoffee extends Coffee {}
abstract class CappuccinoCoffee extends Coffee {}
// Number of specific products = product family * product grade
class StarBucksAmericanCoffee extends AmericanCoffee {}
class StarBucksLatteCoffee extends LatteCoffee {}
class StarBucksCappuccinoCoffee extends CappuccinoCoffee {}
class LuckinAmericanCoffee extends AmericanCoffee {}
class LuckinLatteCoffee extends LatteCoffee {}
class LuckinCappuccinoCoffee extends CappuccinoCoffee {}
// The abstract factory requires three abstract methods
abstract class CafeFactory {
// The abstract method to create americano
abstract createAmericanCoffee(): AmericanCoffee;
// The abstract method creates the latte
abstract createLatteCoffee(): LatteCoffee;
// Create cappuccino with abstract method
abstract createCappuccinoCoffee(): CappuccinoCoffee;
}
// The specific factory of Starbucks
class StarBucksCafeFactory extends CafeFactory {
// How to create a Starbucks Americano
createAmericanCoffee() {
return new StarBucksAmericanCoffee();
}
// How to create a Starbucks Latte
createLatteCoffee() {
return new StarBucksLatteCoffee();
}
// How to create a Starbucks cappuccino
createCappuccinoCoffee() {
return newStarBucksCappuccinoCoffee(); }}// Specific factory luckin
class LuckinCafeFactory extends CafeFactory {
// How to create a Starbucks Americano
createAmericanCoffee() {
return new LuckinAmericanCoffee();
}
// How to create a Starbucks Latte
createLatteCoffee() {
return new LuckinLatteCoffee();
}
// How to create a Starbucks cappuccino
createCappuccinoCoffee() {
return newLuckinCappuccinoCoffee(); }}// Create luckin factory
let luckinCafeFactory = new LuckinCafeFactory();
// Create Luckin's Americano
console.log(luckinCafeFactory.createAmericanCoffee()); //LuckinAmericanCoffee {}
Copy the code
advantages
When the system needs to add a new product family, only need to add a new factory class, without modifying the source code;
disadvantages
However, if a new class of products is added to the product family, all factory classes need to be modified
conclusion
Whether it’s a simple factory pattern, a factory method pattern, or an abstract factory pattern, they essentially take the immutable parts and leave the mutable parts as interfaces for maximum reuse. Which design pattern is more appropriate depends on the specific business needs. Don’t learn how to use a knife and cut everything in sight.
If there is any omission or error in the text, please give advice.
reference
Web front-end – JS design pattern
The pros and cons of the big Three factory model