news 2025/12/25 17:34:45

设计模式之-装饰器模式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
设计模式之-装饰器模式

装饰器模式
1.核心:

动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式比生成子类更加灵活

2.装饰器模式定义:

这是一种结构型设计模式,允许你将对象(A)放入包含行为的特殊对象里面,从而为原来的对象(A)绑定新的行为

3.本质

因此装饰器的本质,其实就是组合,而非继承。

4.简单的一个表单装饰器demo,个人感觉更像是套娃哈哈

// 表单类classForm{submit(){console.log('表单提交啦');}}// 装饰器基类classFormDecorator{constructor(form){this.form=form;}submit(){this.form.submit();}}// 校验装饰器classValidatorDecoratorextendsFormDecorator{submit(){this.validate();super.submit();}validate(){console.log('表单校验开始了');}}// 日志装饰器classLogDecoratorextendsFormDecorator{submit(){super.submit();this.log();}log(){console.log('表单日志记录打印了');}}// 使用letform=newForm();form=newValidatorDecorator(form);form=newLogDecorator(form);form.submit();

5.场景
假设你现在需要一个有通知功能的库,其他程序可以用你这个库来发送一些重要的通知。
一开始的设计就是一个 Notifier 类,里面有几个成员变量:
构造函数
一个 send 方法:通过发送邮件的方式来发送通知

interfaceINotifier{send(message:string):void;}// 基础的通知器:只能发送邮件通知classNotifierimplementsINotifier{privateemailAddresses:string[]=[];constructor(emails:string[]){this.emailAddresses=emails;}send(message:string):void{// 模拟发送邮件console.log(`Email sent to${this.emailAddresses.join(", ")}:${message}`)}}// 使用constbaseNotifier=newNotifier(["123@qq.com","456@gmail.com"]);baseNotifier.send("hello,world!");

目前库的功能是 OK 的,但是需求变化了!

许多客户端希望通过不同的方式来收到通知,例如通过发送短信、微信、QQ,那么此时我们就会去扩展 Notifier 这个类,创建一些子类

好像上面的需求也能够完美的解决。

但是,需求又变化了!

有些客户期望多个渠道收到通知,比如有客户希望微信和 QQ 同时收到通知,有客户端希望短信和微信收到通知…

这个时候你就会发现,如果还是按照之前的设计,那么子类的数量会成倍的增加:
像这种场景下,最佳的解决方案是使用组合而非继承,这也是前面在介绍设计原则的时候,其中就提到了组合优于继承。

此时我们就可以使用装饰器模式,新的结构如下:

// 定义整个装饰器的框架,具体的装饰器需要继承这个类interfaceINotifier{send(message:string):void;}// 基础的通知器:只能发送邮件通知classNotifierimplementsINotifier{privateemailAddresses:string[]=[];constructor(emails:string[]){this.emailAddresses=emails;}send(message:string):void{// 模拟发送邮件console.log(`Email sent to${this.emailAddresses.join(", ")}:${message}`)}}// 基础装饰器classBaseDecoratorimplementsINotifier{// 这个基础的装饰器类,是不能够单独使用的privatewrappee:INotifier;// 回头初始化的时候,需要传入一个 INotifier 类型的实例// 就是接收具体的渠道(QQ、微信、SMS)constructor(notifier:INotifier){this.wrappee=notifier;}send(message:string):void{this.wrappee.send(message);}}// QQ装饰器classQQDecoratorextendsBaseDecorator{privateQQID:string;constructor(notifier:INotifier,QQID:string){super(notifier);this.QQID=QQID;}// 部分重写父类send方法send(message:string):void{// 调用父类的send方法super.send(message);// 在发送完消息之后,在发送一条QQ消息console.log(`QQ message sent to${this.QQID}:${message}`);}}// SMS装饰器classSMSDecoratorextendsBaseDecorator{privatephoneNumber:string;constructor(notifier:INotifier,phoneNumber:string){super(notifier);this.phoneNumber=phoneNumber;}// 部分重写父类send方法send(message:string):void{// 调用父类的send方法super.send(message);// 再调用完父类的 send 方法之后,再完成子类扩展的逻辑// 在发送完消息之后,在发送一条SMS消息console.log(`SMS message sent to${this.phoneNumber}:${message}`);}}// 微信装饰器classWeChatDecoratorextendsBaseDecorator{privateweChatId:string;constructor(notifier:INotifier,weChatId:string){super(notifier);this.weChatId=weChatId;}// 部分重写父类的 send 方法send(message:string):void{// 调用父类的 send 方法super.send(message);// 在发送完消息之后,再发送一条微信消息console.log(`WeChat message sent to${this.weChatId}:${message}`);}}// 使用constbaseNotifier=newNotifier(["123@qq.com","456@gmail.com"]);baseNotifier.send("hello,world!");console.log("==================================");// 接下来需求有变:客户希望发送短信以及微信通知constsmsNotifier=newSMSDecorator(baseNotifier,'1243443553');constsmsAndWechatNotifier=newWeChatDecorator(smsNotifier,"mingtianguohou");smsAndWechatNotifier.send('这是一条非常重要的信息');console.log("==================================");// 回头需求又有变换,客户希望在增加QQ通知constallTypeNotifier=newQQDecorator(smsAndWechatNotifier,"1051705661");allTypeNotifier.send("这是另外一条非常重要的消息,所有渠道都会收到")
  1. AOP的应用实例,体验装饰函数的威力
<button tag="login"id="button">点击打开登陆浮层</button><script>varshowLogin=function(){console.log('打开登陆浮层')log(this.getAttribute('tag'));}varlog=function(tag){console.log('上报标签为:'+tag);// 真正上报代码略。。}document.getElementById('button').onclick=showLogin</script>

现在我们看到在showLogin函数里,即要负责打开登陆浮层,又要负责数据上报,这是两个层面的功能,在此处却被耦合在一个函数,使用AOP分离之后代码如下

<button tag="login"id="button">点击打开登陆浮层</button><script>Function.prototype.after=function(afterfn){var_self=this;returnfunction(){varret=_self.apply(this,arguments);afterfn.apply(this,arguments);returnret;}}varshowLogin=function(){console.log('打开登陆浮层');}varlog=function(){console.log('上报标签为:'+this.getAttribute('tag'));// 真正上报代码略。。}showLogin=showLogin.after(log)document.getElementById('button').onclick=showLogin</script>

7.再看一个AOP的例子

//现在有一个用于发起ajax请求的函数,这个函数负责项目中所有的ajax异步请求varajax=function(type,url,param){console.log(param);// 发送ajax请求代码略}ajax('get','http://xxx.com/userinfo',{name:'syt'});// 上面的伪代码表示向后台cgi和合作也很愉快。直到有一天,// 我们的网站遭受了CSRF攻击,解决这个最简单的办法是在HTTP请求中带上一个Token参数vargetToken=function(){return'token';}// 现在的任务是给每个ajax请求都加上Token参数varajax=function(type,url,param){param=param||{};param.Token=getToken();// 发送ajax请求代码略...}//虽然已经解决了问题,但是我门的ajax函数变得相对僵硬了,// 每个从ajax函数里发出的请求都自动带上了Token参数,虽然现在没有什么问题,// 但如果将来把这个函数移植到其他项目,或者把他放到一个开源库中供其他人使用,Token参数都将是多余的。// 为了解决这个问题,先把ajax函数还原成一个干净的函数Function.prototype.before=function(beforefn){var_self=this;returnfunction(){beforefn.apply(this,arguments);return_self.apply(this,arguments);}}varajax=function(type,url,param){console.log(param);// 发送ajax请求代码略}vargetToken=function(){return'token';}ajax=ajax.before(function(type,url,param){param.Token=getToken();});ajax('get','http://xxx.com/userinfo',{name:'syt'});// 明显可以看到,用AOP的方式给ajax函数动态装饰上了Token函数,// 保证了ajax函数是个相对纯净的函数,提高了ajax函数的可服用行,他在被迁往其他项目时,不需要做任何修改

非原创,来源渡一谢杰老师和javascript设计模式与开发实践 -曾探

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2025/12/24 6:57:41

【DC-AC】使用了H桥MOSFET进行开关,电感器作为滤波器,R和C作为负载目标是产生150V的双极输出和4安培(双极)的电流simulink实现

✅作者简介&#xff1a;热爱科研的Matlab仿真开发者&#xff0c;擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。&#x1f34e; 往期回顾关注个人主页&#xff1a;Matlab科研工作室&#x1f34a;个人信条&#xff1a;格物致知,完整Matlab代码及仿真咨询…

作者头像 李华
网站建设 2025/12/20 0:11:15

智慧校园招投标时间管理的核心节点把控

✅作者简介&#xff1a;合肥自友科技 &#x1f4cc;核心产品&#xff1a;智慧校园平台(包括教工管理、学工管理、教务管理、考务管理、后勤管理、德育管理、资产管理、公寓管理、实习管理、就业管理、离校管理、科研平台、档案管理、学生平台等26个子平台) 。公司所有人员均有多…

作者头像 李华
网站建设 2025/12/20 0:10:22

FaceFusion开源社区活跃度报告:开发者生态正在崛起

FaceFusion开源社区活跃度报告&#xff1a;开发者生态正在崛起在AIGC浪潮席卷全球的今天&#xff0c;图像生成与视觉编辑技术正以前所未有的速度渗透进创作、娱乐乃至工业领域。其中&#xff0c;人脸替换&#xff08;Face Swapping&#xff09;作为最具争议也最引人注目的方向之…

作者头像 李华
网站建设 2025/12/20 0:07:30

为啥要有枚举这个类型,定义一个类,其中定义常量不就行了

枚举类型 vs 常量类 1. 类型安全性 // 使用枚举 - 编译时类型检查 public enum Status {ACTIVE, INACTIVE } void processStatus(Status status) { }// 调用时只能传入定义的枚举值 processStatus(Status.ACTIVE); // ✓ 正确 processStatus("ACTIVE"); // ✗ 编译错…

作者头像 李华
网站建设 2025/12/20 0:03:01

Langchain-Chatchat如何集成快捷键操作?效率提升技巧

Langchain-Chatchat如何集成快捷键操作&#xff1f;效率提升技巧 在企业级知识管理系统中&#xff0c;一个看似微不足道的交互细节——比如是否支持“Ctrl Enter 发送消息”——往往决定了用户是愿意每天使用它&#xff0c;还是用一次就弃之不用。随着本地大模型部署方案逐渐…

作者头像 李华
网站建设 2025/12/20 0:02:56

利用Langchain-Chatchat降低企业AI应用的数据泄露风险

利用Langchain-Chatchat降低企业AI应用的数据泄露风险 在金融、医疗和法律等行业&#xff0c;一份合同、一条病历或一纸合规文件的外泄&#xff0c;都可能引发连锁反应——监管处罚、客户流失、品牌声誉受损。而当这些敏感信息需要接入AI系统以实现智能问答时&#xff0c;传统基…

作者头像 李华