likes
comments
collection
share

| 设计模式 | - 装饰器模式

作者站长头像
站长
· 阅读数 17

装饰器模式

  • 在不改变其原有的结构和功能为对象添加新功能的模式其实就叫做装饰器模式
  • 装饰比继承更加灵活,可以实现装饰者和被装饰者之间松耦合
  • 被装饰者可以使用装饰者动态地增加和撤销功能

| 设计模式 | -  装饰器模式

abstract class Shape {
    abstract draw(): void;
}
class Circle extends Shape {
    draw() {
        console.log('绘制圆形');
    }
}
class Rectangle extends Shape {
    draw() {
        console.log('绘制矩形');
    }
}

abstract class ColorfulShape extends Shape {
    public constructor(public shape: Shape) {
        super();
    }
    abstract draw(): void;
}

class RedColorfulShape extends ColorfulShape {
    draw() {
        this.shape.draw();
        console.log('把边框涂成红色');
    }
}
class GreenColorfulShape extends ColorfulShape {
    draw() {
        this.shape.draw();
        console.log('把边框涂成绿色');
    }
}

let circle = new Circle();
let redColorfulShape = new RedColorfulShape(circle);
redColorfulShape.draw();

let rectangle = new Rectangle();
let greenColorfulShape = new GreenColorfulShape(rectangle);
greenColorfulShape.draw();
function decortor(input, fn) {
  const input = document.querySelector(input);
  if (typeof input.onclick === "function") {
    const oldCallBack = input.onclick;
    //点击之后,除了原来的回调。还扩展了其他的fn
    input.onclick = function () {
      oldCallBack();
      fn();
    };
  } else {
    input.onclick = fn;
  }
}
💡 常见的装饰器有类装饰器、属性装饰器、方法装饰器和参数装饰器

类装饰器

  • 类装饰器在类声明之前声明,用来监控、修改或替换类定义
  • 参数是类的定义或者说构造函数
export { }
namespace decorator {
    interface Animal {
        swings: string;
        fly: any
    }
    function flyable(target: any) {
        console.log(target);

        target.prototype.swings = 2;
        target.prototype.fly = function () {
            console.log('I can fly');
        }
    }
    @flyable
    class Animal {
        constructor() { }
    }
    let animal: Animal = new Animal();
    console.log(animal.swings);
    animal.fly();
}

7.3.1.2 属性装饰器

  • 属性装饰器表达式会在运行时当作函数被调用
  • 属性分为实例属性和类属性
  • 方法分为实例方法和类方法
namespace property_namespace {
    //实例属性target是类的原型对象,key是属性名称
    function instancePropertyDecorator(target: any, key: string) {
    }
    //类属性target是的构造函数
    function classPropertyDecorator(target: any, key: string) {
    }
    //实例方法装饰器target是原型对象,key方法名,descriptor是方法描述符
    function instanceMethodDecorator(target: any, key: string, descriptor: PropertyDescriptor) {
    }
    //类方法装饰器target是类的构造函数
    function classMethodDecorator(target: any, key: string, descriptor: PropertyDescriptor) {
    }
    class Person {
        @instancePropertyDecorator
        instanceProperty: string;
        @classPropertyDecorator
        public static classProperty: string;
        @instanceMethodDecorator
        instanceMethod() {
            console.log('instanceMethod');
        }
        @classMethodDecorator
        classMethod() {
            console.log('classMethod');
        }
    }
}

7.3.1.3 core-decorator

let { readonly } = require('core-decorators');
function deprecate(msg: string, options: any) {
    return function (target: any, attr: any, descriptor: any) {
        //DEPRECATION Calculator#add: This function will be removed in future versions.
        let oldVal = descriptor.value;
        descriptor.value = function (...args: any[]) {
            let message = msg ? msg : `DEPRECATION ${target.constructor.name}#${attr}: This function will be removed in future versions.`;
            let see = options && options.url ? `see ${options.url}` : ``;
            console.warn(message + '\r\n' + see);
            return oldVal(...args);
        }
    }
}
class Calculator {
    @deprecate('stop using this', { url: '<http://www.baidu.com>' })
    add(a: number, b: number) {
        return a + b;
    }
}
let calculator = new Calculator();
calculator.add(1, 2);
7.3.2 AOP概念

AOP

  • 装饰器在 AOP 概念中大放光彩,意为面向切面编程 → 可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术

| 设计模式 | -  装饰器模式

  • 装饰器埋点
import React from 'react';
import { render } from 'react-dom';
import { before, after } from './track';

class App extends React.Component {
    @before(() => console.log('点击方法执行前'))
    onClickBeforeButton() {
        console.log('beforeClick');
    }

    @after(() => console.log('点击方法执行后'))
    onClickAfterButton() {
        console.log('afterClick');
    }

    @after(() => fetch('/api/report'))
    onClickAjaxButton() {
        console.log('ajaxClick');
    }

    render() {
        return (
            <div>
                <button onClick={this.onClickBeforeButton}>beforeClick</button>
                <button onClick={this.onClickAfterButton}>afterClick</button>
                <button onClick={this.onClickAjaxButton}>ajaxClick</button>
            </div>
        )
    }
}
render(<App />, document.getElementById('root'));
export const before = function (beforeFn) {
    return function (target, methodName, descriptor) {
        let oldMethod = descriptor.value;
        descriptor.value = function () {
            beforeFn.apply(this, arguments);
            return oldMethod.apply(this, arguments);
        }
    }
}

export const after = function (afterFn) {
    return function (target, methodName, descriptor) {
        let oldMethod = descriptor.value;
        descriptor.value = function () {
            oldMethod.apply(this, arguments);
            afterFn.apply(this, arguments);
        }
    }
}