这是我参与更文挑战的第25天,活动详情查看:更文挑战
文章引导
代理模式篇幅较多,分为上、中、下三篇进行整理。
JavaScript实现代理模式(上篇)
内容梗概:主要介绍什么是代理模式,代理模式的简单实现
JavaScript实现代理模式(中篇)
内容梗概:分析代理模式的各种用途
JavaScript实现代理模式(下篇)
内容梗概:基于ES6 Proxy API实现更方便的代理模式
什么是代理模式
代理模式是一种结构化设计模式(小灰的文章认为也可以算作是行为型设计模式),代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用,这就是代理模式的定义。通俗的讲,代理模式的核心是在被调用方和调用方之间增加一个中介者的角色,也就是代理。
图源自小灰的文章"什么是代理模式?"
现实生活中,比如我们有租房需求,可能就需要经过房屋中介,让我们认识能够找到合适的房东。
在求职高薪岗位的时候,我们也需要找到猎头,给我们推荐合适的公司。
代理模式在现实生活中无处不在……
当然,你觉得代理模式可能会让简单的事情变复杂,但中介者的角色实际上会给你减少很多麻烦和成本,在代码中代理模式可以避免对业务类的侵入,把日志、事务之类和业务无关的辅助功能单独拎出来。
代理模式有以下两个优点:
中介隔离作用:
在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
符合代码设计的开放-封闭原则:
代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开放-封闭原则。
代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。
真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。
代理模式虽然和装饰者模式很相似,但是装饰器模式会对装饰对象增加功能,而代理模式并不会对源对象有改变,从外层去操作了这个对象,对象本身是不会有其他的改变。
代理模式的缺点主要是增加了系统的复杂度,要斟酌当前场景是不是真的需要引入代理模式。
实现代理模式
这里用《JavaScript设计模式与开发实践》一书中的例子。
小明想要追一个小姐姐,想给小姐姐送一束花表白。在不使用代理模式和使用代理模式的简单例子。
不使用代理模式
ES5
// 鲜花类
var Flower = function(){};
// 小明
var xiaoming = {
sendFlower: function( target ){
var flower = new Flower();
target.receiveFlower( flower );
}
};
// 小姐姐
var cuteGirl = {
receiveFlower: function( flower ){
console.log( '收到花 ' + flower );
}
};
// 小明将鲜花直接交给小姐姐
xiaoming.sendFlower( cuteGirl );
ES6
// 鲜花类
class Flower {
}
// 小明
let xiaoming = {
sendFlower(target){
let flower = new Flower();
target.receiveFlower( flower );
}
}
// 小姐姐
let cuteGirl = {
receiveFlower(flower){
console.log( '收到花 ' + flower );
}
}
// 小明将鲜花直接交给小姐姐
xiaoming.sendFlower( cuteGirl );
使用代理模式
以上就是不使用代理模式的例子,小明在不了解小姐姐喜好的情况下,贸然直接表白被拒绝的概率非常大,但是小明的好朋友恰好认识小姐姐的舍友,舍友会在小姐姐心情好的时候,帮小明把鲜花转交给小姐姐。
ES5
// 鲜花类
var Flower = function(){};
// 小明
var xiaoming = {
sendFlower: function( target){
var flower = new Flower();
target.receiveFlower( flower );
}
};
// 好朋友
var goodFriend = {
receiveFlower: function( flower ){
cuteGirl.listenGoodMood(function(){ // 监听A的好心情
cuteGirl.receiveFlower( flower );
});
}
};
// 小姐姐
var cuteGirl = {
receiveFlower: function( flower ){
console.log( '收到花 ' + flower );
},
listenGoodMood: function( fn ){
setTimeout(function(){ // 假设10秒之后A的心情变好
fn();
}, 10000 );
}
};
// 小明将鲜花交给好朋友,委托好朋友在小姐姐心情好的时候将鲜花转交给小姐姐
xiaoming.sendFlower( goodFriend );
ES6
// 鲜花类
class Flower {
}
// 小明
let xiaoming = {
sendFlower(target){
let flower = new Flower();
target.receiveFlower( flower );
}
};
// 好朋友
let goodFriend = {
receiveFlower(flower){
cuteGirl.listenGoodMood(() => { // 监听A的好心情
cuteGirl.receiveFlower( flower );
});
}
};
// 小姐姐
let cuteGirl = {
receiveFlower( flower ){
console.log( '收到花 ' + flower );
},
listenGoodMood( fn ){
setTimeout(() => { // 假设10秒之后A的心情变好
fn();
}, 10000 );
}
};
// 小明将鲜花交给好朋友,委托好朋友在小姐姐心情好的时候将鲜花转交给小姐姐
xiaoming.sendFlower( goodFriend );
参考资料
[CUG-GZ]前端知识进阶——代理模式
前端设计模式之代理模式
漫画:什么是 “代理模式” ?
JavaScript设计模式与开发实践
从ES6重新认识JavaScript设计模式(五): 代理模式和Proxy
使用 JavaScript 原生的 Proxy 优化应用