GoF introduces 23 design patterns. Do all of these design patterns apply to JavaScript? The answer must be no.
Some years ago, I made a tool based on electron for company development, which needs to interact with C#. At first, I used node-ffi, and then changed node child_process, I don’t know if it is correct. Recently to add functions, found that the code written before is really not easy to expand. I did a couple of refactoring, but it didn’t work as well as I expected, so I checked out some books, including “Javascript Design Patterns and Development Practices”, which describes 16 specific patterns that are suitable for the Javascript language. This book is 15 years of revision, by now should be considered ancient bar, but most of the contents of the book is more practical, but also the upward crowd can read a book.
The book lists:
- The singleton
- strategy
- The agent
- The iterator
- Publish subscription (not inGoFWhich some consider to be consistent with the observer)
- The command
- combination
- Template method
- The flyweight
- Chain of responsibility
- broker
- A decorator
- The state machine
- The adapter
There are 14 modes in total, of which 10 have not been introduced, they are:
- The prototype
- The factory
- The abstract factory
- The builders
- The bridge
- appearance
- The observer
- The visitor
- The memo
Let’s start with patterns we haven’t included:
1. Prototype
As we all know, Javascript itself is a prototype-based transformation language, and all objects are design patterns based on prototype chains, so the authors don’t need to include them, and we don’t really need to elaborate on them.
2. Factory Mode
For strongly typed validation languages, there needs to be an obvious object creation process, and the factory pattern is designed for this.
When we need to build a complex object, we need to use the factory pattern. One is to reduce the amount of template code for the same object creation process, and the other is to reduce the frequency of using the new keyword on the page, so that the code is business-focused, semantically readable, and has less code to modify if the object is subject to major future creation process changes.
But in the case of JS, the flexibility of JS ensures that we rarely need to create complex objects, and therefore rarely have the schema to build objects.
Common programming languages will use the noun Factory as a suffix for Factory patterns, such as the famous BeanFactory.
3. Abstract Factory
Abstract factories are called factories of factories.
Instead of producing a single object in a typical factory, multiple objects can be created in an abstract factory. There is no necessary relationship between each object, but there is a certain relationship with the subject. Generally, drawing will be used as an example to illustrate the problem.
When we use Ps or other tools to paint, there will be a series of painting tools such as colors and shapes, which are generally prepared for painting. The shapes are rectangles, circles and other patterns, and the colors are red, green and other colors. Using an abstract factory allows them to be grouped together, avoiding the need to find different tools in a decentralized manner.
Here is a pseudocode:
DrawToolsAbstructFactory{ Shape getShape(); Color getColor(); } ShapeFacotory extends DrawToolsAbstructFactory { Shape getShape(){ ... return Shape } } ColorFactory extends DrawToolsAbstructFactory{ Color getColor(){ ... return Color } } Producer { DrawToolsAbstructFactory getFactory(typename){ if(typename=='shape'){ return ShapeFacotory } if(typename == 'color'){ return ColorFactory } } } Producer.getFactory('color').getColor();Copy the code
The advantage of abstract factories is cohesion, but the disadvantage is also obvious, is not extensible, every time you add a type, you need to update all factory subclasses, so it is not suitable for more complex scenarios. Nature is not needed for flexible JS.
4. The Builder
The builder model is somewhat similar to the factory model, which produces the same type of object, equivalent to an assembly line. A builder is a custom model that produces an object, the equivalent of a geek.
Take the mainframe computer: the mainframe produced in the same factory mode is the same, the same configuration and the same size. The build model can be combined freely, and can be given recommended options, but ultimately it is up to the different parts of the build — different cases, power supplies, motherboards, graphics cards, cpus, and ultimately a different configuration of the computer.
StringBuilder is probably the most familiar one in Java, because you’re always asked about StringBuilder and StringBuffer.
In addition, we can put it together with the template method, but it is less different. The template method is a customization of the execution, while the build pattern is a customization of the creation process.
5. Bridge
Speaking of the word bridge, I will associate with the bridge mode and routing mode of the router.
In bridge mode, configure the Internet access account on the management page of the router. If no router is configured at home, configure the Internet access function on the configuration page of the optical cat. The routing mode is assumed by the optical cat. Most optical cats are installed in direct dialing mode, so half of the optical cats are in routing mode. However, in routing mode, the optical modem needs to complete both the dial-up and photoelectric conversion functions at the same time, so the network is often jammed.
The above is a little common sense of life.
It is an object structure modelCopy the code
Bridge mode is to change a certain type from the original self-realization ability to external realization ability, through the relationship combination, the whole remains consistent (light cat + router becomes an Internet whole). Just like the router and the light cat here, if the light cat realizes photoelectric conversion and dialing functions at the same time, it is bound to reduce the ability of the light cat itself. It is better to give the Internet access function to the router and devote oneself to signal conversion.
There are many examples of mobile phone and drawing on the Internet, but I won’t say more.
6. Facades
Service in MVC is a facade pattern that we ignore
The facade pattern essentially puts multiple invocation logic into a single method, and when the object is executed, other deep-level methods are executed without retaining any state of their own.
In Ali’s Zhongtai metaphysics, Zhongtai mode is also a kind of appearance mode.
Appearance mode hides execution details and ensures consistency between input and output parameter types. However, the appearance pattern is not extensible and is strongly coupled. It also violates the open and close principle, but has the advantage of ensuring that the change of either end of input and output has little effect on the overall change of the system.
7. Observers
I think those of you who use VUE are very familiar with this pattern and already know it by heart. Observers are divided into two parts, the first part is the object to be observed, the second part is the object to subscribe to observe.
The observer’s understanding is generous:
Your grandmother told you to tell her if your father beat you.
This story illustrates the three-way relationship:
- Your dad: Change
- You:
- Your grandma: Received notice of observation
But in practice, we don’t just tell one party, we tell multiple parties, maybe your grandfather, maybe someone you think can protect you.
Another example of inappropriateness is: tattling, no problem with the observer above, right?
A brief description of the observer design in Vue:
We all know that vue2 uses Object.defineProperty, and vue3 updates it to Proxy, redefining the Object’s data property. When overwritten, Dep is set to collect subscription messages from the subscriber Watcher, notify Watcher when the data property is updated, and then Watcher performs the view update
To be fair, if this book were written now, it would be put into the JS design pattern
8. Visitor
The GoF authors comment that the most complex design patterns, for the most part, you don’t need to use the visitor pattern, but when you do, you really do
There are three elements in the Visitor pattern: Visitor, ConcreteVisitor, ConcreteElement, ConcreteElement.
The example here borrows the wisdom of others and tells the story of the householder and the guest feeding cats and dogs at the same time. We can look at it abstractly: First, the location is a specific location: Home. The householder and the guest are classified as Person, and the cat and dog are classified as Animal.
There is pseudocode like this:
Person { void feed(Cat) void feed(Dog) } Animal { accpet(Person) } Home { List<Animal> animals = new List<Animal>(); add(Animal){ animals.add(Animal) ; } action(Person){ forloop animal : animals { animal.accpet(Person) } } }Copy the code
So the specific logic above becomes:
Owner:Person {
void feed(Dog) {
Dog
}
void feed(Cat) ...
}
Customer:Person {
void feed(Dog) ...
void feed(Cat) ...
}
Dog:Animal {
accept(Person) ...
}
Cat:Animal {
accept(Person) ...
}
Copy the code
Execution time:
Home aHome = new Home();
aHome.add(new Dog)
aHome.add(new Cat)
aHome.action(new Owner)
aHome.action(new Customer)
Copy the code
It seems complicated enough, but if you think about it, it’s just who feeds the dog and the cat. The cat and the dog stay the same in the house, but the person interacting with the cat and the dog changes. However, these are strong type problems, in JS, functions are first-class citizens, it is relatively easy to implement.
function Home(){
let animals = []
return {
add(animal){
animals.push(animal)
},
action(person){
animals.forEach((animal) = >{
animal.accept(person)
})
}
}
}
const Cat = {
name:"cat".accept(person){
person.feed(this)}}const Dog = {
name:"dog".accept(person){
person.feed(this)}}const Owner = {
name:'Owner'.feed(animal){
console.log(`The ${this.name} feed ${animal.name}`)}}const MyHome = Home();
MyHome.add(Cat)
MyHome.add(Dog)
MyHome.action(Owner);
// Owner feed cat
// Owner feed dog
Copy the code
When writing an article, I have no idea about the visitor model at all, so there may be some errors in what I tell you. I hope the reader will keep his eyes peeled and not be confused by the falseness
9. Memento
Memos remind me of routing forward and backward, and each routing access history is a memos record
Memorandum is to record the results of every action, convenient things back to the past, is a medicine for regret. A memo is like a draft for an exam, in which you write your mental calculations on the draft, making it easy to track your thought process.
Each memory snapshot, each router.push, will be a historical state save. If we need to do undo CTRL + Z, then we need to do state saving.
function History(){
let stateList = [];
return {
add(state){
this.stateList.push(state)
}
get(index){
return this.stateList[index]
}
}
}
const Router = {
state:"".push(state){
this.state = state
},
getStateFromMemento(memento){
this.setState(memento.state)
},
saveStateToMemento(){
return { state:this.state }
}
}
const MyHistory = History()
Router.push("/")
Router.push("/index")
MyHistory.add(Router.saveStateToMemento()) // Save the routing status once
Router.push("/home")
Router.state // /home
Route.getStateFromMemento(History.get(0))
Router.state // /index
Copy the code
Is a relatively simple model.
Having covered the design patterns that don't exist in this book, it's time to turn to the ones that do. Write here, really some sleepy, I believe that the official should also be tired, but the book has to write, the story has to say, try to write some perfect, some vivid. This way, the text won't be so pale and the content won't be unattractiveCopy the code
1. Singleton
If a single person is called a single dog, what should a couple be called?
Singletons in JS are really simple, under a single thread, casually declare an object is already a singleton. This is a much lower threshold than multithreaded object orientation. Now with the concept of a module, all package objects are singletons. Modules are not created repeatedly in the system, only references to that module.
The largest singleton in JS is globalThis, which in browsers is an alias for Window.
2. Strategy (Strategy)
As the mouse said, all pipes lead to my house
Policy mode is an execution mode. When the input is constant, different policies can be selected to obtain different output results. It is sometimes compared to the adapter pattern to get a better understanding.
Just as in chess, when moving pieces, different movements are crucial to winning or losing, perhaps vaulting will be more useful than flat guns.
const Context = {
strategy:undefined.setStrategy(strategy){
this.strategy = strategy;
},
execute(num1,num2){
return this.strategy(num1,num2)
}
}
const AddStrategy = function (num1,num2){ return num1 + num2 }
const MultiplyStrategy = function (num1,num2){ return num1 * num2 }
Context.setStrategy(AddStrategy);
Context.execute(1.2) / / 3
Context.setStrategy(MultiplyStrategy);
Context.execute(1.2) / / 2
Copy the code
In the actual development, we will apply it to form verification, provide different verification strategies for the same form field, and finally verify whether the result meets all the verification.
3. Proxy
Xiaoming likes little red, but dare not vindicate again, the boudoir honey that found little red then acts as a letter of affection, result…
The proxy mode must be familiar to everyone, especially those who have used VUe3. The purpose of using proxies is to protect access objects, with limited external access.
var po = new Proxy({name:1}, {set(target,key,value){
if(key=='name' && value <0) {return false
}
console.log(key,value)
target[key] = value
}
})
po.name = -1
po.name / / 1
Copy the code
4. Iterators
The Foolish old man said that his children and grandchildren were infinite. But yu Gong forgot one thing, in case, this successor he did not have the money to marry?
Do you know how to solve asynchronous nested execution without async await? We used function* yield, and there’s a famous library called CO. The next result of an iterator is usually {value:T,done: Boolean}. In js, the Map and Set data types get iterator objects for data through. Entries ().
In Java, the iterator I use most often is to iterate over a Map, get the iterator from the Map, then use the iterator to determine if it is hasNex, and then use next to access the next object value. After the row type conversion, and then do some processing.
Iterators are designed to iterate according to certain rules, and forEach inside is one way to implement iterators. With forEach, however, you cannot change the length of the source object.
5. Publish subscriptions (not inGoFWhich some consider to be consistent with the observer)
Fight your alarm clock every morning
Publish-subscribe and observer patterns, both of which look the same, can also be lumped together. However, in actual execution, a delegate execution queue is appended to the publish subscription to execute the subscription push in order. Publish subscriptions at this point will look similar to the mediation pattern.
Publish subscriptions are usually designed to:
Publisher --> Publish message + Notifier callback --> Message Queue --> Execute in orderCopy the code
And the observer is usually
Observer --> Publish message + notifier callback --> ExecuteCopy the code
The content of publication and subscription is not carried out by the publisher himself, but carried out by others entrusted to him. However, publication and subscription retains the control of the subscriber, which is an intermediate mode between observer and intermediary mode.
There is no other difference from the observer.
6. Command
I order you to read this article.
In command mode, the caller does not care about the execution process of the command, and the receiver does not care about the source of the command. Each command is clear and explicit.
Just like in the restaurant, the waiter only assists the user to place the order. After placing the order, send it to the chef through the command. The chef only needs to deliver the dish according to the order received, and tell the waiter how many tables to deliver the dish through the command.
7. Composite
The leader always says: We have to do a one-two punch.
The composition pattern groups together a set of similar execution objects that have no parent-child relationship. Just like the remote control, the realization, boot, channel search, play three functions, through the combination of buttons to complete.
When working with a tree structure, you can use a composite pattern, where leaf nodes and root nodes are like folders and files, not parent, but can be grouped together.
8. Template method
When I’m being coached on code, I always hear a phrase: Reuse what you can
This is one of the common approaches, a new approach derived from a basic template approach to enhance code diversity. Axios requests, such as GET and POST, are based on template methods. Such as the Element-UI dialog.
The template method presets the steps to be executed, but provides parameters that can be modified. But the whole execution process is the same.
In addition, vue2 hooks are template methods. Because no matter how you write it, Vue always executes these hooks in sequence.
9. Flyweight
The principle of Occam shaving is: do not add substance if it is not necessary. When he saw a batch of OS apps on his phone, it was clear that he was wrong, and very wrong.
Unlike singletons, which only hold one object, singletons hold multiple similar objects. Singletons do not destroy objects, whereas primitives release objects when they have not been used for a certain period of time.
Share mode is used most is the thread pool, can be elastic scaling, according to the system needs as far as possible reuse objects, saving resources.
10. Chain of Responsibility
Beat the drum pass flower game, who is the last stick
In exams, one of the ways we cheat is to pass notes, but we don’t care where the notes go, but the objects are always notes.
It is easy to think of the loader of Webpack, loader is a chain, a chain of execution, the final output of a file.
11. Mediators
The role of intermediary is a sounding board, earning poor information.
Or like to rent a house to take an example, after all, rent is one of the basic intermediary activities. Before there is no intermediary, the landlord wants to rent a house, you need to post ads, people need the streets looking for, need to connect one by one, to see if the landlord house, right rent is within the scope of the right, one or two good, ten eight landlord himself can’t stand, after all his other work to do. And renters don’t just sign up because they’ve seen a landlord’s house.
The tenant is faced with how to choose the right house, the landlord is to choose the right tenant. A single line between the two becomes a spider’s web. Landlords are too busy to handle themselves, and tenants are too busy to handle themselves.
If there is an unrelated third party at this time, as a bridge of communication on both sides, simply do this thing, to help the landlord and tenant do a good job of communication! ?
The intermediary agency was born in this environment. In a communication, there are tenants and landlords, and they are grouped according to certain conditions. For example, the tenants want the rent to be less than 500, and the landlords want the rent not less than 450. Then when the conditions coincide, the intermediary will notify the tenant and inform the landlord to show the house. If appropriate, the contract can be signed.
The agency will inform the information of the publisher to the subscriber according to certain rules, and the subscriber can in turn tell the agency the requirements of the landlord, they are two-way.
What is the difference between the mediation pattern and publish and subscribe?
We can use chat rooms as an example.
If a person wants to receive a message posted by someone else, he has two ways.
- The first way is to subscribe to other people’s messages.
- The second way: by others to inform him.
The first method is problematic. The message recipient needs to subscribe to the notification messages of everyone else in the group, so everyone needs to subscribe to everyone except himself. When a group is large, the number of subscriptions is a terrible existence n*(n-1). With the mediation pattern, you only need to maintain the relationship between the mediation and the subscriber. Messages are selectively passed to each other through established rules, similar to a message bus.
12. Decorators
I am a Painter
Decorators in JS are for classes and their properties and methods. Decorators modify the object itself, which is fundamentally different from annotations in Java.
Notes: notes, solutions. Is a form of configuration.Copy the code
Here is not too much decoration description, there is also a large text on the Internet.
Lady Wang would like to introduce herself,d4axios
Is one way to use decorators to manage AXIos requests.
13. State Machine
I wonder if there is a symbol for Schrodinger’s cat?
When you’re developing an action game, there are moves, jumps, blocks, attacks, all of which affect each other, and if you don’t have a good pattern, you’re going to have a lot of if-else statements, and your code is going to be pretty ugly. In PLC programming, also often involved in the state of the switch, the use of action-state design a two-dimensional table, will be listed all the circumstances. Finally determine the state transition relationship, we call it finite state machine, it is a modeling method of object behavior.
14. Adapter
Only the foot knows whether the shoe fits or not
The adapter and policy patterns and the chain of responsibility patterns are compared side by side.
Adapters, commonly used for power supply adaptation, input different voltage, output the same voltage.
Policies. Enter the same object and configure different policies to obtain different results
Responsibility chain, input the same, configure different responsibility links, output the result of the object
Policies and adapters are a set of opposite processes, and policies and chains of responsibility are a set of similar processes.
Policy pays attention to the result (when the form is verified, the input check value is the same), responsibility chain pays attention to the process change (when the loader processes, the value passed is the processing result of the previous link, the value may change)