当前位置: 首页 > Web前端 > JavaScript

#使用装饰器模式解决频繁修改的需要

时间:2023-03-27 15:49:17 JavaScript

目的装饰器模式(DecoratorPattern)的目的很简单,就是:在不修改原有代码的情况下增加逻辑。这句话听起来可能有些矛盾。既然加了逻辑,怎么可能不修改原来的代码呢?而SOLID的开闭原则(面向对象设计的五个重要原则)正试图解决这个问题。它的内容不是改变已经写好的核心逻辑,而是扩展新的逻辑,即对扩展开放,对修改开放。关闭。例如,如果我们要实现一个专门在浏览器控制台输出文本的功能,我们可能会这样做:classPrinter{print(text){console.log(text);}}constprinter=newPrinter();printer.print('something');//something当你对你的结果满意时,产品过来说:“我觉得颜色不够突出,我们换成黄色吧!”很简单的!放心打开百度易通操作后,把代码改成下面这样:}}但是产品看了看说:“这个字体有点小,应该大点,最好是高档优雅的。”好的。.."你强行控制住自己想拿刀的冲动,一边想着字体多大才高端大气,一边修改打印代码:classPrinter{print(text){console.log(`%c${text}`,'color:yellow;font-size:36px;');}}这次换了你之后,你的心里充满了mmp,不停地想:“这样真的好吗?“你不能保证这是最后一次修改,而且可能不止一个产品对你指指点点。你盯着显示器发呆,直到电脑进入休眠模式,你的仇恨都反映在屏幕上。”沉着脸,想着不断变得凌乱的打印方法,不知道该如何应对那些没完没了的需求……在上面的例子中,原来的Printer根据需求写出了它应该有的逻辑,即就是在控制台输出一些文字。也就是说,当“在控制台输出一些文本”的逻辑写完后,打印机就可以结束了,因为它是打印机的全部。那么在这种情况下如何更改字体或颜色逻辑呢?这时候就需要装饰器模式了。DecoratorPattern(装饰者模式)首先修改原始Printer,使其可以支持扩展样式:classPrinter{print(text='',style=''){console.log(`%c${text}`,style);}}然后创建装饰器来改变字体和颜色:constyellowStyle=(printer)=>({...printer,print:(text='',style='')=>{printer.print(text,`${样式}颜色:黄色;`);}});constboldStyle=(printer)=>({...printer,print:(text='',style='')=>{printer.print(text,`${style}font-weight:bold;`);}});constbigSizeStyle=(printer)=>({...printer,print:(text='',style='')=>{printer.print(text,`${style}font-size:36px;`);}});代码中的yellowStyle、boldStyle、bigSizeStyle分别是print方法的装饰器,它们都接收打印机,并基于打印机,复制同一个对象并返回。返回的打印机与原始打印机的不同之处在于,每个装饰器都会在打印机的打印方法中添加自己的装饰逻辑(例如更改字体、颜色或字体大小)。调用打印机的打印。使用方法如下:只要把所有的装饰逻辑都抽出来,就可以自由搭配什么时候输出什么风格。如果你想增加另一种斜体样式,只需要再增加一个装饰器,而不需要改变原来的。打印逻辑。不过需要注意的是,上面的代码只是简单的对Object进行了解构和复制。如果原型上有方法,可能会出现错误。因此,如果你想深拷贝一个新的对象,你需要写额外的逻辑:constcopyObj=(originObj)=>{constoriginPrototype=Object.getPrototypeOf(originObj);让newObj=Object.create(originPrototype);constoriginObjOwnProperties=Object.getOwnPropertyNames(originObj);originObjOwnProperties.forEach((property)=>{constDescorperTypeProtyoriginObj,property);Object.defineProperty(newObj,property,prototypeDesc);});returnnewObj;}然后在装饰器中更改上面代码中的copyObj以正确复制相同的对象:constyellowStyle=(printer)=>{constdecorator=copyObj(printer);decorator.print=(text='',style='')=>{printer.print(text,`${style}color:yellow;`);};returndecorator;};其他情况因为我们使用的语言是JavaScript,所以没有使用类,只是简单的装饰了一个方法,比如下面这个发布文章的publishArticle:constpublishArticle=()=>{console.log('发表文章');};文章发表后想在微博或QQ空间更新,怎么办?是不是像下面的代码?constpublishArticle=()=>;{console.log('发布文章');console.log('发送微博消息');console.log('发送QQ空间消息');};这显然不好!publishArticle应该只需要发布文章的逻辑!而如果以后第三方服务平台越来越多,那么publishArticle就会陷入一直加逻辑,一直爽的境地。了解了装饰者模式之后,就不能再这样了!所以把这个要求放在装饰器上:constpublishArticle=()=>{console.log('publisharticle');};constpublishWeibo=(publish)=>(...args)=>{publish(args);console.log('发布微博更新');};constpublishQzone=(publish)=>(...args)=>{publish(args);console.log('发布QQ空间更新');};constpublishArticleAndWeiboAndQzone=publishWeibo(publishQzone(publishArticle));前面Printer的例子是复制一个对象返回,但是如果是方法,就不用复制了,只要保证每个装饰器都会返回一个新的方法,然后执行被装饰的方法求和即可装饰器模式是一种非常有用的设计模式,在项目中经常使用到。需求变化的时候,感觉某个逻辑是多余的,就直接不去装饰,也不需要去搞。修改实现逻辑的代码。每个装饰器做自己的事情,独立于其他装饰器。