关于前端:用装饰器模式解决频繁修改的需求

7次阅读

共计 3563 个字符,预计需要花费 9 分钟才能阅读完成。

目标

装璜器模式(Decorator Pattern)的目标非常简单,那就是:

在不批改原有代码的状况下减少逻辑。

这句话听起来可能有些矛盾,既然都要减少逻辑了,怎么可能不去批改原有的代码?但 SOLID(向对象设计 5 大重要准则)的凋谢关闭准则就是在试图解决这个问题,其内容是不去改变曾经写好的外围逻辑,但又可能裁减新逻辑,也就是对扩大凋谢,对批改敞开。

举个例子,咱们想要实现一个专门在浏览器的控制台中输入文本的性能,可能会这样做:

class Printer {print(text) {console.log(text);
  }
}

const printer = new Printer();
printer.print('something'); // something

在你称心的看着本人的成绩时,产品过去说了一句:“我感觉色彩不够突出,还是把它改成黄色的吧!”

小菜一碟!你自信的关上百度一通操作之后,把代码改成了上面这样子:

class Printer {print(text) {console.log(`%c${text}`,'color: yellow;');
  }
}

但产品看了看又说:“这个字体有点太小了,再大一点,最好是高端大气上档次那种。

”好吧。。。“你强行管制着本人拿刀的激动,一边推敲多大的字体才是高端大气上档次,一边批改 print 的代码:

class Printer {print(text) {console.log(`%c${text}`,'color: yellow;font-size: 36px;');
  }
}

这次改完你之后你心中曾经满是 mmp 了,而且在不停的想:

“这样真的好吗?”

你无奈保障这次是最初的批改,而且也可能会不只一个产品来对你挤眉弄眼。你呆呆的看着显示器,直到电脑进入休眠模式,屏幕中映出你那张苦大仇深的脸,想着一直变得乌七八糟的 print 办法,不晓得该怎么去应酬那些永无休止的需要。。。

在下面的例子中,最开始的 Printer 依照需要写出它应该要有的逻辑,那就是在控制台中输入一些文本。换句话说,当写完“在 console 中输入一些文本”这段逻辑后,就能将 Printer 完结了,因为它就是 Printer 的全副了。那在这个状况下该如何扭转字体或是色彩的逻辑呢?

这时你该须要装璜器模式了。

Decorator Pattern(装璜器模式)

首先批改原来的 Printer,使它能够反对裁减款式:

class Printer {print(text = '', style ='') {console.log(`%c${text}`, style);
  }
}

之后别离创立扭转字体和色彩的装璜器:

const yellowStyle = (printer) => ({
  ...printer,
  print: (text = '', style ='') => {printer.print(text, `${style}color: yellow;`);
  }
});

const boldStyle = (printer) => ({
  ...printer,
  print: (text = '', style ='') => {printer.print(text, `${style}font-weight: bold;`);
  }
});

const bigSizeStyle = (printer) => ({
  ...printer,
  print: (text = '', style ='') => {printer.print(text, `${style}font-size: 36px;`);
  }
});

代码中的 yellowStyleboldStylebigSizeStyle 别离是给 print 办法的装璜器,它们都会接管 printer,并以 printer 为根底复制出一个一样的对象进去并返回,而返回的 printer 与原来的区别是,各自 Decorator 都会为 printerprint 办法加上各自装璜的逻辑(例如扭转字体、色彩或字号)后再调用 printerprint

应用形式如下:

只有把所有装璜的逻辑抽出来,就可能自在的搭配什么时候要输入什么款式,退出要再减少一个斜体款式,也只须要再新增一个装璜器就行了,不须要改变原来的 print 逻辑。

不过要留神的是下面的代码只是简略的把 Object 用解构复制,如果在 prototype 上存在办法就有可能会出错,所以要深拷贝一个新对象的话,还须要另外编写逻辑:

const copyObj = (originObj) => {const originPrototype = Object.getPrototypeOf(originObj);
  let newObj = Object.create(originPrototype);
   
  const originObjOwnProperties = Object.getOwnPropertyNames(originObj);
  originObjOwnProperties.forEach((property) => {const prototypeDesc = Object.getOwnPropertyDescriptor(originObj, property);
     Object.defineProperty(newObj, property, prototypeDesc);
  });
  
  return newObj;
}

而后装璜器内改使下面代码中的 copyObj,就能正确复制雷同的对象了:


const yellowStyle = (printer) => {const decorator = copyObj(printer);

  decorator.print = (text = '', style ='') => {printer.print(text, `${style}color: yellow;`);
  };

  return decorator;
};

其余案例

因为咱们用的语言是 JavaScript,所以没有用到类,只是简略的装璜某个办法,比方上面这个用来公布文章的 publishArticle

const publishArticle = () => {console.log('公布文章');
};

如果你想要再公布文章之后在 微博或 QQ 空间之类的平台上发个动静,那又该怎么解决呢?是像上面的代码这样吗?

const publishArticle = () => {console.log('公布文章');

  console.log('发 微博 动静');
  console.log('发 QQ 空间 动静');
};

这样显然不好!publishArticle 应该只须要公布文章的逻辑就够了!而且如果之后第三方服务平台越来越多,那 publishArticle 就会陷入始终加逻辑一爽快的状况,在明确了装璜器模式后就不能再这样做了!

所以把这个需要套上装璜器:

const publishArticle = () => {console.log('公布文章');
};

const publishWeibo = (publish) => (...args) => {publish(args);
  console.log('发 微博 动静');
};

const publishQzone = (publish) => (...args) => {publish(args);
  console.log('发 QQ 空间 动静');
};


const publishArticleAndWeiboAndQzone = publishWeibo(publishQzone(publishArticle));

后面 Printer 的例子是复制一个对象并返回,但如果是办法就不必复制了,只有确保每个装璜器都会返回一个新办法,而后会去执行被装璜的办法就行了。

总结

装璜器模式是一种十分有用的设计模式,在我的项目中也会常常用到,当需要变动时,感觉某个逻辑很多余,那么间接不装璜它就行了,也不须要去批改实现逻辑的代码。每一个装璜器都做他本人的事件,与其余装璜器互不影响。


本文首发微信公众号:前端先锋

欢送扫描二维码关注公众号,每天都给你推送陈腐的前端技术文章


欢送持续浏览本专栏其它高赞文章:

  • 深刻了解 Shadow DOM v1
  • 一步步教你用 WebVR 实现虚拟现实游戏
  • 13 个帮你进步开发效率的古代 CSS 框架
  • 疾速上手 BootstrapVue
  • JavaScript 引擎是如何工作的?从调用栈到 Promise 你须要晓得的所有
  • WebSocket 实战:在 Node 和 React 之间进行实时通信
  • 对于 Git 的 20 个面试题
  • 深刻解析 Node.js 的 console.log
  • Node.js 到底是什么?
  • 30 分钟用 Node.js 构建一个 API 服务器
  • Javascript 的对象拷贝
  • 程序员 30 岁前月薪达不到 30K,该何去何从
  • 14 个最好的 JavaScript 数据可视化库
  • 8 个给前端的顶级 VS Code 扩大插件
  • Node.js 多线程齐全指南
  • 把 HTML 转成 PDF 的 4 个计划及实现

  • 更多文章 …
正文完
 0