懒得改变原始对象?JavaScript 代理模式教你怎样一步步偷懒!
前言
系列首发于公众号『非同质前端札记』 ,若不想错过更多精彩内容,请“星标”一下,敬请关注公众号最新消息。
懒得改变原始对象?JavaScript 代理模式教你怎样一步步偷懒!
何为代理模式
例如,你想预约一家公司的董事长会面,按照正常流程,你只能通过先联系他的秘书,然后跟他的秘书预约时间,约好时间后你们两个才能会面。(也就是说,代理模式的关键是有个中间者来协调你与对方之间的事情,只能通过中间者将事情转达给另一方)。
代理模式的应用
在 Web 开发中,我们通常会在网站或程序中用到图片,当某张图片过大时或网络不佳时,图片区域就是显示一段空白或者直接没有显示(没有设置图片区域高度,待图片加载完成后才会完成自适应图片高度来进行 layout)。友好的做法是在真正的图片还未加载完成时图片区域显示一个 loading 占位符。
如下代码:
代理的意义
可能会有人疑惑,为什么一个图片预加载功能,非要使用什么模式来实现,不适用照样也能实现啊,如下:
为了说明为什么要使用代理模式的意义,我们必须引入一个面向对象的设计原则——单一职责原则,单一职责原则指的是:
对于一个类而言,应该仅有一个引起它改变的入口
。如果一个对象承担了很多职责,那这个对象将变得很臃肿,那引起它变化的原因可能会有很多个。如果一个对象承担的职责过多,就等于把这些职责耦合在了一起,这种耦合会导致代码很脆弱和低内聚的设计。在面向对象的程序设计中,大多数情况下,若违反其他任何原则,同时将违反开放-封闭原则。
代理合并请求数据
比如有一个定时任务,会每个一段时间要往数据库中存储一些数据,如果当有新数据进来时,就调用存储数据的接口,这样既浪费性能,代码的执行效率也不会太高。那要求我们写一个
可参考如下:
上面我们实现了基本的数据定时存储功能,但并不完整,如果我们想再加一个暂停,重新开始,停止的逻辑,那如何编写呢?
如下:
缓存代理
在为了一些开销较大的运算结果和接口请求时,我们需要使用缓存代理来进行优化,为防止重复的请求造成性能浪费。
题目如下:
通过代理对象来实现对电影信息的缓存
创建一个真实对象 MovieService,其中包含一个方法 getMovie(id) 用于获取电影信息。模拟返回一些电影数据。
创建一个代理对象 CachedMovieServiceProxy,通过代理对象实现对电影信息的缓存。
代理对象内部维护一个缓存对象 cache,在第一次请求时将电影信息存入缓存,并在后续请求时直接从缓存中获取。
当调用代理对象的 getMovie(id) 方法时,如果缓存中存在对应的电影信息,则直接返回缓存数据;否则,调用真实对象的 getMovie(id) 方法获取电影信息,并将结果存入缓存。
示例输入和输出:
实现如下:
代理模式的优缺点
优点:
控制访问/增加安全性:可通过代理对象对真实对象的访问进行控制,增加了对真实对象的保护
延迟初始化:将高开销的操作延迟到真正需要的时候,可优化一些性能
封装性:可隐藏对象的复杂性,只需要与代理对象打交道即可
缺点:
增加复杂性:虽然代理模式可分离关注点,但同时也增加了代码的复杂性,因为需要创建和管理代理对象
透明性问题:虽然透明性是一个优点,但如果过度使用,可能导致代码难以理解和调试。
性能开销:代理对象需要拦截所有对原始对象的访问,这会导致一些性能开销。
代理模式的适用场景
访问控制:可用于限制对对象的访问,例如来控制对一些敏感数据的访问。
虚拟代理:对一个对象需要从网络上加载大量的数据时,可使用虚拟代理来优化,在需要时再加载数据。
保护代理:由于代理模式可以控制对真实对象的访问,因此可以保护代理。
缓存代理:可用于实现一个高度重用,并且这个操作很好使的情况。
智能引用代理:当需要在访问对象时需要执行一些额外的操作时,可使用智能引用代理。
日志记录:可用于在调用真实对象的方法前后进行日志记录,包括参数,返回结果等信息,便于调试和排查问题。
Tip: 文章部分内容参考于曾探
大佬的《JavaScript 设计模式与开发实践》。文章仅做个人学习总结和知识汇总
特殊字符描述:
问题标注
Q:(question)
答案标注
R:(result)
注意事项标准:
A:(attention matters)
详情描述标注:
D:(detail info)
总结标注:
S:(summary)
分析标注:
Ana:(analysis)
提示标注:
T:(tips)
往期推荐:
最后:
版权声明: 本文为 InfoQ 作者【控心つcrazy】的原创文章。
原文链接:【http://xie.infoq.cn/article/41f768cad1e8a122068735086】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论