装饰器模式(Decorator Pattern)允许向一个现有的对象动态添加新的功能,同时又不改变其结构。相比JavaScript中通过鸡肋的继承来给对象增加功能来说,装饰器模式相比生成子类更为灵活。
装饰模式和适配器模式都是 包装模式 (Wrapper Pattern),它们都是通过封装其他对象达到设计的目的的,但是它们的形态有很大区别。

适配器模式我们使用的场景比较多,比如连接不同数据库的情况,你需要包装现有的模块接口,从而使之适配数据库 —— 好比你手机使用转接口来适配插座那样;
装饰模式不一样,仅仅包装现有的模块,使之 “更加华丽” ,并不会影响原有接口的功能 —— 好比你给手机添加一个外壳罢了,并不影响手机原有的通话、充电等功能;

下面通过一个实例介绍装饰器模式的使用方法。孙悟空一出生虽然不同寻常,但也是只普通的猴子

  • 首先创建一个普通猴子对象
  1. function Monkey() {
  2. console.log("很久很久以前,海边的一块石头,吸日月之精华,集天地之灵气,突然有一天,石头崩裂,从里面窜出一只泼猴!");
  3. }
  4. Monkey.prototype = {
  5. toString: function () {
  6. console.log('我是泼猴');
  7. },
  8. attack: function () {
  9. console.log("猴拳出击");
  10. },
  11. defend: function () {
  12. console.log("我跳,我跳,我跳跳跳");
  13. }
  14. }
  • 接着创建一个装饰器'类'
  1. // 创建装饰器,接收 monkey 对象作为参数。
  2. var Decorator = function (monkey) {
  3. this.monkey = monkey;
  4. }
  5. // 装饰者要实现这些相同的方法
  6. Decorator.prototype = {
  7. toString: function () {
  8. this.monkey.toString();
  9. },
  10. attack: function () {
  11. this.monkey.attack();
  12. },
  13. defend: function () {
  14. this.monkey.defend();
  15. }
  16. }
  • 创建具体的装饰器对象

接下来我们要为每一个功能创建一个装饰者对象,重写父级方法,添加我们想要的功能。该装饰器对象继承自装饰器'类',也接收monkey实例作为参数。
1.找菩提祖师学72变

  1. var Decorate72Changes = function (monkey) {
  2. Decorator.call(this, monkey);
  3. console.log("学会72变");
  4. }
  5. Decorate72Changes.prototype = new Decorator();
  6. //重写父类方法
  7. Decorate72Changes.prototype.toString = function () {
  8. console.log("我是美猴王");
  9. }
  10. Decorate72Changes.prototype.defend = function () {
  11. this.monkey.defend();
  12. console.log("俺变,俺变,俺变变变");
  13. }

2.找东海龙王要金箍棒

  1. var DecorateGoldenCudgel = function (monkey) {
  2. Decorator.call(this, monkey);
  3. console.log("获得金箍棒");
  4. }
  5. DecorateGoldenCudgel.prototype = new Decorator();
  6. //重写父类方法
  7. DecorateGoldenCudgel.prototype.toString = function () {
  8. console.log("我是齐天大圣,孙悟空");
  9. }
  10. DecorateGoldenCudgel.prototype.attack = function () {
  11. this.monkey.attack();
  12. console.log("吃我一棒");
  13. }

3.太上老君炼丹炉练就火眼金睛

  1. var DecorateSharpEyes = function (monkey) {
  2. Decorator.call(this, monkey);
  3. console.log("获得火眼金睛");
  4. }
  5. DecorateSharpEyes .prototype = new Decorator();
  6. //重写父类方法
  7. DecorateSharpEyes .prototype.toString = function () {
  8. console.log("我是孙行者");
  9. }
  10. DecorateSharpEyes.prototype.findMonster = function () {
  11. console.log("妖怪,哪里跑");
  12. }
  • 使用装饰器装饰泼猴
  1. var monkey=new Monkey();//很久很久以前,海边的一块石头,吸日月之精华,集天地之灵气,突然有一天,石头崩裂,从里面窜出一只泼猴!
  2. monkey.toString();//我是泼猴
  3. monkey.attack();//猴拳出击
  4. monkey.defend();//我跳,我跳,我跳跳跳
  5. var monkeyKing=new Decorate72Changes(monkey);//学会72变
  6. monkeyKing.toString();//我是美猴王
  7. monkeyKing.attack();//猴拳出击
  8. monkeyKing.defend();//我跳,我跳,我跳跳跳;俺变,俺变,俺变变变
  9. var monkeyGod=new DecorateGoldenCudgel(monkeyKing);//获得金箍棒
  10. monkeyGod.toString();//我是齐天大圣,孙悟空
  11. monkeyGod.attack();//猴拳出击;吃我一棒
  12. monkeyGod.defend();//我跳,我跳,我跳跳跳;俺变,俺变,俺变变变
  13. var monkeySun=new DecorateSharpEyes(monkeyGod);//获得火眼金睛
  14. monkeySun.toString();//我是孙行者
  15. monkeySun.findMonster();//妖怪,哪里跑

装饰者模式是保持对象功能差异性的一种很好的方式,从长远来看有助于提高代码的可维护性。

在 ES6 中增加了对类对象的相关定义和操作(比如 class 和 extends ),这就使得我们在多个不同类之间共享或者扩展一些方法或者行为的时候,变得并不是那么优雅。这个时候,我们就需要一种更优雅的方法来帮助我们完成这些事情。
装饰器最早是在 python 2.4 里增加的功能,它的主要作用与装饰者模式类似,是给一个已有的方法或类扩展一些新的行为,而不是去直接修改它本身。

  1. def decorator(f):
  2. print "my decorator"
  3. return f
  4. @decorator
  5. def myfunc():
  6. print "my function"
  7. myfunc()
  8. # my decorator
  9. # my function

这里的 @decorator 就是我们说的装饰器。上面的代码中利用装饰器给目标方法执行前打印出了一行文本,并且并没有对原方法做任何的修改。ES7中的装饰器借鉴了Python的思想,实现方法也类似

  1. function sayYourName(target,key,descriptor){
  2. descriptor.value=()=>{
  3. console.log("我是泼猴");
  4. }
  5. console.log("报上名来")
  6. return descriptor;
  7. }
  8. class Monkey{
  9. @sayYourName
  10. toString(){}
  11. }
  12. monkey=new Monkey();
  13. monkey.toString();
  14. //报上名来
  15. //我是泼猴

ES6+的这种语法实际上是一种语法糖,而实际上当我们给一个类添加一个属性的时候,会调用到 Object.defineProperty 这个方法,它会接受三个参数:target 、name 和 descriptor ,所以上面的代码在未添加装饰器时解析成ES5是这样的:

  1. function Monkey() {}
  2. Object.defineProperty(Monkey.prototype, "toString", {
  3. value: function() {},
  4. enumerable: true,
  5. configurable: true,
  6. writable: true
  7. });

在加上装饰器后,会在执行第二步的时候安装上这个装饰器(可以看成把这个步骤分成了两小步),通过执行装饰器函数返回一个descriptor属性描述符


  1. function sayYourName(target,key,descriptor){
  2. descriptor.value=()=>{
  3. console.log("我是泼猴");
  4. }
  5. console.log("报上民来")
  6. return descriptor;
  7. }
  8. function Monkey() {}
  9. var descriptor={
  10. value:function(){},
  11. enumerable: true,
  12. configurable: true,
  13. writable: true
  14. }
  15. // 装饰器工厂函数 接收的参数与 Object.defineProperty 一致
  16. descriptor = sayYourName(Monkey.prototype, 'toString', descriptor)
  17. Object.defineProperty(Monkey.prototype, "toString", descriptor);
  18. monkey=new Monkey();
  19. monkey.toString();

当装饰器作用于类属性方法时,参数中的target为类的原型,装饰器还可以作用于类本身,此时的target参数是类本身。

  1. function isAnimal(target) {
  2. target.isAnimal = true;
  3. }
  4. @isAnimal
  5. class Monkey{
  6. toString(){}
  7. }
  8. console.log(Monkey.isAnimal); // true

装饰器函数还可以是一个工厂函数,可以传递参数

  1. function animal(name) {
  2. return function (target) {
  3. target.call = name;
  4. }
  5. }
  6. @animal("猴子")
  7. class Monkey { }
  8. @animal("猪")
  9. class Pig { }
  10. console.log(Monkey.call);
  11. console.log(Pig.call);

有了前面的基础,可以使用ES7的decorator实现一开始的装饰器模型。

  1. function decorate72Changes(target, key, descriptor) {
  2. const method = descriptor.value;
  3. descriptor.value = ()=>{
  4. method.apply(target);
  5. console.log("俺变,俺变,俺变变变");
  6. }
  7. console.log("学会72变");
  8. return descriptor;
  9. }
  10. function decorateGoldenCudgel(target, key, descriptor) {
  11. const method = descriptor.value;
  12. descriptor.value = ()=>{
  13. method.apply(target);
  14. console.log("吃我一棒");
  15. }
  16. console.log("获得金箍棒");
  17. return descriptor;
  18. }
  19. function decorateSharpEyes(target, key, descriptor) {
  20. descriptor.value = ()=>{
  21. console.log("妖怪,哪里跑");
  22. }
  23. console.log("获得火眼金眼");
  24. return descriptor;
  25. }
  26. function decorateToString(target, key, descriptor) {
  27. descriptor.value = ()=>{
  28. console.log("我是孙行者");
  29. }
  30. return descriptor;
  31. }
  32. class Monkey {
  33. constructor(){
  34. console.log("很久很久以前,海边的一块石头,吸日月之精华,集天地之灵气,突然有一天,石头崩裂,从里面窜出一只泼猴!");
  35. }
  36. @decorateToString
  37. toString(){
  38. console.log('我是泼猴');
  39. }
  40. @decorateGoldenCudgel
  41. attack(){
  42. console.log("猴拳出击");
  43. }
  44. @decorate72Changes
  45. defend(){
  46. console.log("我跳,我跳,我跳跳跳");
  47. }
  48. @decorateSharpEyes
  49. findMonster(){}
  50. }
  51. monkeySun=new Monkey();
  52. monkeySun.defend();
  53. monkeySun.attack();
  54. monkeySun.findMonster();

要运行上述代码还需要babel转译
1.安装基础依赖包

  1. npm i babel-plugin-transform-decorators-legacy babel-register --save-dev
  2. 安装:
  3. babel-plugin-transform-decorators-legacy
  4. babel-register
  5. transform-decorators-legacy
  6. 是第三方插件,用于支持decorators
  7. babel-register
  8. 用于接入node api

运行方法一:命令行操作

  1. babel --plugins transform-decorators-legacy input.js>input.es5.js
  2. 然后直接运行es5的代码

运行方法二:require hook

  1. require('babel-register')({
  2. plugins: ['transform-decorators-legacy']
  3. });
  4. require("./input.js")

装饰器模式&&ES7 Decorator 装饰器的更多相关文章

  1. 装饰器模式(Decorator Pattern)

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

  2. C#设计模式-装饰器模式(Decorator Pattern)

    引言 当我们完成一个软件产品开发后就需要对其进行各种测试,适配快速迭代下质量的保障.当有一个完善的产品的对象后,如果我们想要给他添加一个测试功能,那么我们可以用一个新的类去装饰它来实现对原有对象职责的 ...

  3. PHP设计模式之装饰器模式(Decorator)

    PHP设计模式之装饰器模式(Decorator) 装饰器模式 装饰器模式允许我们给一个类添加新的功能,而不改变其原有的结构.这种类型的类属于结构类,它是作为现有的类的一个包装 装饰器模式的应用场景 当 ...

  4. C#设计模式:装饰者模式(Decorator Pattern)

    一,装饰者模式(Decorator Pattern):装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能. 二,在以上代码中我们是中国人是根本行为,我们给中国人装饰我会说英语 ...

  5. 今天俺要说一说装饰着模式(Decorator)

    前言:装饰者模式,又叫做装饰器模式.顾名思义,就是给对象包裹一层,包装.让它变成你喜欢的对象.这种模式在我们开发中经常会用到,它是一种处理问题的技巧,即不让程序死板,也可以扩展程序. (一)何时能用到 ...

  6. 装饰者模式(Decorator)---结构型

    1 基础知识 定义:在不改变原有对象的基础上,将功能附加到对象上即动态地给一个对象添加一些额外的职责.特征:提供了比继承更有弹性的替代方案. 本质:动态组合. 使用场景:扩展一个类的功能或给一个类添加 ...

  7. 【设计模式 - 9】之装饰者模式(Decorator)

    1      模式简介 装饰者模式允许向一个现有的对象添加新的功能,同时又不改变其结构. 装饰者模式的思路是用"调料"对象将原始对象进行层层包裹,同时其属性.动作层层传递,达到最终 ...

  8. 设计模式-装饰者模式(Decorator Pattern)

    本文由@呆代待殆原创,转载请注明出处. 此设计模式遵循的设计原则之一:类应该支持扩展,而拒绝修改(Open-Closed Principle) 装饰者模式简述 装饰者模式通过组合的方式扩展对象的特性, ...

  9. 【装饰者模式】Decorator Pattern

    装饰者模式,这个模式说我一直记忆深刻的模式,因为Java的IO,我以前总觉得Java的IO是一个类爆炸,自从明白了装饰者模式,Java的IO体系让我觉得非常的可爱,我们现在看看什么是装饰者,然后再来看 ...

随机推荐

  1. 一键分享代码(提供能分享到QQ空间、新浪微博、人人网等的分享功能)

    <html> <head></head> <body> <div class="xl_2"> <span styl ...

  2. flask 状态保持session和上下文session的区别

    问题场景: 在falsk项目中导入了两个session:    首先,配置文件config.py文件中 有个 flask_session扩展导入了Session  ( from flask_sessi ...

  3. 前端获取checkbox复选框的值 通过数组形式传递

    html代码: <form role="form" class="select_people"> <div style="displ ...

  4. Raspberry pi connect temperature and humidity to onenet (移动云平台)

    工具 树莓派3 modelB 一个 dht11温湿度传感器一个  onenet平台 安装好requests库的python(一定要安装好不然代码不能正确运行,可以参考我的另一篇博文点击打开链接) 树莓 ...

  5. JPA 连表查询

    A表和B表 @Entity @Table(name = "A", schema = "kps", catalog = "kps") @Dyn ...

  6. Linux基本命令总结(六)

    接上篇: 27,diff在命令行中打印每一个行的改动.最新版本的diff还支持二进制文件.diff程序的输出被称为补丁 (patch),因为Linux系统中还有一个patch程序,可以根据diff的输 ...

  7. 分布式监控系统开发【day37】:监控客户端开发(五)

    一.目录结构 二.模块方法调用关系总图 三.入口文件main 1.解决了说明问题 1.客户端就干了一件事情,干什么事情 收集数据汇报给服务端? 但是我这个客户端是插件形式2.首先必须要传一个参数,st ...

  8. python 错误捕获机制分析

    python语言是编程中使用率在Top 3之内的语言.python语言以灵活与简单著称,那么越是灵活的语言越需要判断出错的功力. 简单示例 以下是一个简单的错误程序,被除数不可为0,那么看看该代码的执 ...

  9. 第29月第27天 Error: Multiple commands produce

    1. 解决方法可以有两种,一种是不使用New Build System,在File > Project/Workspace Settings中的Share Project/Workspace S ...

  10. openwrt 加入nand flash的支持

    参考链接 :   https://blog.csdn.net/wwx0715/article/details/77189456?locationNum=9&fps=1