Many programmers must be confused when they talk about design patterns. I don’t care what design patterns you don’t design patterns!
Whatever it takes to get what it needs, it’s a mess. In fact, reasonable design in accordance with a certain design pattern to design the code structure of the project can improve the maintainable line/readability of the code and reduce the number of code. This performance is not prompted, their own code when not fragrant, uncomfortable yao?
Today you’ll learn about front-end design patterns through four practical examples. Here are four common design patterns
- The strategy pattern
- Publish and subscribe
- Decorator mode
- Chain of Responsibility model
The strategy pattern
Suppose we have a requirement that when a user tries to open a page, the correct content can only be seen if the following conditions are met:
- The user is a registered user of the site
- The user level is not less than 1
- The user must be a front-end development engineer
- The user type is active user
Now, we need to write the judgment logic to ensure that only the right users see the content. What do you do? Many novice programmers may simply choose “if-else” and write code like this:
function checkAuth(data) {
if(data.role ! = ='registered') {
console.log('If not registered');
return false;
}
if (data.grade < 1) {
console.log("User level less than 1");
return false;
}
if(data.job ! = ='FE') {
console.log('Users are not front-end developers');
return false;
}
if(data.type ! = ='active user') {
console.log('User is not active user');
return false; }}Copy the code
You’ve probably written code like this before, but it obviously has a few problems:
- The checkAuth function is bloated
- Each judgment function cannot be reused
- Violate the open and close principle
So how do we solve this problem? This is where the strategic pattern comes in.
It is a design pattern that allows the encapsulation of alternative algorithms for specific tasks. It defines a set of algorithms and encapsulates them in a way that is interchangeable at run time without interference.
Now, let’s use the policy pattern to improve the previous code.
const jobList = ['FE'.'BE'];
const strategies = {
checkRole: function(value) {
if (value === 'registered') {
return true;
}
return false;
},
checkGrade: function(value) {
if (value >= 1) {
return true;
}
return false;
},
checkJob: function(value) {
if (jobList.indexOf(value) > 1) {
return true;
}
return false;
},
checkType: function(value) {
if (value === 'active user') {
return true;
}
return false; }};Copy the code
const Validator = function() {
/ / cache
this.cache = [];
// Add cache
this.add = function(value, method) {
this.cache.push(function() {
return strategies[method](value);
});
};
// Check if there is a cache
this.check = function() {
for (let i = 0; i < this.cache.length; i++) {
let valiFn = this.cache[i];
var data = valiFn();
if(! data) {return false; }}return true;
};
};
Copy the code
Doesn’t it seem comfortable that one object implements a specific function, and then one object implements a specific split function in detail
Now let’s implement the previous requirement
var compose1 = function() {
var validator = new Validator();
const data1 = {
role: 'register'.grade: 3.job: 'FE'.type: 'active user'
};
validator.add(data1.role, 'checkRole');
validator.add(data1.grade, 'checkGrade');
validator.add(data1.type, 'checkType');
validator.add(data1.job, 'checkJob');
const result = validator.check();
return result;
};
Copy the code
We can see that our code becomes more maintainable by applying the policy pattern. Now you can consider applying the policy pattern to your own projects, for example when dealing with form validation.
You can consider using the policy pattern to optimize your code when the module you are responsible for basically meets the following criteria.
- The policy under each judgment condition is independent and reusable
- The internal logic of this strategy is relatively complex
- Policies need to be flexibly combined
Publish and subscribe
Now let’s look at another requirement: when a user successfully completes an application, the background needs to trigger the corresponding order, message, and approval modules.
How would you code it? Many programmers might write something like this:
How would you code it? Many programmers might write something like this:
function applySuccess() {
// Notify the message center to get the latest content
MessageCenter.fetch();
// Update order information
Order.update();
// Notify responsible person for review
Checker.alert();
}
Copy the code
As more and more modules were involved, our code became bloated and difficult to maintain. That’s when the publish and subscribe model can save disaster.
Are you familiar with EventEmitter? Yeah, a lot of interviews, right?
Publish-subscribe is a messaging paradigm in which the publisher of a message does not send the message directly to a particular subscriber, but broadcasts it through a message channel, and subscribers can subscribe to get the message they want. First, let’s write an EventEmit function:
const EventEmit = function(){
this.events = {};
this.on = function(The name of cb){
if(this.events [name]){
this.events [name] .push(cb);
} else {
this.events [name] = [cb]; }};this.trigger = function(The name,... arg){
if(this.events [name]){
this.events [name] .forEach(eventListener= >{ eventListener(... arg); }); }}; };Copy the code
Above we wrote an EventEmit, and then our code could be changed to:
let event = new EventEmit();
MessageCenter.fetch(){
event.on('success', () = > {console.log('Notify message Center to get updated content');
});
}
Order.update(){
event.on('success', () = > {console.log('Update Order Information');
});
}
Checker.alert(){
event.on('success', () = > {console.log('Notify responsible person for review');
});
}
event.trigger('success');
Copy the code
Isn’t that better? All events are independent of each other. We can add, modify, and delete events at any time without affecting other modules. When you are responsible for a module that basically meets the following criteria, you can consider using a publish-subscribe model.
Decorator mode
Now let’s go straight to an example.
As anyone who knows React knows, a higher-order component is really just a function. It takes a component as an argument and returns a new component. So let’s write a high-level component, HOC, and use it to decorate the TargetComponent.
import React from 'react';
const yellowHOC = WrapperComponent= > {
return class extends React.Component {
render() {
<div style={{ backgroundColor: 'yellow'}} ><WrapperComponent {. this.props} / >
</div>; }}; };export default yellowHOC;
Copy the code
In the example above, we designed the component yellowHOC to wrap the other components. This is the decorator mode. If in any doubt, let’s look at another example of the decorator pattern.
//Jon is originally a Chinese speaker
const jonWrite = function() {
this.writeChinese = function() {
console.log('I can only write In Chinese');
};
};
// Through the decorator
const Decorator = function(old) {
this.oldWrite = old.writeChinese;
this.writeEnglish = function() {
console.log('Give him the ability to write English');
};
this.newWrite = function() {
this.oldWrite();
this.writeEnglish();
};
};
const oldJonWrite = new jonWrite();
const decorator = new Decorator(oldJonWrite);
decorator.newWrite();
Copy the code
It looks like it meets the requirements, but in fact, the above disadvantages are quite large:
Our purchasing process may change, for example by adding an inventory check process. Then, you have to overhaul the original code, which is hard to maintain.
At this point, we can consider using the chain of responsibility model.
We can rewrite the code like this:
const Chain = function(fn){
this.fn = fn;
this.setNext = function(){}
this.run = function(){}}const applyDevice = function(){}const chainApplyDevice = new Chain(applyDevice);
const selectAddress = function(){}const chainSelectAddress = new Chain(selectAddress);
const selectChecker = function(){}const chainSelectChecker = new Chain(selectChecker);
chainApplyDevice.setNext(chainSelectAddress).setNext(chainSelectChecker);
chainApplyDevice.run();
Copy the code
What are the benefits? The first thing we do is decouple the nodes, and the way we do that is we call function B in function A, and then we call function C in function B. But now it’s different. Each function is independent of the other.
Now, suppose we need to check inventory before selecting an address after applying for the device. In the chain of responsibility pattern of code, we can simply modify the code to fulfill the requirements.
Consider using the chain of responsibility mode when the module you are responsible for meets the following conditions.
- The code for each process can be reused
- Each process has a fixed order of execution
- Every process can be reorganized
My translation project: Little Brother Hook
Original text: medium.com/fedever/4-u…