Use decorator mode to do interesting things

Source: Internet
Author: User
What is decorator mode

Decorator mode is a technique for adding features to a function or class, which allows us to add new capabilities and behaviors to it without modifying the original object. It is also essentially a function (in Javascipt, the class is just the syntactic sugar of the function).

When are we going to get it?

Let's assume a scenario where a bike shop has several models of bikes, and now the store allows users to provide some extra accessories for each bike, such as headlights, taillights, bells, etc. Each choice of one or several accessories will affect the price of the bike.

If you follow the traditional way of creating subclasses, we currently have a bike base class, and we want to create a new class for each of the possible choices. However, because the user can choose one or several kinds of accessories, which leads to the eventual production of hundreds of dozens of of subclasses, which is obviously unscientific. However, in this case, we can use the decorator mode to solve this problem.

The base class for bicycles is as follows:

class Bicycle {    // 其它方法    wash () {}    ride () {}    getPrice() {        return 200;    }}

Then we can start by creating a decorator pattern base class

class BicycleDecotator {    constructor(bicycle) {        this.bicycle = bicycle;    }    wash () {        return this.bicycle.wash();    }    ride () {        return this.bicycle.ride();    }    getPrice() {        return this.bicycle.getPrice();    }}

This base class does not actually do anything, it just takes a bicycle instance, implements its corresponding method, and calls its method back.

With this base class, we can do whatever we want for the original bicycle class according to our needs. For example, I can create an adorner with a headlamp added and an adorner with a tail light added:

class HeadLightDecorator extends BicycleDecorator {    constructor(bicycle) {        super(bicycle);    }    getPrice() {        return this.bicycle.getPrice() + 20;    }}class TailLightDecorator extends BicycleDecorator {    constructor(bicycle) {        super(bicycle);    }    getPrice() {        return this.bicycle.getPrice() + 20;    }}

Well, then we can come up with the freedom to combine them:

let bicycle = new Bicycle();console.log(bicycle.getPrice()); // 200bicycle = new HeadLightDecorator(bicycle); // 添加了前灯的自行车console.log(bicycle.getPrice());  // 220bicycle = new TailLightDecorator(bicycle); // 添加了前灯和尾灯的自行车console.log(bicycle.getPrice()); // 240

What are the benefits of this? Let's say we have 10 accessories, so we just need to write 10 accessory decorators, and then we can mix the bikes with different accessories and calculate the price. And if you follow the implementation of the subclass, 10 of the accessories may need to have hundreds of or even thousands of sub-categories.

From the example we can see the application of the decorator pattern:

    1. If you need to add attributes or responsibilities to a class, but the workaround for deriving subclasses from a class is not too realistic, you should use decorator mode.
    2. In the example, we did not modify the original bicycle base class, so we do not have side effects on the original code. We just added some functionality to the original base. Therefore, if you want to add attributes to an object and do not want to change the code that uses the object, you can use decorator mode.

Decorator mode can be applied to a function in addition to the class (which is, in fact, the higher order function). For example, we want to measure the execution time of a function, so I can write a decorator like this:

function func() {    console.log('func');}function timeProfileDecorator(func) {    return function (...args) {        const startTime = new Date();        func.call(this, ...args);        const elapserdTime = (new Date()).getTime() - startTime.getTime();        console.log(`该函数消耗了${elapserdTime}ms`);    }}const newFunc = timeProfileDecorator(func);console.log(newFunc());
Do some interesting things.

Now that we know that decorator mode can add some new features without modifying the original code, then we can do something interesting.

We can provide performance analysis for the methods of a class.

 class Timeprofiledecorator {constructor (component, keys) {this.component = component; This.timers = {}; Const SELF = this; For (Let I-keys) {Let key = Keys[i]; if (typeof component[key] = = = ' function ') {This[key] = function (... args) {This.starttimer (key); Resolve this reference error issue Component[key].call (component, ... args); This.logtimer (key); }}}} Starttimer (namespace) {This.timers[namespace] = new Date (); } Logtimer (namespace) {Const ELAPSERDTIME = (new Date ()). GetTime ()-this.timers[namespace].gettime (); Console.log (' This function consumes ${elapserdtime}ms '); }}//ExampleClass Test {constructor () {this.name = ' cjg '; This.age = 22; } sayname () {console.log (this.name); } sayage () {console.log (this.age); }}let test1 = new Test () test1 = new Timeprofiledecorator (test1, [' Sayname ', ' sayage ']); Console.log (Test1.sayname ()); Console.log (Test1.sayage ()); 
To enhance a function

Throttle function or anti-shake function

function throttle(func, delay) {    const self = this;    let tid;    return function(...args) {        if (tid) return;        tid = setTimeout(() => {            func.call(self, ...args);            tid = null;        }, delay);    }}function debounce(func, delay) {    const self = this;    let tid;    return function(...args) {        if (tid) clearTimeout(tid);        tid = setTimeout(() => {            func.call(self, ...args);            tid = null;        }, delay);    }}

Cache function return value

// 缓存函数结果,对于一些计算量比较大的函数效果比较明显。function memorize(func) {    const cache = {};    return function (...args) {        const key = JSON.stringify(args);        if (cache[key]) {          console.log('缓存了');          return cache[key];        }        const result = func.call(this, ...args);        cache[key] = result;        return result;    };}function fib(num) {  return num < 2 ? num : fib(num - 1) + fib(num - 2);}const enhanceFib = memorize(fib);console.log(enhanceFib(40));console.log(enhanceFib(40));console.log(enhanceFib(40));console.log(enhanceFib(40));

constructs react high-level components to add additional functionality to components , such as providing Shallowcompare functionality for components:

import React from 'react';const { Component } = react;const ShadowCompareDecorator = (Instance) => class extends Component {  shouldComponentUpdate(nextProps, nextState) {    return !shallowCompare(this.props, nextProps) ||      !shallowCompare(this.state, nextState);  }  render() {    return (      <Instance {...this.props} />    );  }};export default ShadowCompareDecorator;

Of course, if you have used React-redux, you must have used connect. In fact, connect is also a way of higher-order components. It gets the global state from the provider context through the decorator mode, and passes it through the props to the original component.

Summarize

Using decorator mode allows us to add new functionality to the original classes and functions, without modifying the original code or changing the way it is called, so there is no side effect on the original system. We do not have to worry about the original system will fail or incompatible because of it. Personally, I think this is a very useful design pattern.

The good news is that the JS decorator has been added to the ES7 's draft. It allows us to use the decorator mode more elegantly, if interested can add the next Babel Plugins plugin in advance experience. Nanyi Teacher's tutorial is also very easy to understand.

Reference documents:

JavaScript Design Patterns

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.