Recently, I reviewed these design patterns that I had learned before, and I felt that I still had a lot to gain. I do have a feeling of reviewing the old and learning the new, so I am ready to write an article about this design pattern after reviewing each design pattern, so that I can deepen my understanding of this design pattern. I can also discuss it with you.
Today we’re going to look at the observer pattern, and we don’t need to know what the observer pattern is to begin with, but we’ll get to that later. I want to start with a small example from my life. Starting with something familiar in our lives will help us understand the use of this pattern more quickly.
Small examples of life
I believe that we have paid attention to some public number, so for a public number, if there is a new article published; Then all concerned about the public number of users will receive the update notice, if a user does not pay attention to or after the attention and cancelled the attention, then the user will not receive the update notice of the public number. I’m sure you’re all familiar with this scenario. So if we abstracted this process and implemented it in code, what would you do? Stop and think about it now.
From the above description, we know that this is a one-to-many relationship. That is, a public number corresponds to many users concerned about this public number.
So for this public number, its internal need to have a list of users concerned about this public number, once the public number has new content. So for the public, it’s going to iterate over the list. Each user in the list is then sent a content and new notification. We can represent this process in code:
/ / user
const user = {
update() {
console.log('The official account has been updated with new content'); }};/ / the public number
const officialAccount = {
// Pay attention to the list of current public account users
followList: [user],
// The notification function called when the public account is updated
notify() {
const len = this.followList.length;
if (len > 0) {
// Notify each user who has followed the public account that there is a content update
for (let user of this.followList) { user.update(); }}}};// The official account has new content updates
officialAccount.notify();
Copy the code
The results are as follows:
The official account has been updated with new contentCopy the code
The above code can simply represent the process of notifying the users concerned about the public account when the content of the public account is updated. But the implementation is rudimentary and lacks some content. Let’s complete these missing processes. For the public account, it is also necessary to add new users, remove users who are no longer concerned, and obtain the total number of users concerned with the public account. Let’s implement the above process:
/ / the public number
const officialAccount = {
// Pay attention to the list of current public account users
followList: [].// The notification function called when the public account is updated
notify() {
const len = this.followList.length;
if (len > 0) {
// Notify each user who has followed the public account that there is a content update
for (let user of this.followList) { user.update(); }}},// Add a new user to follow
add(user) {
this.followList.push(user);
},
// Remove users that you no longer follow
remove(user) {
const idx = this.followList.indexOf(user);
if(idx ! = = -1) {
this.followList.splice(idx, 1); }},// Calculate the total number of users
count() {
return this.followList.length; }};// Create a new user class
class User {
constructor(name) {
this.name = name;
}
// Receive notification of content update of public account
update() {
console.log(`The ${this.name}Received the update of the public number); }}// Create two new users
const zhangSan = new User('Joe');
const liSi = new User('bill');
// Add the following user to the public account
officialAccount.add(zhangSan);
officialAccount.add(liSi);
// The official account has new content updates
officialAccount.notify();
console.log('The current number of concerned public account users is:${officialAccount.count()}`);
// Zhang SAN no longer pays attention to the official account
officialAccount.remove(zhangSan);
// The official account has new content updates
officialAccount.notify();
console.log('The current number of concerned public account users is:${officialAccount.count()}`);
Copy the code
The output is as follows:
The current number of users following the public account is: 2. The current number of users following the public account is: 1Copy the code
The above code improves the process of following and unfollowing, and can obtain the number of followers of the current public account. We also implemented a user class that allows us to quickly create users that we need to add to our public account’s follow list. Of course, you can also put the implementation of the public number through a class to complete, here will no longer show the implementation of the process.
Have you learned something from this simple example? What we have implemented above is a simple observer pattern. Let’s talk about the definition of the Observer pattern and some of its practical uses in development.
Definition of observer pattern
The so-called Observer mode refers to a one-to-many relationship, in which we call one Subject (analogous to the public account mentioned above) and many of them are called observers (analogous to the user following the public account mentioned above), namely observers. Since changes in multiple observers depend on Subject’s status updates, Subject maintains a list of observers internally, and when Subject’s status changes, the list is iterated over, notifying each Observer in the list to update accordingly. Because of this list, the Subject can add, delete, change, or check the list. This enables the Observer to update and unbind the Subject dependencies.
Let’s look at a UML diagram of the Observer pattern:
From the figure above, we can see that for the Subject, it needs to maintain an observerCollection of its own. This list contains instances of the Observer. Methods to add, remove, and notify observers are then implemented within the Subject. The observer is notified by traversing the observerCollection list, calling the update method of each observer in the list in turn.
At this point, you now have some idea of the design pattern. So what does it do for us to learn this design pattern? First, if we encounter a one-to-many relationship like the one above in development, and many state updates depend on one state; Then we can use this design pattern to solve this problem. And we can use this pattern to decouple our code, making it more extensible and maintainable.
Of course, some students may feel that they have not used this design pattern in their daily development. That is because we usually use some frameworks, such as Vue or React, and this design pattern has been implemented internally by these frameworks. We can use it directly, so we are less aware of the design pattern.
Actual combat: to achieve a simple TODO small application
We can use the Observer pattern to implement a small application that simply lets users add their own to-do lists and displays the number of added items.
Once we understand the requirements, we need to determine which ones are one and which ones are many. Of course we know that the entire TODO state is what we call one, so the presentation of to-do lists and the count of to-do lists is what we call many. With that in mind, implementing this little application is simple.
You can check out this simple app here at π.
First we need to implement the Subject and Observer classes in the Observer schema, as shown below.
Subject:
// Subject
class Subject {
constructor() {
this.observerCollection = [];
}
// Add an observer
registerObserver(observer) {
this.observerCollection.push(observer);
}
// Remove the observer
unregisterObserver(observer) {
const observerIndex = this.observerCollection.indexOf(observer);
this.observerCollection.splice(observerIndex, 1);
}
// Notify the observer
notifyObservers(subject) {
const collection = this.observerCollection;
const len = collection.length;
if (len > 0) {
for (let observer ofcollection) { observer.update(subject); }}}}Copy the code
The Observer:
/ / observer
class Observer {
update(){}}Copy the code
So the next code is the implementation of the above to-do list, the code has added the corresponding comments, let’s take a look.
Logical parts of the backlog application:
// Form status
class Todo extends Subject {
constructor() {
super(a);this.items = [];
}
/ / add a todo
addItem(item) {
this.items.push(item);
super.notifyObservers(this); }}// List rendering
class ListRender extends Observer {
constructor(el) {
super(a);this.el = document.getElementById(el);
}
// Update the list
update(todo) {
super.update();
const items = todo.items;
this.el.innerHTML = items.map(text= > `<li>${text}</li>`).join(' '); }}// List counts observers
class CountObserver extends Observer {
constructor(el) {
super(a);this.el = document.getElementById(el);
}
// Update the count
update(todo) {
this.el.innerText = `${todo.items.length}`; }}// List observer
const listObserver = new ListRender('item-list');
// Count observers
const countObserver = new CountObserver('item-count');
const todo = new Todo();
// Add list observer
todo.registerObserver(listObserver);
// Add count observer
todo.registerObserver(countObserver);
// Get the todo button
const addBtn = document.getElementById('add-btn');
// Get the contents of the input box
const inputEle = document.getElementById('new-item');
addBtn.onclick = () = > {
const item = inputEle.value;
// Check whether the added content is empty
if (item) {
todo.addItem(item);
inputEle.value = ' '; }};Copy the code
From the code above we can clearly see every part of the application. The Subject being observed is our Todo object, and its state is the to-do list. The observer lists it maintains are listObserver, which shows to-do lists, and countObserver, which shows to-do numbers. As soon as a new item is added to todo’s list, the two observers are notified to make the corresponding content update. So the logic of the code is pretty straightforward. If we need to add new functionality later when the state changes, we can simply add a corresponding Observer again, which is easy to maintain.
Of course, the above code only implements the basic functions, and does not include to-do completion and deletion, as well as the classification of unfinished and completed tasks. And the rendering of the list is re-rendered every time, with no logic for reuse. Because this chapter is about discussing the observer pattern, the code above is simple and illustrative. I believe that excellent you can on this basis, these functions are perfect, quickly try it.
In fact, we learn these design patterns, are in order to make the logic of the code more clear, can reuse some of the logic of the code, reduce repetitive work, improve the efficiency of development. Make the entire application easier to maintain and expand. Of course, do not use for the sake of use, before use, need to do a comprehensive understanding of the current problem. Whether or not you need to use a design pattern is a clear question.
Well, this is the end of the observer mode, if you have any comments and suggestions can leave a comment below the article, we discuss together. We can also bring it up here, so we can have a better discussion. You are also welcome to pay attention to my public account guan Shan is not difficult to get updates anytime and anywhere.
Reference links:
- The Observer Pattern
- How to Use the Observable Pattern in JavaScript
Article cover image source: unDraw