purpose
The purpose of the Decorator Pattern is very simple, that is:
Add logic without modifying the original code.
This sentence may sound a bit contradictory. Since logic is needed to be added, how can it be possible not to modify the original code? But the open and closed principle of SOLID (5 Important Principles for Object Design) is trying to solve this problem. Its content is not to change the core logic that has been written, but to expand the new logic, that is, open to extension and open to modification. shut down.
For example, if we want to implement a function to output text in the browser console, we might do this:
class Printer {
print(text) {
console.log(text);
}
}
const printer = new Printer();
printer.print('something'); // something
When you looked at your results with satisfaction, the product came over and said: "I think the color is not prominent enough, so let's change it to yellow!"
A piece of cake! After you open the Baidu One-Pass operation with confidence, change the code to the following:
class Printer {
print(text) {
console.log(`%c${text}`,'color: yellow;');
}
}
But after looking at the product, he said: "This font is a bit too small, and bigger, it's best to be a high-end style.
"Okay..." You are forcibly controlling your urge to take a knife, while figuring out how big the font is for the high-end atmosphere, you modify the code of print
class Printer {
print(text) {
console.log(`%c${text}`,'color: yellow;font-size: 36px;');
}
}
After changing you this time, your mind is full of mmp, and you keep thinking:
"Is this really good?"
You cannot guarantee that this is the last modification, and there may be more than one product that will point you at your fingertips. print
until the computer went into sleep mode, and your bitter face was reflected on the screen, thinking about the 060e26eb968312 method that keeps getting messy, and don’t know how to deal with those never-ending demands. . . .
In the above example, the initial Printer
writes out the logic it should have according to the requirements, that is, output some text in the console. In other words, after writing the logic of "output some text in the console", you can Printer
, because it is all of Printer
How to change the font or color logic in this situation?
At this time you need decorator mode.
Decorator Pattern
First modify the original Printer
that it can support extended styles:
class Printer {
print(text = '', style = '') {
console.log(`%c${text}`, style);
}
}
Then create a decorator that changes the font and color:
const yellowStyle = (printer) => ({
...printer,
print: (text = '', style = '') => {
printer.print(text, `${style}color: yellow;`);
}
});
const boldStyle = (printer) => ({
...printer,
print: (text = '', style = '') => {
printer.print(text, `${style}font-weight: bold;`);
}
});
const bigSizeStyle = (printer) => ({
...printer,
print: (text = '', style = '') => {
printer.print(text, `${style}font-size: 36px;`);
}
});
Code yellowStyle
, boldStyle
and bigSizeStyle
are to print
method decorators, they are received printer
, and to printer
based reproduce a different object and returned, and the return printer
original difference is that each Decorator
will is printer
the print
method having the respective logical decorative (e.g., change the font, size or color) and then call printer
of print
.
The usage is as follows:
As long as you extract all the decoration logic, you can freely match when to output which style, add another italic style, and only need to add another decorator, without changing the original print
logic.
However, it should be noted that the above code simply copies Object by destructuring. If prototype
, there may be errors, so if you want to deep copy a new object, you need to write additional logic:
const copyObj = (originObj) => {
const originPrototype = Object.getPrototypeOf(originObj);
let newObj = Object.create(originPrototype);
const originObjOwnProperties = Object.getOwnPropertyNames(originObj);
originObjOwnProperties.forEach((property) => {
const prototypeDesc = Object.getOwnPropertyDescriptor(originObj, property);
Object.defineProperty(newObj, property, prototypeDesc);
});
return newObj;
}
copyObj
in the above code in the decorator to copy the same object correctly:
const yellowStyle = (printer) => {
const decorator = copyObj(printer);
decorator.print = (text = '', style = '') => {
printer.print(text, `${style}color: yellow;`);
};
return decorator;
};
Other cases
Because the language we use is JavaScript, we don't use classes, we just simply decorate a certain method, such as the following publishArticle
used to publish articles:
const publishArticle = () => {
console.log('发布文章');
};
If you want to post an update on a platform such as Weibo or Qzone after publishing an article, what should you do? Is it like the following code?
const publishArticle = () => {
console.log('发布文章');
console.log('发 微博 动态');
console.log('发 QQ空间 动态');
};
This is obviously not good! publishArticle should only need the logic of publishing the article! And if there are more and more third-party service platforms in the publishArticle
, then 060e26eb96867f will fall into a situation where logic is always added, and you can no longer do this after you understand the decorator mode!
So put this requirement into a decorator:
const publishArticle = () => {
console.log('发布文章');
};
const publishWeibo = (publish) => (...args) => {
publish(args);
console.log('发 微博 动态');
};
const publishQzone = (publish) => (...args) => {
publish(args);
console.log('发 QQ空间 动态');
};
const publishArticleAndWeiboAndQzone = publishWeibo(publishQzone(publishArticle));
The previous Printer
is to copy an object and return it, but if it is a method, there is no need to copy it. Just make sure that each decorator will return a new method and then execute the decorated method.
to sum up
The decorator pattern is a very useful design pattern, and it is often used in projects. When the requirements change, if you feel that there is a lot of logic, then you can just just not decorate it, and you don’t need to modify the code that implements the logic. . Each decorator does its own thing and does not affect each other with other decorators.
This article first published WeChat public account: Front-end Pioneer
Welcome to scan the QR code to follow the official account, and push you fresh front-end technical articles every day
Welcome to continue reading other highly praised articles in this column:
- Deep understanding of Shadow DOM v1
- Step by step teaches you to use WebVR to realize virtual reality games
- 13 modern CSS frameworks to help you improve development efficiency
- Quickly get started with BootstrapVue
- JavaScript engine work? Everything you need to know from call stack to
- WebSocket combat: real-time communication between Node and React
- 20 interview questions about Git
- depth analysis of Node.js console.log
- What exactly is Node.js?
- Build an API server with Node.js in 30 minutes
- Javascript object copy
- programmer's monthly salary is less than 30K before the age of 30, so what should I do?
- 14 best JavaScript data visualization libraries
- 8 top VS Code extension plug-ins for the front end
- Node.js Multithreading Complete Guide
- 4 solutions for converting HTML to PDF and their implementation
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。