技术头条 - 一个快速在微博传播文章的方式     搜索本站
您现在的位置首页 --> 编程语言 --> 聊聊设计模式(4):装饰模式

聊聊设计模式(4):装饰模式

浏览:1492次  出处信息

Decorator Pattern,中文名为装饰者模式,这个模式思想很简单,但是特别容易把代码搞复杂,它包含四个重要角色:

  • Component, 抽象构件

  • ConcreteComponent, 具体构件

  • Decorator, 抽象装饰类

  • ConcreteDecorator, 具体装饰类


这几个角色很容易把人搞懵,所以在使用这个模式之前要彻底理解它。ECMAScript 2017 中增加了修饰器,它从语法层面帮掩盖了 Decorator 装饰类和 Component 构件,极大程度方便了我们理解装饰器:

functionactivate(target) {
   target.barkable = true;
   target.runnable = true;
}
@activate
classEDog{}
console.log(EDog.barkable, EDog.runnable); // true true

上面的代码很简单,activate 是一个开关装饰类,通过它激活了 EDog —— 不会跑、不会叫的电子狗,让它具备了 barkrun 能力。

过年回来后业务压力不大,小苏让小喜抽空做一个代码发布平台,帮助团队迁移代码发布平台,之前的前端静态代码是通过 SVN 管理和发布的,现在需要迁移到 Git 上去,小苏说这是个重任。小喜暗自欣喜,这 TM 也太简单了。

使用开源的 gitlab 搭建了一个代码管理平台,然后在 gitlab 上添加了钩子,每次有代码推送时,向小喜的代码平台发送一个请求,小喜便直接执行 publish 操作:

classCodePlatform{
   publish(gitInfo) {
this.scpCodeFromGitlabToOnline();
   }
// 直接将 gitlab 代码直接复制到线上机器
   scpCodeFromGitlabToOnline() {
// ...
   }
}

不久之后代码的构建从线下迁移到了线上,要求代码发布时先使用构建服务器处理,于是小喜给 CodePlatform 增加了一项功能:

classCodePlatform{
   publish() {
this.transferCodeToBuilderServer(() => {
this.scpCodeFromGitlabToOnline();
       });
   }
   scpCodeFromGitlabToOnline() {}
// 将代码扔到构建服务器
   transferCodeToBuilderServer() {}
}

随着团队工程化程度越来越高,很多线下操作流程渐渐搬移到了线上进行云处理,后续又增加了测试服务器、日志服务器、安全监测服务器等,结果小喜的代码变成了这样:

classCodePlatform{
   publish() {
// publish 的逻辑越来越重
   }
   scpCodeFromGitlabToOnline() {}
   transferCodeToBuilderServer() {}
   transferCodeToLogServer() {}
   transferCodeToSecureServer() {}
   transferCodeToTestServer() {}
}

小喜怎么也没想到,发布一个仓库在 CodePlatform 需要走这么多流程,这个类的职责越来越多,代码体积也越来越庞大,此时的小喜有点苦恼。

有一次,小喜学习 ES7 相关知识,突然看到一个叫做 Decorator 的东西,想了想似乎可以在项目中用一下。这不用不知道,一用吓一跳,原来之前的代码还可以这样架构:

// 为所有的服务添加日志服务
classLogService{}
const logger = new LogService();
@logger('build')
classBuildService{}
@logger('secure')
classSecureService{}
@logger('test')
classTestService{}

首先将服务抽离出来,然后:

const builder = new BuildService();
const securer = new SecureSevice();
const tester = new TestService();
@securer
@logger('CodePlatform')
classCodePlatform{
   @builder
   @tester
  publish() {}
}

让 CodePlatform 的代码先走一遍 securer,在发布的时候走一遍 testerbuilder,代码结构瞬间变得清晰了很多。

我们经常在代码中干这件事情,尤其是业务代码。一些临时需求,为了图方便、图快,直接在原有的基础上增加几个方法,然后在执行入口位置添加补丁,时间长了之后,维护起来十分吃力,这也是为什么在业务交接时大家相互吐槽代码的主要原因之一。很多人拿到代码的第一感觉就是,写这代码的人是傻x,这代码得重构。

上面的问题在于 CodePlatform 承担了太多的职责,最后这个类变得特别繁忙,加上代码量多了之后,阅读难度提升了。事实上,测试、监控、日志、安全等都是发布之前对代码的流程化操作,CodePlatform 的核心功能是 发布代码,其他的操作都可以看做对待发布代码的装饰。

JS 中 Decorator 语法糖十分容易理解,它既可以装饰类也可以装饰类的成员方法。所以我就没把这个模式的具体实现写出来,感兴趣的可以到网上搜索下。

对类做职能补充有几种方式:

  • 继承,让继承类拥有父类的所有能力,并自我扩展

  • 关联,将两个功能集进行合并

  • 修改父类(基类),这是当然不推荐的方案

装饰模式,动态地给一个对象增加一些额外的职责 ( Responsibility ),就增加对象功能来说,装饰模式比生成子类实现更为灵活。

装饰者模式可以在运行时给对象动态地增加更多的职责,它属于通过建立关联方式来扩充对象的能力。从上面的例子中看得出来,CodePlatform 在装饰前后基本看不出差异,这就是这个模式的特点。


建议继续学习:

  1. 软件架构模式的种类    (阅读:2855)
  2. 让生活变简单的简单网站    (阅读:2242)
  3. 状态模式和策略模式的比较    (阅读:1812)
  4. JavaScript Dynamic Prototype Pattern    (阅读:1764)
  5. 关于经营模式    (阅读:1634)
  6. JavaScript Creating Objects Other Pattern    (阅读:1607)
  7. Python创建单例模式的三种方式    (阅读:1465)
  8. [译]Go开发中一些有用的模式    (阅读:1063)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
© 2009 - 2024 by blogread.cn 微博:@IT技术博客大学习

京ICP备15002552号-1