设计模式篇(6) 装饰器

装饰器模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

我们通过下面的实例来演示装饰器模式的用法。其中,我们将把一个形状装饰上不同的颜色,同时又不改变形状类。

介绍

意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。

何时使用:在不想增加很多子类的情况下扩展类。

如何解决:将具体功能职责划分,同时继承装饰者模式。

关键代码: 1、Component 类充当抽象角色,不应该具体实现。 2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。

应用实例: 1、孙悟空有 72 变,当他变成"庙宇"后,他的根本还是一只猴子,但是他又有了庙宇的功能。 2、不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。

优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。

缺点:多层装饰比较复杂。

使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。

注意事项:可代替继承。

实现

1,绘制形状,并给形状加一个颜色装饰器

abstract class Shape {
    abstract draw():void
}

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

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

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

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

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

2,类装饰器

namespace a {
    interface Animal {
        swings: number;
        fly: Function
    }

    function flyable(swings:number) {
        return function(target: any) {
            console.log(target);
            target.prototype.swings = swings;
            target.prototype.fly = function () {
                console.log('我能飞');
            }
        }
    }


    @flyable(2)
    class Animal {
        constructor() { }
    }

    let animal: Animal = new Animal();
    console.log(animal.swings);
    animal.fly();
}

namespace b {
    interface Person {
        protoName: string
    }
    //实例属性上的target就是类的原型对象 ,key是属性的名字
    function instancePropertyDecorator(target:any,key:any) {
        target.protoName = '我是类的原型上的属性';
        console.log('instancePropertyDecorator', target, key)
    }
    //实例方法上的target就是类的原型对象 ,key是属性的名字
    function instanceMethodDecorator(target:any,key:any) {
        console.log('classPropertyDecorator', target, key);
    }

    //静态属性时候target指的是类的构造函数,key方法名 descriptor属性描述符
    function classPropertyDecorator(target:any,key:any) {
        console.log('classPropertyDecorator',target,key);
    }
    //静态方法时候target指的是类的构造函数,key方法名 descriptor属性描述符
    function classMethodDecorator(target:any,key:any) {
        console.log('classMethodDecorator',target,key);
    }


    class Person {
        @instancePropertyDecorator
        instanceProperty: string;//实例属性
        constructor() {
            this.instanceProperty = '';
        }
        @classPropertyDecorator
        static classProperty: string;//类的静态属性
        @instanceMethodDecorator
        instanceMethod() {//实例的方法

        }
        @classMethodDecorator
        static classMethod() {//类的静态方法

        }
    }
}

3,利用装饰器实现埋点

import React from 'react';
import ReactDom from 'react-dom';

function before(beforeFn) {
    return function (target:any, methodName: any, descriptor: any) {
        let oldMethod = descriptor.value;
        descriptor.value = function () {
            beforeFn.apply(this, arguments);
            return oldMethod.apply(this, arguments);
        }
    }
}

function after(afterFn) {
    return function (target, methodName, descriptor) {
        let oldMethod = descriptor.value;
        descriptor.value = function() {
            let result = oldMethod.apply(this,arguments);
            afterFn.apply(this, arguments);
            return result;
        }
    }
}
//埋点函数
class App extends React.component {
    @before(() => {console.log('before')})
    onClickBefore() {
        console.log('beforeClick');
    }
    @after(() => {console.log('after')})
    onClickAfter() {
        console.log('afterClick'); 
    }
    @after(() => fetch('/api/report'))
    ajaxClick() {
        console.log('ajaxClick');
    }
    render() {
        return {
            <div>
                <button onClick={this.onClickBefore}>beforeClick</button>
                <button onClick={this.onClickAfter}>afterClick</button>
                <button onClick={this.ajaxClick}>ajaxClick</button>
            </div>
        }
    }
}
  1. 装饰器实现表单验证
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
      用户名:<input type="text" id="username" /> 密码:<input
        type="password"
        id="password"
      />
      <button id="sub">提交</button>

    <script>
      Function.prototype.before = function (beforeFn) {
        let thisFn = this;
        return function () {
          let pass = beforeFn();
          if (pass) {
            thisFn.apply(this, arguments);
          }
        };
      };
      function registerFn(event) {
        console.log("提交表单");
      }
      registerFn = registerFn.before(function () {
        let username = document.getElementById("username").value;
        console.log(username);
        if (!username) {
          return alert("用户名不能为空");
        }
        return true;
      });
      registerFn = registerFn.before(function () {
        let passowrd = document.getElementById("password");
        console.log(password.value)
        if (!password.value) {
          return alert("密码不能为空");
        }
        return true;
      });
      let btn = document.getElementById("sub");
      btn.addEventListener("click", registerFn);
    </script>
  </body>
</html>

热门相关:地球第一剑   网游之逆天飞扬   天启预报   仗剑高歌   寂静王冠