摘要:但是,这样做的后果就是,我们会不断的改变本体,就像把凤姐送去做整形手术一样。在中,我们叫做引用装饰。所以,这里引入的装饰模式装饰亲切,熟悉,完美。实例讲解装饰上面那个例子,只能算是装饰模式的一个不起眼的角落。
装饰者,英文名叫decorator. 所谓的"装饰",从字面可以很容易的理解出,就是给 土肥圆,化个妆,华丽的转身为白富美,但本体还是土肥圆。
说人话.
咳咳~
在js里面一切都是对象,而且函数就是一等对象。 在普通的Object中,我们可以很容易的添加属性或者其他方法,当然函数也不例外。 但是,这样做的后果就是,我们会不断的改变本体,就像把凤姐送去做整形手术一样。 当然,结果有好有坏,也许凤姐就是下一个angelababy,也许... 所以,为了我们代码的纯洁度(就算你是丑小鸭), 我们可以不动刀子的情况下,让你变得又白又美。
引用装饰这个是装饰的初级阶段,就是抹点粉而已。 在js中,我们叫做引用装饰。
talk is cheap, show u code
//我们给jimmy函数额外添加其他的功能 var jimmy = function(){ console.log("jimmy"); } var _jimmy = jimmy; jimmy = function(){ _jimmy(); console.log("I love HuaHua"); } jimmy();
这个的应用场景主要就是在多人协作和框架设计里面。比如,李冰岩已经使用了onload函数,但是,小舟又想使用onload函数。 这样会造成一个问题,如果小舟直接改动的话,他需要看的是李冰岩写的蜜汁代码,而且还要防止不会引起错误。这无疑是很困难的,所以在这里,可以使用引用装饰,来给onload在添加一层。
//这是小李的蜜汁代码 var xiaoLi = function(){ console.log("蜜汁代码"); } window.onload = xiaoLi; //小李已经绑定好onload函数了 //接下来小舟需要改动onload代码 var fn = window.onload; var xiaoZhou = function(){ fn(); conosle.log("整洁代码"); } window.onload = function(){ //根据onload的特性,直接覆盖掉小李的code xiaoZhou(); }
所以引用装饰的用处还是蛮大的。
大你妹啊~~
啊。。。。
(另一Me) 我们来分析一下,上面那个引用模式有什么弊端(好处已经都知道了).
首先,我们使用引用模式的时候,必定会添加一个多余的引用对象,比如上文的"fn".
而且随着你程序链的增加,中间对象一定会和你节点同等数量的。当然,起名我就不说了,关键是,一大堆无用的代码放在那里,感觉很不爽的。 所以,这里引入AOP的装饰模式.
亲切,熟悉,完美。 我们见过AOP应该不止一次了,在职责链模式使用过,在迭代器模式使用过等等。使用这么多次,好像还没有对AOP做一些基本解释呢?所以,这里给大家咻咻.
AOP中文名叫面向切面编程。 先说一下这个名词,“面向”这词应该不用解释,关键"切面"是什么鬼。 如果大家做过sangwich,应该就知道,首先我们买来一块面包,需要将面包切开,然后在切面上面加上一些flavoring,比如蔬菜,火腿,培根之类的。 恩,对比js程序来说,一个程序链就相当于你买回来的面包,flavoring就是你想加的功能函数,如何将函数正确的放置在程序链中合适的位置,这就是AOP做的事情。
首先,再次将两个动态函数咻咻:
Function.prototype.after = function(fn){ var _this = this; return function(){ var res = _this.apply(this,arguments); fn.apply(this,arguments); return res; } } Function.prototype.before = function(fn){ var _this = this; return function(){ fn.apply(this,arguments); return _this.apply(this,arguments); } }
这两个函数的组合构成了js中AOP模式的精华.而AOP最常用的就是讲与业务逻辑无关的功能动态织入到主程序中。
talk is cheap , show u code
举个栗子吧: 使用AOP计算程序运行事件
//纯手写计算函数运行事件 function factorial(n) { //最基本的阶乘计算 if (n === 1) return 1; return n * factorial(n - 1); } function calTime(n){ var start = new Date().getMilliseconds(); factorial(n); console.log(new Date().getMilliseconds() - start+"ms"); } calTime(1000);
可以得出耗费的时间,但是,如果还有其他的函数需要测试,那么这么做的意义并没有很大的价值。我们使用AOP进行重构。
function factorial(n) { //最基本的阶乘计算 if (n === 1) return 1; return n * factorial(n - 1); } var calTime = (function(){ var start; return function(){ if(!start){ //给开始时间赋值 start = new Date().getMilliseconds(); }else{ console.log(new Date().getMilliseconds()-start+"ms"); start = undefined; } } })(); var calcu = factorial.before(calTime).after(calTime)(200);
这样很好的将计时功能从业务逻辑中提取出来,而且看着真的很有angelababy的味道诶.
使用AOP的时候需要注意一点就是,before&after执行完后,返回的结果都是第一个函数的内容。
var result = function(){ return 1; }.before(function(){ return 2; }).after(function(){ return 3; }); console.log(result()); //1
我们大致的了解了AOP的用法和理论,可以针对于开头所说的例子进行重构.
window.onload = function(){ console.log("小李的蜜汁代码"); } var fn = window.onload; fn.before(function(){ console.log("整洁代码"); }); window.onload = fn;
看起来,比上面那个栗子清晰很多,而且使用before和after也十分利于代码的阅读。
实例讲解AOP装饰上面那个例子,只能算是AOP装饰模式的一个不起眼的角落。 AOP引用的场景在js中,或者说在任何一门语言中都是大放光彩的。 在js中,"细粒度"对象是程序中复用性最高的对象,能把对象用细粒度的形式表示,那么AOP无疑是最佳的选择。
在写业务逻辑的时候,我们最大的问题就是判断逻辑,使用大量的if语句,而这都可以经过思考巧妙化解。比如,我在写购买课程的时候就会遇到一些逻辑。 只有当课程数目符合要求的时候,购买的效果才能有效.
按正常的业务逻辑编写
var buy = function(){ if(confirm()){ //验证购买信息是否合法 http.buyCourse("xxx"); //发起请求 } } var confirm = function(){ console.log("验证购买数量"); } document.querySelector(".buy").addEventListener("click",function(){ buy(); },false);
使用AOP装饰模式重构后
var buy = function(){ http.buyCourse("xxx"); //给后台发起请求,验证 } var confirm = function(){ console.log("验证购买数量"); } var buy = buy.before(confirm); document.querySelector(".buy").addEventListener("click",function(){ buy(); },false);
美美代码的 满满即视感!!!
不够,老板,再来份糖炒栗子~
好嘞~
这里我们只是获取函数的结果,那我们想直接干预传递的参数,可以吗?
当然可以。
我们研究一下,before的内部构造(after是一样的)
Function.prototype.before = function(fn){ var _this = this; return function(){ fn.apply(this,arguments); return _this.apply(this,arguments); } }
这里,由于arguments是引用类型,如果fn改变了arguments,则会反映到_this.apply的arguments也会发生改变。 而这个应用场景就是,给ajax的地址添加上你需要的参数。
在实际项目中,一开始的接口,就是一个普普通通的地址,发请求,然后获取参数。
http.ajax(url,type).then(...)
想这样的使用,但是某天,你的leader提高了要求等级,将地址后面都加上一个token参数,或者说一个口令的md5或sha1的计算值,我想,这尼玛工作量应该不小。
当然,我们可以直接将url进行传递。
var http = { ajax1(url){ url += param.getToken(); sendAjax(url); }, ajax2(url){ ... } ... }
而且,万一哪天你的leader说,哎,这样做安全性还是不太高,要不加两个token混淆一下。
啊~啊~啊~啊~(混淆你妹啊,过不过年啦)
如果你继续这么写,我相信,年终奖是有的,但是,春运火车票你估计是摸不着了。
所以可以使用AOP进行动态织入。要知道,参数,我AOP也是可以动的。
function dealUrl(url){ url+=param.getToken(); } http.ajax = http.ajax.before(dealUrl); http.ajax("www.example.com"); //此时的Url = www.example.com?token=23jkfd3kjfdksjfkjds
而且,这个处理url函数,我也是可以扔到任意一个js文件里面复用的耶.
棒!!!
我AOP可以动你要的参数,而且,我还可以把我的结果给你是咻咻,如果我不让你执行,你永远也不会执行,哈哈哈哈~~~~
对不起,,上面那段是我意淫AOP说的。。。 其实AOP可以算是万能的配置工具,比如表单验证吧。 我们经常会把表单验证和表单结果发送耦合在一起。
像这样
var sendRes = function(){ if(user.userName === ""){ alert("用户名不能为空~"); return; }else if(user.password === ""){ alert("密码不能为空~"); return; } http.sendUser("xxx"); //验证成功发送用户信息 }
一个函数里面同时含有了两个职责,一个验证一个发送用户信息。 所以我们现在的主要目的就是解耦。
回想一下,以前表单验证我们使用策略模式,解耦了验证,这里我们再次使用。
var sendRes = function(){ var detect = new Detect(); //策略者模式 detect.add(user.userName,["notEmpty"]); detect.add(user.password,["notEmpty"]); var notPass = detect.getResult(); if(notPass){ //如果没通过 console.log(notPass); return; } http.sendUser("xxx"); //验证成功发送用户信息 }
可以使用上面那个验证,但是结果是,验证和策略模式还是在一起。我们再使用AOP进行解耦。首先我们得重构一下before函数
Function.prototype.before = function(fn){ var _this = this; return function(){ var res = fn.apply(this,arguments); //值为Boolean,表示是否继续向下传递 if(res==="next"){ //如果返回不成立,则继续向下传递 return _this.apply(this,arguments); } return res; } }
看到这里,有些同学应该明白是怎么一回事了。没错,就是根据before里面验证的结果判断是否执行下个发送请求的功能函数。
当然,如果不想污染原型,你也可以自定义一个函数。
var before = function(beforeFn,fn){ return function(){ var res = beforeFn.apply(this,arguments); if(res==="next"){ return fn.apply(this,arguments); } } }
这样写也是可以的。
我们先按原型方式写,这样直观一点
var sendRes = function(){ http.sendUser("xxx"); //验证成功发送用户信息 } sendRes = sendRes.before(function(){ var detect = new Detect(); //策略者模式 detect.add(user.userName,["notEmpty"]); detect.add(user.password,["notEmpty"]); var notPass = detect.getResult(); if(notPass){ //如果没通过 console.log(notPass); } return "next"; });
可以看出,验证那部分已经完全和发送用户信息的功能函数完全给解耦了。 这样不仅提高了函数的重用性,而且也让你的代码朝着“细粒度”方向大步前进.
辩证装饰者模式其实,装饰者模式和职责链模式的形式是完全一样的,所以,他们的弊端也是类似的。链造的过长,对于性能来说就是一次rape.所以,还是那句话,不要为了模式而模式,没有万能的模式。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/78552.html
摘要:下装饰者的实现了解了装饰者模式和的概念之后,我们写一段能够兼容的代码来实现装饰者模式原函数拍照片定义函数装饰函数加滤镜用装饰函数装饰原函数这样我们就实现了抽离拍照与滤镜逻辑,如果以后需要自动上传功能,也可以通过函数来添加。 showImg(https://segmentfault.com/img/bVbueyz?w=852&h=356); 什么是装饰者模式 当我们拍了一张照片准备发朋友...
摘要:装饰者模式定义装饰者模式能够在不改变对象自身的基础上,在程序运行期间给对像动态的添加职责。与继承相比,装饰者是一种更轻便灵活的做法。 装饰者模式 定义 : 装饰者(decorator)模式能够在不改变对象自身的基础上,在程序运行期间给对像动态的添加职责。与继承相比,装饰者是一种更轻便灵活的做法。 在不改变对象自身的基础上,在程序运行期间给对象动态地添加一些额外职责 特点 : 可以动态的...
摘要:用户名不能为空密码不能为空校验未通过使用优化代码返回的情况直接,不再执行后面的原函数用户名不能为空密码不能为空 本文是《JavaScript设计模式与开发实践》的学习笔记,例子来源于书中,对于设计模式的看法,推荐看看本书作者的建议。 什么是装饰者模式? 给对象动态增加职责的方式成为装饰者模式。 装饰者模式能够在不改变对象自身的基础上,在运行程序期间给对象动态地添加职责。这是一种轻便灵活...
摘要:会一直完善下去,欢迎建议和指导,同时也欢迎中用到了那些设计模式中用到了那些设计模式这两个问题,在面试中比较常见。工厂设计模式使用工厂模式可以通过或创建对象。 我自己总结的Java学习的系统知识点以及面试问题,已经开源,目前已经 41k+ Star。会一直完善下去,欢迎建议和指导,同时也欢迎Star: https://github.com/Snailclimb... JDK 中用到了那...
摘要:修饰者模式设计模式中的修饰者模式能动态地给目标对象增加额外的职责。修饰者模式调用的时序图如下图所示。的实现原理和修饰者模式类似。 在上边一篇文章中我们介绍了Spring AOP的基本概念,今天我们就来学习一下与AOP实现相关的修饰者模式和Java Proxy相关的原理,为之后源码分析打下基础。 修饰者模式 Java设计模式中的修饰者模式能动态地给目标对象增加额外的职责(Respon...
阅读 2932·2021-11-24 10:22
阅读 3006·2021-11-23 10:10
阅读 1264·2021-09-28 09:35
阅读 1701·2019-08-29 13:16
阅读 1375·2019-08-26 13:29
阅读 2763·2019-08-26 10:27
阅读 660·2019-08-26 10:09
阅读 1414·2019-08-23 18:05