当前位置: 首页 > 科技观察

这个需求差点让我崩溃,幸好我理解了装饰器模式的目的

时间:2023-03-21 12:31:10 科技观察

装饰器模式(DecoratorPattern)的目的很简单,就是:在不修改原有代码的情况下增加逻辑。这句话听起来可能有些矛盾。既然要加逻辑,怎么能不修改原来的代码呢?而SOLID的开闭原则(面向对象设计的5个重要原则)正试图解决这个问题。它的内容不是改变已经写好的核心逻辑,而是扩展新的逻辑,即对扩展开放,对修改关闭。例如,如果产品需求是实现一个专门在浏览器控制台输出文本的功能,您可以这样做:classPrinter{print(text){console.log(text);}}constprinter=newPrinter();printer。print('something');//something当你对结果满意时,产品过来说:“我觉得颜色不够突出,我们换成黄色吧!”小菜一碟!放心打开百度易通操作后,将代码改为:classPrinter{print(text){console.log(`%c${text}`,'color:yellow;');}}但是Product看了看说:“这个字体有点小,应该大点,最好是高档优雅的。”好的。..”你强行控制住自己想拿刀的冲动,一边想着字体多大才高端大气,一边修改打印代码: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,`${style}color:yellow;`);}});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方法的装饰器,它们都是接收打印机,并以打印机为基础,复制返回的是同一个对象,返回的打印机和原来的区别在于每个Decorator都会在打印机的print方法中添加自己的装饰逻辑(比如改变字体、颜色或字体大小),然后调用打印机的print。使用方法如下:只要把所有的装饰逻辑都抽出来,就可以自由搭配什么时候输出什么风格。如果你想增加另一种斜体样式,只需要再增加一个装饰器,而不需要改变原来的。打印逻辑。不过需要注意的是,上面的代码只是简单的对Object进行了解构和复制。如果原型上有方法,可能会出现错误。因此,如果要深拷贝一个新的对象,需要写额外的逻辑:(originObj);originObjOwnProperties.forEach((property)=>{constprototypeDescorj=Object.getOwnDescript,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就会陷入逻辑一直在加,逻辑一直很酷的境地。了解了装饰者模式之后,就不能再这样了!所以把这个需求放在装饰器上:constpublishArticle=()=>{console.log('publisharticle');};constpublishWeibo=(publish)=>(...args)=>{publish(args);console。log('发布微博');};constpublishQzone=(publish)=>(...args)=>{publish(args);console.log('发布QQ空间');};constpublishArticleAndWeiboAndQzone=publishWeibo(publishQzone(publishArticle));前面Printer的例子是复制一个对象返回,但是如果是方法,就不用复制了,只要保证每个装饰器都会返回一个新的方法,然后执行被装饰的方法即可总结装饰器mode是一种非常好用的设计模式,在项目中经常会用到。当需求发生变化时,你会觉得某个逻辑是多余的,所以你不需要直接装饰它,也不需要修改实现逻辑。代码。每个装饰器做自己的事情,独立于其他装饰器。