一、引言

  在软件开发中,我们经常想要对一类对象添加不同的功能,例如要给手机添加贴膜,手机挂件,手机外壳等,如果此时利用继承来实现的话,就需要定义无数的类,如StickerPhone(贴膜是手机类)、AccessoriesPhone(挂件手机类)等,这样就会导致 ”子类爆炸“问题,为了解决这个问题,我们可以使用装饰者模式来动态地给一个对象添加额外的职责。下面让我们看看装饰者模式。

二、装饰者模式的详细介绍

2.1 定义

  装饰者模式以对客户透明的方式动态地给一个对象附加上更多的责任,装饰者模式相比生成子类可以更灵活地增加功能。

2.2 装饰者模式实现

这里以手机和手机配件的例子来演示装饰者模式的实现,具体代码如下:

  1. /// <summary>
  2. /// 手机抽象类,即装饰者模式中的抽象组件类
  3. /// </summary>
  4. public abstract class Phone
  5. {
  6. public abstract void Print();
  7. }
  8. /// <summary>
  9. /// 苹果手机,即装饰着模式中的具体组件类
  10. /// </summary>
  11. public class ApplePhone:Phone
  12. {
  13. /// <summary>
  14. /// 重写基类方法
  15. /// </summary>
  16. public override void Print()
  17. {
  18. Console.WriteLine("开始执行具体的对象——苹果手机");
  19. }
  20. }
  21. /// <summary>
  22. /// 装饰抽象类,要让装饰完全取代抽象组件,所以必须继承自Photo
  23. /// </summary>
  24. public abstract class Decorator:Phone
  25. {
  26. private Phone phone;
  27. public Decorator(Phone p)
  28. {
  29. this.phone = p;
  30. }
  31. public override void Print()
  32. {
  33. if (phone != null)
  34. {
  35. phone.Print();
  36. }
  37. }
  38. }
  39. /// <summary>
  40. /// 贴膜,即具体装饰者
  41. /// </summary>
  42. public class Sticker : Decorator
  43. {
  44. public Sticker(Phone p)
  45. : base(p)
  46. {
  47. }
  48. public override void Print()
  49. {
  50. base.Print();
  51. // 添加新的行为
  52. AddSticker();
  53. }
  54. /// <summary>
  55. /// 新的行为方法
  56. /// </summary>
  57. public void AddSticker()
  58. {
  59. Console.WriteLine("现在苹果手机有贴膜了");
  60. }
  61. }
  62. /// <summary>
  63. /// 手机挂件
  64. /// </summary>
  65. public class Accessories : Decorator
  66. {
  67. public Accessories(Phone p)
  68. : base(p)
  69. {
  70. }
  71. public override void Print()
  72. {
  73. base.Print();
  74. // 添加新的行为
  75. AddAccessories();
  76. }
  77. /// <summary>
  78. /// 新的行为方法
  79. /// </summary>
  80. public void AddAccessories()
  81. {
  82. Console.WriteLine("现在苹果手机有漂亮的挂件了");
  83. }
  84. }

此时客户端调用代码如下:

  1. class Customer
  2. {
  3. static void Main(string[] args)
  4. {
  5. // 我买了个苹果手机
  6. Phone phone = new ApplePhone();
  7. // 现在想贴膜了
  8. Decorator applePhoneWithSticker = new Sticker(phone);
  9. // 扩展贴膜行为
  10. applePhoneWithSticker.Print();
  11. Console.WriteLine("----------------------\n");
  12. // 现在我想有挂件了
  13. Decorator applePhoneWithAccessories = new Accessories(phone);
  14. // 扩展手机挂件行为
  15. applePhoneWithAccessories.Print();
  16. Console.WriteLine("----------------------\n");
  17. // 现在我同时有贴膜和手机挂件了
  18. Sticker sticker = new Sticker(phone);
  19. Accessories applePhoneWithAccessoriesAndSticker = new Accessories(sticker);
  20. applePhoneWithAccessoriesAndSticker.Print();
  21. Console.ReadLine();
  22. }

从上面的客户端代码可以看出,客户端可以动态地将手机配件增加到手机上,如果需要添加手机外壳时,此时只需要添加一个继承Decorator的手机外壳类,从而,装饰者模式扩展性也非常好。

2.3 装饰者模式的类图

实现完了装饰者模式之后,让我们看看装饰者模式实现中类之间的关系,具体见下图:

在装饰者模式中各个角色有:

  抽象构件(Phone)角色:给出一个抽象接口,以规范准备接受附加责任的对象。
  具体构件(AppPhone)角色:定义一个将要接收附加责任的类。
  装饰(Dicorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
  具体装饰(Sticker和Accessories)角色:负责给构件对象 ”贴上“附加的责任。

三、装饰者模式的优缺点

看完装饰者模式的详细介绍之后,我们继续分析下它的优缺点。

优点:

  装饰这模式和继承的目的都是扩展对象的功能,但装饰者模式比继承更灵活
  通过使用不同的具体装饰类以及这些类的排列组合,设计师可以创造出很多不同行为的组合
  装饰者模式有很好地可扩展性

缺点:

  装饰者模式会导致设计中出现许多小对象,如果过度使用,会让程序变的更复杂。并且更多的对象会是的差错变得困难,特别是这些对象看上去都很像。

四、使用场景

下面让我们看看装饰者模式具体在哪些情况下使用,在以下情况下应当使用装饰者模式:

  1,需要扩展一个类的功能或给一个类增加附加责任。
  2,需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
  3,需要增加由一些基本功能的排列组合而产生的非常大量的功能。

五、.NET中装饰者模式的实现

在.NET 类库中也有装饰者模式的实现,该类就是System.IO.Stream,下面看看Stream类结构:

上图中,BufferedStream、CryptoStream和GZipStream其实就是两个具体装饰类,这里的装饰者模式省略了抽象装饰角色(Decorator)。下面演示下客户端如何动态地为MemoryStream动态增加功能的。

  1. MemoryStream memoryStream = new MemoryStream(new byte[] {,,,,});
  2. // 扩展缓冲的功能
  3. BufferedStream buffStream = new BufferedStream(memoryStream);
  4. // 添加加密的功能
  5. CryptoStream cryptoStream = new CryptoStream(memoryStream,new AesManaged().CreateEncryptor(),CryptoStreamMode.Write);
  6. // 添加压缩功能
  7. GZipStream gzipStream = new GZipStream(memoryStream, CompressionMode.Compress, true);

六、总结

  到这里,装饰者模式的介绍就结束了,装饰者模式采用对象组合而非继承的方式实现了再运行时动态地扩展对象功能的能力,而且可以根据需要扩展多个功能,避免了单独使用继承带来的 ”灵活性差“和”多子类衍生问题“。同时它很好地符合面向对象设计原则中 ”优先使用对象组合而非继承“和”开放-封闭“原则。

以上内容照抄自:http://learninghard.blog.51cto.com/6146675/1310760

设计模式(九)装饰者模式(Decorator Pattern)的更多相关文章

  1. 设计模式学习--装饰者模式(Decorator Pattern)

    概念: 装饰者模式(Decorator Pattern): 动态地将功能添加到对象,相比生成子类更灵活,更富有弹性. 解决方案: 装饰者模式的重点是对象的类型,装饰者对象必须有着相同的接口,也也就是有 ...

  2. python 设计模式之装饰器模式 Decorator Pattern

    #写在前面 已经有一个礼拜多没写博客了,因为沉醉在了<妙味>这部小说里,里面讲的是一个厨师苏秒的故事.现实中大部分人不会有她的天分.我喜欢她的性格:总是想着去解决问题,好像从来没有怨天尤人 ...

  3. 23种设计模式之装饰器模式(Decorator Pattern)

    装饰器模式(Decorator Pattern) 允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰类,用来包 ...

  4. C#设计模式之装饰者模式(Decorator Pattern)

    1.概述 装饰者模式,英文名叫做Decorator Pattern.装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能.它是通过创建一个包装对象,也就是装饰来包裹真实的对象. 2 ...

  5. c#设计模式之装饰器模式(Decorator Pattern)

    引子 在面向对象语言中,我们常常会听到这样一句话:组合优于继承.那么该如何去理解这句话呢? 下面我将以游戏装备为模型用简单的代码去展示它 先创建一个装备的抽象类,然后创建刀枪2个具体的业务子类 pub ...

  6. 【UE4 设计模式】装饰器模式 Decorator Pattern

    概述 描述 动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活.是一种对象结构型模式. 套路 抽象构件(Component) 具体构 ...

  7. 浅谈设计模式--装饰者模式(Decorator Pattern)

    挖了设计模式这个坑,得继续填上.继续设计模式之路.这次讨论的模式,是 装饰者模式(Decorator Pattern) 装饰者模式,有时也叫包装者(Wrapper),主要用于静态或动态地为一个特定的对 ...

  8. 设计模式 - 装饰者模式(Decorator Pattern) Java的IO类 用法

    装饰者模式(Decorator Pattern) Java的IO类 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26716 ...

  9. 设计模式 - 装饰者模式(Decorator Pattern) 具体解释

    装饰者模式(Decorator Pattern) 具体解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26707033 装饰者 ...

  10. 设计模式(三):“花瓶+鲜花”中的装饰者模式(Decorator Pattern)

    在前两篇博客中详细的介绍了"策略模式"和“观察者模式”,今天我们就通过花瓶与鲜花的例子来类比一下“装饰模式”(Decorator Pattern).在“装饰模式”中很好的提现了开放 ...

随机推荐

  1. 用神奇的currentColor制作简洁的颜色动画效果

    先上一个兼容性总结图:老版本ie可以直接用复杂方法了,套用某表情包的话:  2016年了,做前端你还考虑兼容IE6?你这简直是自暴自弃! 好了,知道了兼容性,我们可以放心的使用了. 在CSS3中扩展了 ...

  2. ASP.NET Core 中文文档 第五章 测试(5.2)集成测试

    原文: Integration Testing 作者: Steve Smith 翻译: 王健 校对: 孟帅洋(书缘) 集成测试确保应用程序的组件组装在一起时正常工作. ASP.NET Core支持使用 ...

  3. 记录一次bug解决过程:数据迁移

    一 总结 不擅长语言表达,勤于沟通,多锻炼 调试MyBatis中SQL语法:foreach 问题:缺少关键字VALUES.很遗憾:它的错误报的让人找不着北. 二 BUG描述:MyBatis中批量插入数 ...

  4. BPM配置故事之案例12-触发另外流程

    还记得阿海么,对就是之前的那个采购员,他又有了些意见. 阿海:小明,你看现在的流程让大家都这么方便,能不能帮个忙让我也轻松点啊-- 小明:--你有什么麻烦,现在不是已经各个部门自己提交申请了嘛? 阿海 ...

  5. keepalived从机接管后主机恢复不抢占VIP

    在lvs+keepalived环境中,为了减小keepalived主从切换带来的意外风险,,设置主机恢复后不抢占VIP.待进行vrrp协议通告备机不可用时切换.主要修改两个地方.(红色部分) 只需修改 ...

  6. Entity Framework 6 Recipes 2nd Edition(11-9)译 -> 在LINQ中使用规范函数

    11-9. 在LINQ中使用规范函数 问题 想在一个LINQ查询中使用规范函数 解决方案 假设我们已经有一个影片租赁(MovieRental )实体,它保存某个影片什么时候租出及还回来,以及滞纳金等, ...

  7. keil MDK error: L6236E: No section matches selector - no section 错误

    今天板子刚到,新建的第一个工程就报错了. .\Objects\cse.sct(7): error: L6236E: No section matches selector - no section t ...

  8. Windows 10 安装SVN 不显示状态图标--解决方法

    升级win10以后,什么都正常,就是svn版本库图标不见了,图标的显示有助于我们定位代码的修改及提交情况,该怎么办呢? 下面分享详细的解决办法亲测有用: 其实也比较简单, 在注册表中找到此项: HKE ...

  9. redis成长之路——(六)

    redis配置 为了码农在代码上只关心业务以及代码上的统一性,wenli.drive.redis内部使用配置来完成那些不同的场景,也就是说随便填填配置就能适应不同的场景! 当然配置多了码农也会受不了, ...

  10. C#制作、打包、签名、发布Activex全过程

    一.前言 最近有这样一个需求,需要在网页上面启动客户端的软件,软件之间的通信.调用,单单依靠HTML是无法实现了,因此必须借用Activex来实现.由于本人主要擅长C#,自然本文给出了用C#实现的范例 ...