diff --git "a/175.\347\262\276\350\257\273\343\200\212\350\256\276\350\256\241\346\250\241\345\274\217 - Decorator \350\243\205\351\245\260\345\231\250\346\250\241\345\274\217\343\200\213.md" "b/175.\347\262\276\350\257\273\343\200\212\350\256\276\350\256\241\346\250\241\345\274\217 - Decorator \350\243\205\351\245\260\345\231\250\346\250\241\345\274\217\343\200\213.md" new file mode 100644 index 00000000..3dfa97fa --- /dev/null +++ "b/175.\347\262\276\350\257\273\343\200\212\350\256\276\350\256\241\346\250\241\345\274\217 - Decorator \350\243\205\351\245\260\345\231\250\346\250\241\345\274\217\343\200\213.md" @@ -0,0 +1,104 @@ +# Decorator(装饰器模式) + +Decorator(装饰器模式)属于结构型模式,是一种拓展对象额外功能的设计模式,别名 `wrapper`。 + +**意图:动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator 模式相比生成子类更为灵活。** + +## 举例子 + +如果看不懂上面的意图介绍,没有关系,设计模式需要在日常工作里用起来,结合例子可以加深你的理解,下面我准备了三个例子,让你体会什么场景下会用到这种设计模式。 + +### 相框 + +照片 + 相框 = 带相框的照片,这背后就是一种装饰器模式:照片具有看的功能,相框具有装饰功能,在你看照片的基础上,还能看到精心设计的相框,增加了美感,同时相框还可以增加照片的保存时间与安全性。 + +相框与照片是一种组合关系,任何照片都可以放到相框中,而不是每个照片生成一个特定的相框,显然,组合的方式更加灵活。 + +### 带有缓存的文件读写 + +假设我们有一个类 `FileIO` 用来读写文件,但是没有缓存能力,此时是新建一个 `CachedFileIO` 子类好,还是创建一个 `CachedIO`? + +一眼看上去好像 `CachedFileIO` 用起来更方便,而 `CachedIO` 的用法是 `new CachedIO(new FileIO())` 稍微麻烦一些,但如果我们增加一个网络读写类 `NetworkIO`,一个数据库读写类 `DBIO` 呢? + +显然,继承的方式会使子类数量极速膨胀,而组合的方式则非常灵活,生成一个支持缓存的网络读写器,只需要 `new CachedIO(new NetworkIO())` 即可,这就是组合灵活的地方。 + +当然,为了实现这个能力,`CachedIO` 需要与 `FileIO`、`CachedFileIO`、`CachedIO` 继承自同一个类,具备相同的接口。 + +### 搭建平台的组件 wrapper + +装饰器模式别名也叫 `wrapper`,`wrapper` 也经常在前端搭建场景中遇到,当搭建平台加载一个组件时,希望拓展其基础能力,一般会使用 `wrapper` 层对组件进行嵌套,`wrapper` 层就是在不改变 API 的基础上,对第三方组件进行增强。 + +## 意图解释 + +**意图:动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator 模式相比生成子类更为灵活。** + +不同于继承,组合可以在运行时进行,所以称之为 “动态添加”,这里的 “额外职责” 泛指一切功能,比如在按钮点击时进行一些 log 日志的打印,在绘制 text 文本框时,额外绘制一个滚动条和边框等等。 + +“就增加功能来说,Decorator 模式相比生成子类更为灵活” 这句话的含义是,组合比继承更灵活,当可拓展的功能很多时,继承方案会产生大量的子类,而组合可以提前写好处理函数,在需要时动态构造,显然是更灵活的。 + +## 结构图 + + + +`ConcreteComponent` 指的是需要被装饰的组件,可以看到,装饰器 `Decorator` 与他都继承同一个类,这样能保证 API 的一致,才保证无论装饰多少层,始终符合 `Component` 类型。 + +装饰器如果有多种,就要将 `Decorator` 申明为抽象类,`ConcreteDecoratorA`、`ConcreteDecoratorB` 分别实现它们,如果只有一种装饰器,可以退化到 `Decorator` 自身就是一种实现。 + +## 代码例子 + +下面例子使用 typescript 编写。 + +```typescript +class Component { + // 具有点击事件 + public onClick = () => {} +} + +class Decorator extends Component { + private _component + + constructor(component) { + this._component = component + } + + public onClick = () => { + log('打点') + this._component.onClick() + } +} + +const component = new Component() +// 一个普通的点击 +component.onClick() + +const wrapperComponent = new Decorator(component) +// 一个具有打点功能的点击 +wrapperComponent.onClick() +``` + +其实方法很简单,通过组合,我们得到了一个能力更强的组件,而实现的方式就是利用构造函数保存组件实例,并在复写函数时,增加一些增强实现。 + +## 弊端 + +装饰器的问题也是组合的问题,过多的组合会导致: + +- 组合过程的复杂,要生成过多的对象。 +- 包装器层次增多,会增加调试成本,我们比较难追溯到一个 bug 是在哪一层包装导致的。 + +## 总结 + +装饰器模式是非常常用的模式,Decorator 是一个透明的包装,只要保证包装的透明性,就可以最大限度发挥装饰器模式的优势。 + +最后总结一个装饰器应用图: + + + +> 讨论地址是:[精读《设计模式 - Decorator 装饰器模式》· Issue #286 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/286) + +**如果你想参与讨论,请 [点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。** + +> 关注 **前端精读微信公众号** + + + +> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh))