The code has been written for several years, and the design pattern is in a state of forgetting and forgetting. Recently, I have some feelings about the design pattern, so I will learn and summarize it again.
Most speak design pattern articles are using Java, c + + such based on the class the static type of language, as a front-end developer, js this prototype based dynamic language, function has become a first class citizen, slightly different on some design patterns, even simple to don’t like to use the design patterns, sometimes also can produce some confusion.
The following are summarized in the order of “scenario” – “design pattern definition” – “code implementation” – “general”. If there are any improper points, welcome to discuss.
scenario
Suppose we are developing a food delivery site. When entering the site, the first step is to request the back-end interface to get the user’s common food delivery address. Then go to the other interface and render the page. Without thinking about anything else, you might write something like this:
// getAddress asynchronous request
// There are three modules in the page: A, B, and C
// Modules A, B, and C are all written by different people and provide different methods for us to call
getAddress().then(res= > {
const address = res.address;
A.update(address)
B.next(address)
C.change(address)
})
Copy the code
At this time, there is a module D in the page, and we also need to get the address for the next operation, so we have to turn over the code requesting the address to make up the call of module D.
// getAddress asynchronous request
// There are three modules in the page: A, B, and C
// Modules A, B, and C are all written by different people and provide different methods for us to call
getAddress().then(res= > {
const address = res.address;
A.update(address)
B.next(address)
C.change(address)
D.init(address)
})
Copy the code
It can be seen that the coupling between each module and the address acquisition module is serious. If there are changes or new modules in modules A, B and C, it is necessary to go deep into the code to obtain the address to modify, and A careless change may cause problems.
This is where the observer pattern comes in.
Design Pattern definition
Take a look at wikipedia:
The observer pattern is a software design pattern in which an object, named the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.
This is a well-understood design pattern. There is a subject object, and then there are many observers, notifying observers when the subject is about to change.
Look again at the UML diagram and sequence diagram:
Each observer implements the UPDATE method and invokes the Attach method of the Subject object to subscribe to the changes. The Update method of the Observer is called to notify the Observer when the Subject changes.
Write a simple example in Java:
Articles on the official account can be regarded as Subject, which will be updated irregularly. Then each user becomes an Observer, subscribes to the official account, and receives updates as soon as possible.
import java.util.ArrayList;
interface Observer {
public void update(a);
}
// Extract the public part of the Subject
abstract class Subject {
private ArrayList<Observer> list = new ArrayList<Observer>();
public void attach(Observer observer){
list.add(observer);
}
public void detach(Observer observer){
list.remove(observer);
}
public void notifyObserver(a){
for(Observer observer : list){ observer.update(); }}}// A specific public number to write articles and get articles
class WindLiang extends Subject {
private String post;
public void writePost(String p) {
post = p;
}
public String getPost(a) {
returnpost; }}/ / xiao Ming
class XiaoMing implements Observer {
private WindLiang subject;
XiaoMing(WindLiang sub) {
subject = sub;
}
@Override
public void update(a){
String post = subject.getPost();
System.out.println("I got it." + post + "And gave a thumbs-up."); }}/ / xiao Yang
class XiaoYang implements Observer {
private WindLiang subject;
XiaoYang(WindLiang sub) {
subject = sub;
}
@Override
public void update(a){
String post = subject.getPost();
System.out.println("I got it." + post + "And forwarded it."); }}/ / xiao gang
class XiaoGang implements Observer {
private WindLiang subject;
XiaoGang(WindLiang sub) {
subject = sub;
}
@Override
public void update(a){
String post = subject.getPost();
System.out.println("I got it." + post + "And collect."); }}public class Main {
public static void main(String[] args) {
WindLiang windliang = new WindLiang(); // Subject
XiaoMing xiaoMing = new XiaoMing(windliang);
XiaoYang xiaoYang = new XiaoYang(windliang);
XiaoGang xiaoGang = new XiaoGang(windliang);
// Add an observer
windliang.attach(xiaoMing);
windliang.attach(xiaoYang);
windliang.attach(xiaoGang);
windliang.writePost("New article - Observer Model, Balabala."); // Update the article
windliang.notifyObserver(); // Notify the observer}}Copy the code
The following output is displayed:
The above implementation is mainly to comply with the original definition, update calls do not pass arguments. If the observer needs the same parameters, it can pass the updated data directly, so that the observer does not need to call subject.getPost() to retrieve the updated data manually.
The former is called pull mode. After receiving the notification from the Subject, the internal Subject object calls the corresponding method to get the required data.
The latter is called push mode, in which the Subject pushes the data to the observer when updating, and the observer can use it directly.
The following uses js to rewrite to push mode:
const WindLiang = () = > {
const list = [];
let post = "It hasn't been updated.";
return {
attach(update) {
list.push(update);
},
detach(update) {
let findIndex = -1;
for (let i = 0; i < list.length; i++) {
if (list[i] === update) {
findIndex = i;
break; }}if(findIndex ! = = -1) {
list.splice(findIndex, 1); }},notifyObserver() {
for (let i = 0; i < list.length; i++) { list[i](post); }},writePost(p){ post = p; }}; };const XiaoMing = {
update(post){
console.log("I got it." + post + "And gave a thumbs-up."); }}const XiaoYang = {
update(post){
console.log("I got it." + post + "And forwarded it."); }}const XiaoGang = {
update(post){
console.log("I got it." + post + "And collect.");
}
}
windliang = WindLiang();
windliang.attach(XiaoMing.update)
windliang.attach(XiaoYang.update)
windliang.attach(XiaoGang.update)
windliang.writePost("New article - Observer Model, Balabala.")
windliang.notifyObserver()
Copy the code
In JS, we can pass the update method directly to the Subject, and use push mode. When we call update, we can pass the data directly to the observer, which looks much cleaner.
Code implementation
Going back to the opening scenario, we can use the observer pattern to decouple the sequence of subsequent operations after the address is obtained.
// There are three modules in the page: A, B, and C
// Modules A, B, and C are all written by different people and provide different methods for us to call
const observers = []
// Register the observer
observers.push(A.update)
observers.push(B.next)
obervers.push(C.change)
// getAddress asynchronous request
getAddress().then(res= > {
const address = res.address;
observers.forEach(update= > update(address))
})
Copy the code
Through the observer mode, we decouple the operation after obtaining the address. In the future, new modules only need to register the observer.
When getAddress is complex, the observer mode makes future changes clear without affecting the getAddress logic.
If necessary, avoid making a file too bloated by decoupling it into a new module, observers.
The total
The observer pattern is easier to understand, reducing the over-coupling between a Subject and multiple observers by abstracting them out. The simple thing is to use the callback function and call the incoming callback after the asynchrony. But the observer model has some drawbacks:
Subject
You still need to maintain a list of observers yourselfpush
和update
.- If other modules need to use the observer pattern, the module itself will need to maintain a new list of observers, rather than reuse the previous code.
Subject
You need to know what methods the observer provides for future callbacks.
The next article will improve on this, but the essence of the observer pattern remains the same (one object changes and then notifies other observer objects to update).
But the writing introduces an intermediate platform that makes the code easier to reuse, decouple the Subject from the observer more thoroughly, and gives it a new name, “publish and subscribe.”
More design patterns recommended reading:
Front end design mode series – strategic mode
Front-end design mode series – proxy mode
Front end design mode series – decorator mode