装饰器模式&&ES7 Decorator 装饰器
装饰器模式(Decorator Pattern)允许向一个现有的对象动态添加新的功能,同时又不改变其结构。相比JavaScript中通过鸡肋的继承来给对象增加功能来说,装饰器模式相比生成子类更为灵活。
装饰模式和适配器模式都是 包装模式 (Wrapper Pattern),它们都是通过封装其他对象达到设计的目的的,但是它们的形态有很大区别。
适配器模式我们使用的场景比较多,比如连接不同数据库的情况,你需要包装现有的模块接口,从而使之适配数据库 —— 好比你手机使用转接口来适配插座那样;
装饰模式不一样,仅仅包装现有的模块,使之 “更加华丽” ,并不会影响原有接口的功能 —— 好比你给手机添加一个外壳罢了,并不影响手机原有的通话、充电等功能;
下面通过一个实例介绍装饰器模式的使用方法。孙悟空一出生虽然不同寻常,但也是只普通的猴子
- 首先创建一个普通猴子对象
function Monkey() {
console.log("很久很久以前,海边的一块石头,吸日月之精华,集天地之灵气,突然有一天,石头崩裂,从里面窜出一只泼猴!");
}
Monkey.prototype = {
toString: function () {
console.log('我是泼猴');
},
attack: function () {
console.log("猴拳出击");
},
defend: function () {
console.log("我跳,我跳,我跳跳跳");
}
}
- 接着创建一个装饰器'类'
// 创建装饰器,接收 monkey 对象作为参数。
var Decorator = function (monkey) {
this.monkey = monkey;
}
// 装饰者要实现这些相同的方法
Decorator.prototype = {
toString: function () {
this.monkey.toString();
},
attack: function () {
this.monkey.attack();
},
defend: function () {
this.monkey.defend();
}
}
- 创建具体的装饰器对象
接下来我们要为每一个功能创建一个装饰者对象,重写父级方法,添加我们想要的功能。该装饰器对象继承自装饰器'类',也接收monkey实例作为参数。
1.找菩提祖师学72变
var Decorate72Changes = function (monkey) {
Decorator.call(this, monkey);
console.log("学会72变");
}
Decorate72Changes.prototype = new Decorator();
//重写父类方法
Decorate72Changes.prototype.toString = function () {
console.log("我是美猴王");
}
Decorate72Changes.prototype.defend = function () {
this.monkey.defend();
console.log("俺变,俺变,俺变变变");
}
2.找东海龙王要金箍棒
var DecorateGoldenCudgel = function (monkey) {
Decorator.call(this, monkey);
console.log("获得金箍棒");
}
DecorateGoldenCudgel.prototype = new Decorator();
//重写父类方法
DecorateGoldenCudgel.prototype.toString = function () {
console.log("我是齐天大圣,孙悟空");
}
DecorateGoldenCudgel.prototype.attack = function () {
this.monkey.attack();
console.log("吃我一棒");
}
3.太上老君炼丹炉练就火眼金睛
var DecorateSharpEyes = function (monkey) {
Decorator.call(this, monkey);
console.log("获得火眼金睛");
}
DecorateSharpEyes .prototype = new Decorator();
//重写父类方法
DecorateSharpEyes .prototype.toString = function () {
console.log("我是孙行者");
}
DecorateSharpEyes.prototype.findMonster = function () {
console.log("妖怪,哪里跑");
}
- 使用装饰器装饰泼猴
var monkey=new Monkey();//很久很久以前,海边的一块石头,吸日月之精华,集天地之灵气,突然有一天,石头崩裂,从里面窜出一只泼猴!
monkey.toString();//我是泼猴
monkey.attack();//猴拳出击
monkey.defend();//我跳,我跳,我跳跳跳
var monkeyKing=new Decorate72Changes(monkey);//学会72变
monkeyKing.toString();//我是美猴王
monkeyKing.attack();//猴拳出击
monkeyKing.defend();//我跳,我跳,我跳跳跳;俺变,俺变,俺变变变
var monkeyGod=new DecorateGoldenCudgel(monkeyKing);//获得金箍棒
monkeyGod.toString();//我是齐天大圣,孙悟空
monkeyGod.attack();//猴拳出击;吃我一棒
monkeyGod.defend();//我跳,我跳,我跳跳跳;俺变,俺变,俺变变变
var monkeySun=new DecorateSharpEyes(monkeyGod);//获得火眼金睛
monkeySun.toString();//我是孙行者
monkeySun.findMonster();//妖怪,哪里跑
装饰者模式是保持对象功能差异性的一种很好的方式,从长远来看有助于提高代码的可维护性。
在 ES6 中增加了对类对象的相关定义和操作(比如 class 和 extends ),这就使得我们在多个不同类之间共享或者扩展一些方法或者行为的时候,变得并不是那么优雅。这个时候,我们就需要一种更优雅的方法来帮助我们完成这些事情。
装饰器最早是在 python 2.4 里增加的功能,它的主要作用与装饰者模式类似,是给一个已有的方法或类扩展一些新的行为,而不是去直接修改它本身。
def decorator(f):
print "my decorator"
return f
@decorator
def myfunc():
print "my function"
myfunc()
# my decorator
# my function
这里的 @decorator 就是我们说的装饰器。上面的代码中利用装饰器给目标方法执行前打印出了一行文本,并且并没有对原方法做任何的修改。ES7中的装饰器借鉴了Python的思想,实现方法也类似
function sayYourName(target,key,descriptor){
descriptor.value=()=>{
console.log("我是泼猴");
}
console.log("报上名来")
return descriptor;
}
class Monkey{
@sayYourName
toString(){}
}
monkey=new Monkey();
monkey.toString();
//报上名来
//我是泼猴
ES6+的这种语法实际上是一种语法糖,而实际上当我们给一个类添加一个属性的时候,会调用到 Object.defineProperty 这个方法,它会接受三个参数:target 、name 和 descriptor ,所以上面的代码在未添加装饰器时解析成ES5是这样的:
function Monkey() {}
Object.defineProperty(Monkey.prototype, "toString", {
value: function() {},
enumerable: true,
configurable: true,
writable: true
});
在加上装饰器后,会在执行第二步的时候安装上这个装饰器(可以看成把这个步骤分成了两小步),通过执行装饰器函数返回一个descriptor属性描述符
function sayYourName(target,key,descriptor){
descriptor.value=()=>{
console.log("我是泼猴");
}
console.log("报上民来")
return descriptor;
}
function Monkey() {}
var descriptor={
value:function(){},
enumerable: true,
configurable: true,
writable: true
}
// 装饰器工厂函数 接收的参数与 Object.defineProperty 一致
descriptor = sayYourName(Monkey.prototype, 'toString', descriptor)
Object.defineProperty(Monkey.prototype, "toString", descriptor);
monkey=new Monkey();
monkey.toString();
当装饰器作用于类属性方法时,参数中的target为类的原型,装饰器还可以作用于类本身,此时的target参数是类本身。
function isAnimal(target) {
target.isAnimal = true;
}
@isAnimal
class Monkey{
toString(){}
}
console.log(Monkey.isAnimal); // true
装饰器函数还可以是一个工厂函数,可以传递参数
function animal(name) {
return function (target) {
target.call = name;
}
}
@animal("猴子")
class Monkey { }
@animal("猪")
class Pig { }
console.log(Monkey.call);
console.log(Pig.call);
有了前面的基础,可以使用ES7的decorator实现一开始的装饰器模型。
function decorate72Changes(target, key, descriptor) {
const method = descriptor.value;
descriptor.value = ()=>{
method.apply(target);
console.log("俺变,俺变,俺变变变");
}
console.log("学会72变");
return descriptor;
}
function decorateGoldenCudgel(target, key, descriptor) {
const method = descriptor.value;
descriptor.value = ()=>{
method.apply(target);
console.log("吃我一棒");
}
console.log("获得金箍棒");
return descriptor;
}
function decorateSharpEyes(target, key, descriptor) {
descriptor.value = ()=>{
console.log("妖怪,哪里跑");
}
console.log("获得火眼金眼");
return descriptor;
}
function decorateToString(target, key, descriptor) {
descriptor.value = ()=>{
console.log("我是孙行者");
}
return descriptor;
}
class Monkey {
constructor(){
console.log("很久很久以前,海边的一块石头,吸日月之精华,集天地之灵气,突然有一天,石头崩裂,从里面窜出一只泼猴!");
}
@decorateToString
toString(){
console.log('我是泼猴');
}
@decorateGoldenCudgel
attack(){
console.log("猴拳出击");
}
@decorate72Changes
defend(){
console.log("我跳,我跳,我跳跳跳");
}
@decorateSharpEyes
findMonster(){}
}
monkeySun=new Monkey();
monkeySun.defend();
monkeySun.attack();
monkeySun.findMonster();
要运行上述代码还需要babel转译
1.安装基础依赖包
npm i babel-plugin-transform-decorators-legacy babel-register --save-dev
安装:
babel-plugin-transform-decorators-legacy
babel-register
transform-decorators-legacy:
是第三方插件,用于支持decorators
babel-register:
用于接入node api
运行方法一:命令行操作
babel --plugins transform-decorators-legacy input.js>input.es5.js
然后直接运行es5的代码
运行方法二:require hook
require('babel-register')({
plugins: ['transform-decorators-legacy']
});
require("./input.js")
装饰器模式&&ES7 Decorator 装饰器的更多相关文章
- 装饰器模式(Decorator Pattern)
装饰器模式 一.什么是装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装 ...
- C#设计模式-装饰器模式(Decorator Pattern)
引言 当我们完成一个软件产品开发后就需要对其进行各种测试,适配快速迭代下质量的保障.当有一个完善的产品的对象后,如果我们想要给他添加一个测试功能,那么我们可以用一个新的类去装饰它来实现对原有对象职责的 ...
- PHP设计模式之装饰器模式(Decorator)
PHP设计模式之装饰器模式(Decorator) 装饰器模式 装饰器模式允许我们给一个类添加新的功能,而不改变其原有的结构.这种类型的类属于结构类,它是作为现有的类的一个包装 装饰器模式的应用场景 当 ...
- C#设计模式:装饰者模式(Decorator Pattern)
一,装饰者模式(Decorator Pattern):装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能. 二,在以上代码中我们是中国人是根本行为,我们给中国人装饰我会说英语 ...
- 今天俺要说一说装饰着模式(Decorator)
前言:装饰者模式,又叫做装饰器模式.顾名思义,就是给对象包裹一层,包装.让它变成你喜欢的对象.这种模式在我们开发中经常会用到,它是一种处理问题的技巧,即不让程序死板,也可以扩展程序. (一)何时能用到 ...
- 装饰者模式(Decorator)---结构型
1 基础知识 定义:在不改变原有对象的基础上,将功能附加到对象上即动态地给一个对象添加一些额外的职责.特征:提供了比继承更有弹性的替代方案. 本质:动态组合. 使用场景:扩展一个类的功能或给一个类添加 ...
- 【设计模式 - 9】之装饰者模式(Decorator)
1 模式简介 装饰者模式允许向一个现有的对象添加新的功能,同时又不改变其结构. 装饰者模式的思路是用"调料"对象将原始对象进行层层包裹,同时其属性.动作层层传递,达到最终 ...
- 设计模式-装饰者模式(Decorator Pattern)
本文由@呆代待殆原创,转载请注明出处. 此设计模式遵循的设计原则之一:类应该支持扩展,而拒绝修改(Open-Closed Principle) 装饰者模式简述 装饰者模式通过组合的方式扩展对象的特性, ...
- 【装饰者模式】Decorator Pattern
装饰者模式,这个模式说我一直记忆深刻的模式,因为Java的IO,我以前总觉得Java的IO是一个类爆炸,自从明白了装饰者模式,Java的IO体系让我觉得非常的可爱,我们现在看看什么是装饰者,然后再来看 ...
随机推荐
- Jekins相关笔记
Jekins忘记密码操作 https://blog.csdn.net/intelrain/article/details/78749635 Jekins重启 https://www.cnblogs.c ...
- poj2778(AC自动机+矩阵快速幂)
题意:给你n个字符串,问你长度为m的字符串且字符串中不含有那n个子串的字符串的数量 解题思路:这道题一开始就不太懂,还以为是组合数学的题目,后面看了别人的博客,才知道这是属于AC自动机的另一种用法,是 ...
- bzoj3277-串
Code #include<cstdio> #include<iostream> #include<cmath> #include<cstring> # ...
- 如何在源码里修改openwrt root密码
root密码在etc/shadow文件里,但里边的密码是加密过后的.具体的加密方式我不清楚,应该是Linux里的一套加密算法.但要达到修改源码密码的目的,我还是想到了一个间接的办法.首先在源码里改成t ...
- 怎样在ISE14.7中固化FLASH文件
前言 当工程开发完成后,bit文件类型掉电后会消失,而此时采用FLASH固化就很重要了. 软件版本:ISE14.7 流程 1.对生成FLASH文件进行设置:配置速率为33,选择66貌似配置失败,中庸之 ...
- Windows下Redis的安装和部署
Redis 简介 Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库. Redis 与其他 key - value 缓存产品有以下三个特点: Redis支持数据的持久 ...
- zsh fg: no job control in this shell.
图片的上面就是将一个应用按Ctrl+Z,把任务放到后台里面.没法fg将任务回到前台运行. 在.zshrc中添加set -m. 具体原因不明.我切换到root用户里,没有出现这个问题.将我的.zshrc ...
- JS学习笔记Day8
一.内置函数Math 1.Math 1)Math.abs() 求绝对值 2)Math.PI 圆周率 2.求近似值: 1)Math.round() 四舍五入(负数: >0.5 进一 <=0. ...
- Linux设备树(六 memory&chosen节点)
六 memory&chosen节点 根节点那一节我们说过,最简单的设备树也必须包含cpus节点和memory节点.memory节点用来描述硬件内存布局的.如果有多块内存,既可以通过多个memo ...
- CMDB资产管理系统开发【day27】:理解RESTful架构
理解RESTful架构 越来越多的人开始意识到,网站即软件,而且是一种新型的软件. 这种"互联网软件"采用客户端/服务器模式,建立在分布式体系上,通过互联网通信,具有高延时(hig ...