博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Javascript设计模式之装饰者模式详解篇
阅读量:5298 次
发布时间:2019-06-14

本文共 6709 字,大约阅读时间需要 22 分钟。

一、前言:

装饰者模式(Decorator Pattern):在不改变原类和继承的情况下动态扩展对象功能,通过包装一个对象来实现一个新的具有原对象相同接口的新的对象。

装饰者模式的特点:

1. 在不改变原对象的原本结构的情况下进行功能添加。
2. 装饰对象和原对象具有相同的接口,可以使客户以与原对象相同的方式使用装饰对象。
3. 装饰对象中包含原对象的引用,即装饰对象是真正的原对象经过包装后的对象。

 

二、Javascript装饰者模式详解:

描述:

装饰者模式中,可以在运行时动态添加附加功能到对象中。当处理静态类时,这可能是一个挑战。在Javascript中,由于对象是可变的,因此,添加功能到对象中的过程本身并不是问题。
装饰者模式的一个比较方便的特征在于其预期行为的可定制和可配置特性。可以从仅具有一些基本功能的普通对象开始,然后从可用装饰资源池中选择需要用于增强普通对象的哪些功能,并且按照顺序进行装饰,尤其是当装饰顺序很重要的时候。
实现装饰者模式的其中一个方法是使得每个装饰者成为一个对象,并且该对象包含了应该被重载的方法。每个装饰者实际上继承了目前已经被前一个装饰者进行增强后的对象。每个装饰方法在“继承的对象”上调用了同样的方法并获取其值,此外它还继续执行了一些操作。

先上实例1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//需要装饰的类(函数)
function 
Macbook() {
    
this
.cost =
function 
() {
        
return 
1000;
    
};
}
 
//计算商品的包装费
function 
PackagingFee(macbook) {
    
this
.cost =
function 
() {
        
return 
macbook.cost() + 75;
    
};
}
 
//计算商品的运费
function 
Freight(macbook) {
    
this
.cost =
function 
() {
        
return 
macbook.cost() + 300;
    
};
}
 
//计算商品的保险费用
function 
Insurance(macbook) {
    
this
.cost =
function 
() {
        
return 
macbook.cost() + 250;
    
};
}
 
// 用法
var 
myMacbook =
new 
Insurance(
new 
Freight(
new 
PackagingFee(
new 
Macbook())));
console.log(myMacbook.cost());
//1625

我们简单的分析下上面的代码,上面的代码中,一共定义了四个函数(其中一个需要修饰的函数,三个用于修饰的函数)。

然后,声明一个变量myMacbook指向new出来的Insurance对象,Insurance对象的形参指向new出来的Freight对象,Freight对象的形参指向new出来的PackagingFee对象,PackagingFee对象的形参指向new出来的Macbook对象。
接下来,调用myMacbook的cost方法。从上面的分析,我们可以得出 myMacbook.cost()的值等于(Freight对象的cost方法+250),Freight对象的cost方法等于(PackagingFee对象的cost方法+300),PackagingFee对象的cost方法等于(Macbook对象的cost方法+75)。
所以最终的结果是:myMacbook.cost()的值 = 250 + (300 + (75 + 1000)) = 1625。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 用法
var 
myMacbook =
new 
Insurance(
new 
Freight(
new 
PackagingFee(
new 
Macbook())));
console.log(myMacbook.cost());
//1625
 
//上面的代码等价于下面拆分后的代码,或许拆分后代码你更能看出前后的逻辑性
var 
macbook =
new 
Macbook();
var 
package 
=
new 
PackagingFee(macbook);
var 
freight =
new 
Freight(
package
);
var 
myMacbook =
new 
Insurance(freight);
 
//当然,如果你不想声明这么多变量(macbook、package、freight),只用一个变量也是可以的
var 
macbook =
new 
Macbook();
macbook =
new 
PackagingFee(macbook);
macbook =
new 
Freight(macbook);
var 
myMacbook =
new 
Insurance(macbook);

再看看实例2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function 
ConcreteClass() {
    
this
.performTask =
function 
() {
        
this
.preTask();
        
console.log(
'doing something'
);
        
this
.postTask();
    
};
}
 
function 
AbstractDecorator(decorated) {
    
this
.performTask =
function 
() {
        
decorated.performTask();
    
};
}
 
function 
ConcreteDecoratorClass(decorated) {
    
this
.base = AbstractDecorator;
    
this
.base(decorated);
// add performTask method
 
    
decorated.preTask =
function 
() {
        
console.log(
'pre-calling..'
);
    
};
 
    
decorated.postTask =
function 
() {
        
console.log(
'post-calling..'
);
    
};
}
 
var 
concrete =
new 
ConcreteClass();
var 
decorator1 =
new 
ConcreteDecoratorClass(concrete);
decorator1.performTask();
//pre-calling..
//doing something
//post-calling..
实例2实际上和实例1是非常类似的,我们来简单分析下吧。首先,实例2中定义了三个函数,然后声明了两个变量concrete和decorator1,最后调用了decorator1的performTask方法。
粗看一眼,ConcreteDecoratorClass里面好像并没有performTask方法。我们先来分析下面的两行代码:
1
2
3
var 
concrete =
new 
ConcreteClass();
//声明一个变量concrete指向new出来的ConcreteClass对象
var 
decorator1 =
new 
ConcreteDecoratorClass(concrete);
//声明一个变量decorator1指向new出来的ConcreteDecoratorClass对象,并传入变量concrete作为形参
然后,我们再来逐行分析下ConcreteDecoratorClass函数里面的代码:
1
2
3
this
.base = AbstractDecorator;
//定义一个当前对象(decorator1)的base属性,并指向函数AbstractDecorator
this
.base(decorated);
//调用base属性指向的函数,也就是调用AbstractDecorator函数,同时传入形参decorated,形参decorated指向new出来的ConcreteClass对象
说到这里,好像还是没有分析出ConcreteDecoratorClass函数里面有performTask方法,重点是看 
"this"
ConcreteDecoratorClass函数中的this指向new出来的ConcreteDecoratorClass对象(也就是和decorator1指向同一个对象);
AbstractDecorator函数里面的this关键是看哪个对象来调用这个函数,this就指向哪个对象(从代码 “this.base = AbstractDecorator; this.base(decorated);” 中我们可以看出是new出来的ConcreteDecoratorClass对象在调用AbstractDecorator函数),所以AbstractDecorator函数里面的this指向new出来的ConcreteDecoratorClass对象(也和decorator1指向同一个对象)。
总结下来,我们会发现,在上面的代码中,不管是ConcreteDecoratorClass函数里面的this,还是AbstractDecorator函数里面的this,都指向new出来的ConcreteDecoratorClass对象。 
所以,当我们执行decorator1.performTask()时,它会继续执行匿名函数中的代码(decorated.performTask();),匿名函数中的decorated形参指向new出来的ConcreteClass对象,并执行该对象的performTask方法。

最后看看实例3:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
var 
tree = {};
tree.decorate =
function 
() {
    
console.log(
'Make sure the tree won\'t fall'
);
};
 
tree.getDecorator =
function 
(deco) {
    
tree[deco].prototype =
this
;
    
return 
new 
tree[deco];
};
 
tree.RedApples =
function 
() {
    
this
.decorate =
function 
() {
        
this
.RedApples.prototype.decorate();
// 第7步:先执行原型(这时候是Angel了)的decorate方法
        
console.log(
'Add some red apples'
);
// 第8步 再输出 red
        
// 将这2步作为RedApples的decorate方法
    
}
};
 
tree.BlueApples =
function 
() {
    
this
.decorate =
function 
() {
        
this
.BlueApples.prototype.decorate();
// 第1步:先执行原型的decorate方法,也就是tree.decorate()
        
console.log(
'Put on some blue apples'
);
// 第2步 再输出blue
        
// 将这2步作为BlueApples的decorate方法
    
}
};
 
tree.Angel =
function 
() {
    
this
.decorate =
function 
() {
        
this
.Angel.prototype.decorate();
// 第4步:先执行原型(这时候是BlueApples了)的decorate方法
        
console.log(
'An angel on the top'
);
// 第5步 再输出angel
        
// 将这2步作为Angel的decorate方法
    
}
};
 
tree = tree.getDecorator(
'BlueApples'
);
// 第3步:将BlueApples对象赋给tree,这时候父原型里的getDecorator依然可用
tree = tree.getDecorator(
'Angel'
);
// 第6步:将Angel对象赋给tree,这时候父原型的父原型里的getDecorator依然可用
tree = tree.getDecorator(
'RedApples'
);
// 第9步:将RedApples对象赋给tree
 
tree.decorate();
// 第10步:执行RedApples对象的decorate方法
//Make sure the tree won't fall
//Add blue apples
//An angel on the top
//Put on some red apples
实例3看起来很复杂,实际上分析逻辑还是和前面两个实例一样,我们可以看出实例3中一共声明了5个函数表达式。我们重点分析下下面的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//tree.getDecorator('BlueApples')返回new出来的tree.BlueApples的实例对象,并将该对象赋值给空的tree对象
tree = tree.getDecorator(
'BlueApples'
);
//new出来的tree.BlueApples的实例对象的原型指向 --> 空对象tree
 
//tree.getDecorator('Angel')返回new出来的tree.Angel的实例对象(这行代码中的第二个tree已经是上面一行代码运行结果后的tree.BlueApples的实例对象)
tree = tree.getDecorator(
'Angel'
);
//new出来的tree.Angel的实例对象的原型指向 --> tree.BlueApples的实例对象
 
//tree.getDecorator('RedApples')返回new出来的tree.RedApples的实例对象(这行代码中的第二个tree已经是上面一行代码运行结果后的tree.Angel的实例对象)
tree = tree.getDecorator(
'RedApples'
);
//new出来的tree.RedApples的实例对象的原型指向 --> tree.Angel的实例对象
 
//调用tree.decorate(),这里的tree已经是new出来的tree.RedApples的实例对象了。
//tree.RedApples的实例对象的decorate属性方法里面的第一行代码是 “this.RedApples.prototype.decorate()”
//结合上面的分析可以得出以下的原型链结构:
//this.RedApples.prototype --> tree.Angel;
//tree.Angel.prototype --> tree.BlueApples;
//tree.BlueApples.prototype --> 空对象tree
tree.decorate();

分析到这里,就不难知道最后的输出结果了。

 

三、其他:

我们可以看出本文章中的装饰者模式案例中用了很多this,对this不太了解的朋友可以移步到 。

本文案例建议复制下来逐行分析,赶紧行动起来吧!

转载于:https://www.cnblogs.com/libin-1/p/6291442.html

你可能感兴趣的文章
python workspace_python报错汇总
查看>>
python 模拟登录web 库_[Python] Python 模拟登录,并请求
查看>>
python图色模拟脚本_利用python编写一个图片主色转换的脚本
查看>>
java jdts mssql_JSP直连MSSQL代码示例
查看>>
java socket datainputstream_Java DataInputStream.available方法代碼示例
查看>>
php接口百度百科_php接口由什么组成
查看>>
php手机验证码源码,php验证码实现源码
查看>>
php fpm高并发,php服务器高并发优化思路
查看>>
php gd 图像翻转,php(gd库)输出中文图像的转换函数
查看>>
java 表头添加复选框,表头带有CheckBox可以实现全选的jtable
查看>>
java b组 小计算器,简单计算器..
查看>>
php server port,$_SERVER[‘SERVER_PORT’]关于php5.2一个bug
查看>>
php 类 init,PHP内核探索:类的定义
查看>>
java的二叉树树一层层输出,Java构造二叉树、树形结构先序遍历、中序遍历、后序遍历...
查看>>
meep php,麻省理工时域差分软件 MEEP windows 下编译开发(一)——准备工作
查看>>
matlab的清除0,matlab中的平均值clear %清除变量dx=0.01*2*pi; %间隔x=0:dx:2*pi; %自变量向量y=...
查看>>
php 循环套 重复,php 循环套循环 出现重复数据
查看>>
mysql distince,MySQL学习(未完待续)
查看>>
php libevent 定时器,PHP 使用pcntl和libevent实现Timer功能
查看>>
对数字进行 混淆 php,解密混淆的PHP程序
查看>>