Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >使用装饰者模式做有趣的事情

使用装饰者模式做有趣的事情

作者头像
嘿嘿嘿
发布于 2018-09-10 08:49:00
发布于 2018-09-10 08:49:00
44900
代码可运行
举报
文章被收录于专栏:陈纪庚陈纪庚
运行总次数:0
代码可运行
什么是装饰者模式

装饰者模式是一种为函数或类增添特性的技术,它可以让我们在不修改原来对象的基础上,为其增添新的能力和行为。它本质上也是一个函数(在javascipt中,类也只是函数的语法糖)。

我们什么时候可以弄到它呢

我们来假设一个场景,一个自行车商店有几种型号的自行车,现在商店允许用户为每一种自行车提供一些额外的配件,比如前灯、尾灯、铃铛等。每选择一种或几种配件都会影响自行车的售价。

如果按照比较传统的创建子类的方式,就等于我们目前有一个自行车基类,而我们要为每一种可能的选择创建一个新的类。可是由于用户可以选择一种或者几种任意的配件,这就导致最终可能会生产几十上百个子类,这明显是不科学的。然而,对这种情况,我们可以使用装饰者模式来解决这个问题。

自行车的基类如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Bicycle {
    // 其它方法
    wash () {}
    ride () {}
    getPrice() {
        return 200;
    }
}

那么我们可以先创建一个装饰者模式基类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class BicycleDecotator {
    constructor(bicycle) {
        this.bicycle = bicycle;
    }
    wash () {
        return this.bicycle.wash();
    }
    ride () {
        return this.bicycle.ride();
    }
    getPrice() {
        return this.bicycle.getPrice();
    }
}

这个基类其实没有做什么事情,它只是接受一个Bicycle实例,实现其对应的方法,并且将调用其方法返回而已。

有了这个基类之后,我们就可以根据我们的需求对原来的Bicycle类为所欲为了。比如我可以创建一个添加了前灯的装饰器以及添加了尾灯的装饰器:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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;
    }
}

那么,接下来我们就可以来对其自由组合了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
let bicycle = new Bicycle();
console.log(bicycle.getPrice()); // 200
bicycle = new HeadLightDecorator(bicycle); // 添加了前灯的自行车
console.log(bicycle.getPrice());  // 220
bicycle = new TailLightDecorator(bicycle); // 添加了前灯和尾灯的自行车
console.log(bicycle.getPrice()); // 240

这样写的好处是什么呢?假设说我们有10个配件,那么我们只需要写10个配件装饰器,然后就可以任意搭配成不同配件的自行车并计算价格。而如果是按照子类的实现方式的话,10个配件可能就需要有几百个甚至上千个子类了。

从例子中我们可以看出装饰者模式的适用场合:

  1. 如果你需要为类增添特性或职责,可是从类派生子类的解决方法并不太现实的情况下,就应该使用装饰者模式。
  2. 在例子中,我们并没有对原来的Bicycle基类进行修改,因此也不会对原有的代码产生副作用。我们只是在原有的基础上增添了一些功能。因此,如果想为对象增添特性又不想改变使用该对象的代码的话,则可以采用装饰者模式。

装饰者模式除了可以应用在类上之外,还可以应用在函数上(其实这就是高阶函数)。比如,我们想测量函数的执行时间,那么我可以写这么一个装饰器:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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());
做一些有趣的事情

既然知道了装饰者模式可以在不修改原来代码的情况下为其增添一些新的功能,那么我们就可以来做一些有趣的事情。

我们可以为一个类的方法提供性能分析的功能。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class TimeProfileDecorator {
  constructor(component, keys) {
    this.component = component;
    this.timers = {};
    const self = this;
    for (let i in keys) {
      let key = keys[i];
        if (typeof component[key] === 'function') {
          this[key] = function(...args) {
            this.startTimer(key);
            // 解决this引用错误问题
            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(`该函数消耗了${elapserdTime}ms`);
  }
}
// example
class 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());
对函数进行增强

节流函数or防抖函数

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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);
    }
}

缓存函数返回值

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 缓存函数结果,对于一些计算量比较大的函数效果比较明显。
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));

构造React高阶组件,为组件增加额外的功能,比如为组件提供shallowCompare功能:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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;

当然,你如果用过react-redux的话,你肯定也用过connect。其实connect也是一种高阶组件的方式。它通过装饰者模式,从Provider的context里拿到全局的state,并且将其通过props的方式传给原来的组件。

总结

使用装饰者模式可以让我们为原有的类和函数增添新的功能,并且不会修改原有的代码或者改变其调用方式,因此不会对原有的系统带来副作用。我们也不用担心原来系统会因为它而失灵或者不兼容。就我个人而言,我觉得这是一种特别好用的设计模式。

一个好消息就是,js的装饰器已经加入了es7的草案里啦。它让我们可以更加优雅的使用装饰者模式,如果有兴趣的可以添加下babel的plugins插件提前体验下。阮一峰老师的这个教程也十分浅显易懂。

参考文献:

Javascript设计模式

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-09-10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
学习《JavaScript设计模式与开发实践》- 装饰者模式
假如我们现在想设计一个可以配置自行车的游戏,自行车由玩家自行配置,包括有没有前面的框,后面的座椅,照明灯,换挡器等。
Jou
2022/08/10
2350
介绍几个JavaScript设计模式及场景应用
当然我们可以用一个通俗的说法:设计模式是解决某个特定场景下对某种问题的解决方案。因此,当我们遇到合适的场景时,我们可能会条件反射一样自然而然想到符合这种场景的设计模式。
winty
2020/04/14
7520
每天一个设计模式之装饰者模式
装饰者模式由于松耦合,多用于一开始不确定对象的功能、或者对象功能经常变动的时候。 尤其是在参数检查、参数拦截等场景。
py3study
2020/01/06
3420
装饰者模式
你在夸赞一个女性可以说,她是见过的女人中"最美丽,最知性,最善良"的。但你在夸赞一个程序员时,不能说他写的程序"最封装,最继承,最多态"。因为这三者是相生相克的。
一粒小麦
2019/11/19
5660
装饰者模式
TS 设计模式05 - 装饰者模式
在 oop 中,继承是实现多态最简单的方案。同一类的对象会有不同表现时,我们基于此基类去写派生类即可。但有时候,过度使用继承会导致程序无法维护。比如说,人有一个展示自己外观的方法,穿上不同的衣服这个展现形式就不一样。一个人可以选择穿 T-shirt,裤子,裙子,外套等等,它的顺序和搭配是不固定的,如果使用继承,我们对每种组合都需要去定义一个类,比如穿裤子的人,穿裙子的人,穿裤子和裙子的人,先穿裤子再穿外套的人......这样会是我们的程序变得非常庞大而难以维护。 事实上,不管穿什么衣服,本质上仍然是人,衣服只是基于人类的装饰而已。装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
love丁酥酥
2020/09/01
1.3K0
TS 设计模式05 - 装饰者模式
JS设计模式--装饰者模式
所谓装饰者模式,就是动态的给类或对象增加职责的设计模式。它能在不改变类或对象自身的基础上,在程序的运行期间动态的添加职责。这种设计模式非常符合敏捷开发的设计思想:先提炼出产品的MVP(Minimum Viable Product,最小可用产品),再通过快速迭代的方式添加功能。
Dickensl
2022/06/14
1.1K0
装饰者模式及其应用
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/details/51885105
程序员徐公
2018/09/18
1K0
装饰者模式及其应用
【说站】js装饰者模式是什么
在程序开发中,不希望某种类型天生庞大,一次承担很多责任,可以使用装饰者模型。装饰者的模式可以动态地给某个对象追加责任,不会影响从这个类中诞生其他对象。
很酷的站长
2022/11/24
4270
【说站】js装饰者模式是什么
你应该了解的5种TypeScript设计模式
本文最初发布于 Medium 网站,经原作者授权由 InfoQ 中文站翻译并分享。
深度学习与Python
2020/09/04
5000
你应该了解的5种TypeScript设计模式
你应该知道的 5 种 TypeScript设计模式
最近开源了一个 Vue 组件,还不够完善,欢迎大家来一起完善它,也希望大家能给个 star 支持一下,谢谢各位了。
前端小智@大迁世界
2020/11/24
4090
装饰者模式
前面分析到方案1因为咖啡单品+调料组合会造成类的倍增,因此可以做改进,将调料内置到Drink类,这样就不会造成类数量过多。从而提高项目 的维护性(如图)
@派大星
2023/06/28
1450
装饰者模式
设计模式(11)[JS版]-JavaScript中的注解之装饰器模式
装饰器模式模式动态地扩展了(装饰)一个对象的行为,同时又不改变其结构。在运行时添加新的行为的能力是由一个装饰器对象来完成的,它 "包裹 "了原始对象,用来提供额外的功能。多个装饰器可以添加或覆盖原始对象的功能。装饰器模式属于结构型模式。和适配器模式不同的是,适配器模式是原有的对象不能用了,而装饰器模式是原来的对象还能用,在不改变原有对象结构和功能的前提下,为对象添加新功能。
AlbertYang
2020/09/08
8810
设计模式(11)[JS版]-JavaScript中的注解之装饰器模式
装饰者模式Decorator
装饰者模式,动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更加有弹性的替代方案
见得乐
2022/07/20
6420
装饰者模式Decorator
前端也要学系列:设计模式之装饰者模式
今天我们来讲另外一个非常实用的设计模式:装饰者模式。这个名字听上去有些莫名其妙,不着急,我们先来记住它的一个别名:包装器模式。
司想君
2018/08/01
3970
结合案例深入解析装饰者模式
允许向一个现有的对象添加新的功能。同时又不改变其结构,它是作为现有的类的一个包装。
李红
2019/06/14
3650
结合案例深入解析装饰者模式
JavaScript 设计模式学习第十四篇-装饰者模式
装饰者模式(Decorator Pattern)又称装饰器模式,在不改变原对象的基础上,通过对其添加属性或方法来进行包装拓展,使得原有对象可以动态具有更多功能。
越陌度阡
2020/11/26
4330
JavaScript 设计模式学习第十四篇-装饰者模式
装饰者模式
假设我们需要买一杯奶茶,计算总共花了多少钱,这个比较容易,一个class一个getPrice就行。那如果我要买一杯珍珠奶茶呢?再new一个?如果还有珍珠果肉奶茶?再new一个?显然不科学,所以我们需要装饰者模式
提莫队长
2019/02/21
3770
Java设计模式-装饰者模式
装饰者模式:在不改变原类文件以及不使用继承的情况下,动态地将责任附加到对象上,从而实现动态拓展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
蒋老湿
2019/08/29
8600
Java设计模式-装饰者模式
干货 | ES6 系列之我们来聊聊装饰器
       点击上方“腾讯NEXT学院”关注我们 Decorator 装饰器主要用于: 1. 装饰类 2. 装饰方法或属性 1 .装饰类 @annotationclass MyClass { } function annotation(target) { target.annotated = true;} 2. 装饰方法或属性 class MyClass { @readonly method() { }} function readonly(targe
腾讯NEXT学位
2020/02/11
6380
干货 | ES6 系列之我们来聊聊装饰器
设计模式之装饰者模式
1)、咖啡种类 : Espresso、ShortBlack、LongBlack、Decaf 2)、调料 : Milk、Soy、Chocolate 3)、扩展性好、改动方便、维护方便
用户9854323
2022/06/25
1770
设计模式之装饰者模式
相关推荐
学习《JavaScript设计模式与开发实践》- 装饰者模式
更多 >
领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验