资讯专栏INFORMATION COLUMN

前端也要学系列:设计模式之装饰者模式

高胜山 / 2006人阅读

摘要:什么是装饰者模式今天我们来讲另外一个非常实用的设计模式装饰者模式。就增加功能来说,装饰者模式相比生成子类更为灵活。下面,装饰者模式就要正式登场了。下一步,我们可以愉快的去使用装饰者模式啦

什么是装饰者模式

今天我们来讲另外一个非常实用的设计模式:装饰者模式。这个名字听上去有些莫名其妙,不着急,我们先来记住它的一个别名:包装器模式

我们记着这两个名字来开始今天的文章。

首先还是上《设计模式》一书中的经典定义:

</>复制代码

  1. 动态地给一个对象添加一些额外的职责。

  2. 就增加功能来说,装饰者模式相比生成子类更为灵活。

我们来分析一下这个定义。

给对象添加一些新的职责,我们很容易想到创建子类来继承父类,然后在子类上增加额外的职责。

那什么是动态地呢?应该就是说这些新添加的职责在类一开始创建的时候我们并不知道,而是在使用过程根据需要而添加的。

相比生成子类更为灵活,这句话让装饰者模式和子类继承赤裸裸的刀兵相见了。没有对比就没有伤害,那我们就用例子来验证这句话。

传统面向对象的实现

</>复制代码

  1. 我们假设你是以为已经走上人生巅峰的汽车生产商,你的公司生产各种用途的汽车,某一天一个客户下单了四种汽车,分别是家用轿车、SUV、旅行车和跑车。我们很轻松地像下面这样进行交付了。

</>复制代码

  1. var Car = function(){}
  2. Car.prototype.start = function(){
  3. console.log("轰轰轰,启动正常!")
  4. }
  5. var Sedan = new car();// 小轿车
  6. var Suv = new Car();// SUV
  7. var Wagon=new Car();// 旅行车
  8. var Roadster=new Car();// 跑车
  9. //是不是又学会了几个英文单词?

过了几天客户找来了,说最近人们爱上了西藏自驾游,人们都希望能够选装一些方便越野和载物的功能,比如加装雪地胎、行李箱,升高底盘。

有经验的你满口答应下来,这个简单,于是你交付了下面的代码:

</>复制代码

  1. //SUV
  2. Suv.prototype.changeTire = function(){
  3. console.log("我换了雪地胎");
  4. }
  5. Suv.prototype.addHeight = function(){
  6. console.log("我升高了底盘");
  7. }
  8. Suv.prototype.addBox = function(){
  9. console.log("我安装了行李箱");
  10. }
  11. //Wagon
  12. Wagon.prototype.changeTire = function(){
  13. console.log("我换了雪地胎");
  14. }
  15. Wagon.prototype.addHeight = function(){
  16. console.log("我升高了底盘");
  17. }
  18. Wagon.prototype.addBox = function(){
  19. console.log("我安装了行李箱");
  20. }
  21. //Sedan
  22. Sedan.prototype.changeTire = function(){
  23. console.log("我换了雪地胎");
  24. }
  25. Sedan.prototype.addHeight = function(){
  26. console.log("我升高了底盘");
  27. }
  28. Sedan.prototype.addBox = function(){
  29. console.log("我安装了行李箱");
  30. }
  31. // 使用
  32. var suv = new Suv();
  33. suv.changeTire();
  34. suv.addHeight();
  35. suv.addBox();
  36. suv.start();
  37. ...

你增加了多少种特性?3x3=9种。

你又问,我直接把这三个特性加在Car上不行吗?就不用这么麻烦了。

当然不行,因为我们还有一种车:Roadster跑车。

你能想象法拉利换了雪地胎背上行李箱升高底盘是个什么死样子吗?这么干的人肯定疯了。

如果我们把特性一股脑加在Car上,就避免不了这种情况的发生。

这个时候,就体现出子类继承的不灵活之处。

下面,装饰者模式就要正式登场了。

</>复制代码

  1. var Car=function (){}
  2. Car.prototype.start=function(){
  3. console.log("轰轰轰,启动正常!")
  4. }
  5. // 创建装饰类(包装类)
  6. var ChangeTireDec=function(car){
  7. this.car=car;
  8. }
  9. var AddHeightDec=function(car){
  10. this.car=car;
  11. }
  12. var AddBoxDec=function(car){
  13. this.car=car;
  14. }
  15. // 装饰类具有和Car同样的特性,只不过额外执行了一些其他的操作
  16. ChangeTireDec.prototype.start=function(){
  17. console.log("我换了雪地胎");
  18. this.car.start();
  19. }
  20. AddHeightDec.prototype.start=function(car){
  21. console.log("我升高了底盘");
  22. this.car.start();
  23. }
  24. AddBoxDec.prototype.start=function(car){
  25. console.log("我安装了行李箱");
  26. this.car.start();
  27. }
  28. // 使用
  29. var suv=new Suv();
  30. suv=new ChangeTireDec(suv);
  31. suv=new AddHeightDec(suv);
  32. suv=new AddBoxDec(suv);
  33. suv.start();

上面的代码你增加了几种特性?只有三种!而且不管你是给SUV还是Wagon还是Sedan加装,都不需要再增加特性的代码。

这,就是装饰者模式的优势所在。

现在我们再回过头来看看GoF的定义:

</>复制代码

  1. 动态地给一个对象添加一些额外的职责。

  2. 就增加功能来说,装饰者模式相比生成子类更为灵活。

怎么样,是不是如同1+1=2一样简单了?现在你应该也明白了为什么装饰者模式又叫座包装器模式了。因为它将类的原有特性包装起来,添加其他的特性,就像一个箱子一样。而且实现过程中,还满足了封闭-开放原则。

JavaScript的实现

上面的例子中,我们是模拟了传统的面向对象语言来解释什么是装饰者模式。我们都知道,要动态改变JavaScript对象非常容易,可以向操作变量一个操作对象,我们再来改写下上面的例子,让它更javasripty

</>复制代码

  1. var car = {
  2. start: function(){
  3. console.log("轰轰轰,正常启动!");
  4. }
  5. }
  6. var ChangeTireDec = function(){
  7. console.log("我换了雪地胎");
  8. }
  9. var AddHeightDec = function(){
  10. console.log("我升高了底盘");
  11. }
  12. var AddBoxDec = function(){
  13. console.log("我安装了行李箱");
  14. }
  15. var start1 = car.start;
  16. car.start=function(){
  17. ChangeTireDec();
  18. start1();
  19. }
  20. var start2=car.start;
  21. car.start = function(){
  22. AddHeightDec();
  23. start2();
  24. }
  25. var start3=car.start();
  26. car.start = function(){
  27. AddBoxDec();
  28. start3();
  29. }
  30. // 执行
  31. car.start();
实际中的应用

从上面的例子我们可以看出来,我们不断的将car.start的引用赋值给临时变量,然后将原来的car.start指向新的对象--包含了原来对象的引用和新的特性的对象。这很好的保证了代码的开放-封闭原则,这是今天第二次提到这个原则了,就是对修改封闭,对新增开放。

特别当你要重构一个非常复杂的多人项目时,如果你不想因为修改了同事的一行代码而引起“蝴蝶效应”,那么将他的方法整个打包赋值然后用装饰者模式增加新的功能,是一种非常安全而且高效的做法。

下一步,我们可以愉快的去使用装饰者模式啦!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107792.html

相关文章

  • 前端要学系列设计模式策略模式

    摘要:做前端开发已经好几年了,对设计模式一直没有深入学习总结过。今天第一天,首先来讲策略模式。什么是策略模式四兄弟的经典设计模式中,对策略模式的定义如下定义一系列的算法,把它们一个个封装起来,并且使它们可互相替换。 做前端开发已经好几年了,对设计模式一直没有深入学习总结过。随着架构相关的工作越来越多,越来越能感觉到设计模式成为了我前进道路上的一个阻碍。所以从今天开始深入学习和总结经典的设计模...

    Anchorer 评论0 收藏0
  • JS 适配器模式

    摘要:另外,适配器模式和其它几个模式可能容易让人迷惑,这里说一下大概的区别适配器和桥接模式虽然类似,但桥接的出发点不同,桥接的目的是将接口部分和实现部分分离,从而对他们可以更为容易也相对独立的加以改变。 1. 简介 适配器模式(Adapter)是将一个类(对象)的接口(方法或属性)转化成客户希望的另外一个接口(方法或属性),适配器模式使得原本由于接口不兼容而不能一起工作的那些类(对象)可以一...

    Jeffrrey 评论0 收藏0
  • 设计模式系列装饰模式

    摘要:测试类设计模式装饰者模式工厂模式只能读啦会报错只读异常可以正确运行第二部分定义抽象组件是具体组件和抽象装饰类的共同父类,声明了在具体组件中实现的方法。 前言 本篇文章分为四个部分:第一部分会举一个例子引出装饰者模式,让读者对装饰者模式有个感官上的认识;第二部分会给出装饰者模式的定义(当然我们主要不是来背定义,就当做积累专业名词来记吧,我个人是很不喜欢下定义的);第三部分,我会拿jdk中...

    ytwman 评论0 收藏0
  • 每天一个设计模式装饰模式

    摘要:作者按每天一个设计模式旨在初步领会设计模式的精髓,目前采用和两种语言实现。诚然,每种设计模式都有多种实现方式,但此小册只记录最直截了当的实现方式原文地址是每天一个设计模式之装饰者模式欢迎关注个人技术博客。 作者按:《每天一个设计模式》旨在初步领会设计模式的精髓,目前采用javascript和python两种语言实现。诚然,每种设计模式都有多种实现方式,但此小册只记录最直截了当的实现方式...

    brianway 评论0 收藏0
  • 每天一个设计模式装饰模式

    摘要:作者按每天一个设计模式旨在初步领会设计模式的精髓,目前采用和两种语言实现。诚然,每种设计模式都有多种实现方式,但此小册只记录最直截了当的实现方式原文地址是每天一个设计模式之装饰者模式欢迎关注个人技术博客。 作者按:《每天一个设计模式》旨在初步领会设计模式的精髓,目前采用javascript和python两种语言实现。诚然,每种设计模式都有多种实现方式,但此小册只记录最直截了当的实现方式...

    shleyZ 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<