写在前面

为方便读者,本文已添加至索引

  • 设计模式

    学习笔记索引

Decorator(装饰)模式,可以动态地给一个对象添加一些额外的职能。为了更好地理解这个模式,我们将时间线拉回Bridge模式笔记的结尾。那时,白雪公主射出了充满魔法力量的一箭。如好莱坞大片一般,那支飞出的箭矢散发出各种你能想到的美丽光芒。当然,我不会告诉你那支箭华华丽丽地射偏了。因为我们这次更关心这绚烂的魔法效果。要说的是,时の魔导士在建立这个平行世界的时候,定义了一个所有可见物体的抽象类VisualObject

1 class VisualObject {
2 public:
3     virtual void show() = 0;
4     // ... other ...
5 }

只要是出现在这个世界中,并能被世界之外的人们(比如说我们)看到的物体,都是它的子类。同时,必须实现show()方法。show定义了这个物体呈现给观众的样式。当然我们可以看到时の魔导士在创建箭矢时是如何偷懒的:

1 class Arrow : public VisualObject {
2 public:
3     virtual void show() { cout << "你想它是什么样就是什么样。" << endl; }
4     // ... other ...
5 }

当然,这是不可行的。因为肯定有人会把Arrow想像成一个巨大的炮弹,甚至一头会飞的猪,以此来增强整个故事的动画性。但无论如何,当魔导士想要告诉大家,公主射出的箭矢是附着着冰霜效果的话,他必须明确地描述这支箭矢到底长什么样。如果偷懒到底,他大可以就创造个Arrow的子类IceArrow,只需多加个关于冰晶的描述就好了。

但是问题来了,既然可以附着魔法,箭矢就可以有更多更多的效果:附着火焰的,附着闪电的,长得更好看的,等等等等……如果每一个都用子类的话,必然会导致子类数目太多。况且,我们仅仅是希望给某个对象而不是整个类添加一些特效。同时别忘了我们还要考虑箭矢之外的可以附魔的东西。怎么办才好?时の魔导士采用了Decorator模式。

要点梳理

  • 目的分类

    • 对象结构型模式
  • 范围准则
    • 对象(该模式处理对象间的关系,这些关系在运行时刻是可以变化的,更具动态性)
  • 主要功能
    • 动态地给一个对象添加一些额外的职责。就增加功能来说, 它相比生成子类更为灵活。
  • 适用情况
    • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责
    • 处理那些可以撤消的职责
    • 当不能采用生成子类的方法进行扩充时。例如,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长
  • 参与部分
    • Component:定义一个对象接口,可以给这些对象动态地添加职责
    • ConcreteComponent:定义一个对象,可以给这个对象添加一些职责
    • Decorator:维持一个指向Component对象的指针,并定义一个与Component接口一致的接口
    • ConcreteDecorator:向组件添加职能
  • 协作过程
    • Decorator将请求转发给它的Component对象,并有可能在转发请求前后执行一些附加的动作
  • UML图

示例分析 - 宛如大片中的魔法箭矢

请不要太在意上面这幅图到底是谁。为了能给箭矢(乃至更多物体)增加魔法效果,魔导士引入了ObjectDecorator类:

 1 class ObjectDecorator : public VisualObject {
 2 public:
 3     ObjectDecorator(VisualObject *);
 4
 5     virtual void show();
 6     // ... other ...
 7 private:
 8     VisualObject* _component;
 9 }
10
11 void ObjectDecorator::show() {
12     _component->show();
13 }

ObjectDecorator装饰由_component实例变量引用的VisualObject,这个变量在构造器中被初始化。同时,ObjectDecorator将show的请求转发给_component。我们将看到ObjectDecorator的子类之一MagicDecorator,它将能给可视化物件添加魔法的神奇效果:

 1 class MagicDecorator : public ObjectDecorator {
 2 public:
 3     MagicDecorator(VisualObject *, int);
 4     virtual void show();
 5     // ... other ...
 6 private:
 7     int _magicType;
 8
 9     void addMagic(int);
10 }
11
12 void MagicDecorator::show() {
13     ObjectDecorator::show();
14     addMagic(_magicType);
15 }

类似的,我们也可以实现别的装饰类。我们接下来要把这个箭矢放到这个世界中去。假设World类(一个单例类)已经为此提供了一个add2Me操作:

1 void World::add2Me(VisualObject* obj) {
2     // ...
3     obj->show();
4     // ...
5 }

现在我们要让身处世界之外的人也看到一根箭矢,只需这么做:

1 Arrow* arrow = new Arrow();
2 World::getInstance()->add2Me(arrow);

那么此刻,正是展现电影特效各种神奇绚丽效果的时刻了。我们在将箭矢放入世界之前,先给它加上一层寒冰魔法的装饰:

1 #define ICEMAGIC 1
2 World::getInstance()->add2Me(new MagicDecorator(arrow, ICEMAGIC));

OK,白雪公主射出了一支带有寒冰魔法的箭矢。下面的UML图说明了我们这个例子:

特点总结

可以看到Decorator装饰模式有以下2个主要优点和缺点:

  1. 比静态继承更灵活。它提供了更加灵活的向对象添加职能的方式,可以用添加和分离的方法,用装饰在运行时刻增加和删除职能。同时使用Decorator模式可以很容易地重复添加一个特性,比如让箭矢附上冰与火双重魔法。
  2. 避免在层次结构高层的类有太多的特征。它并不试图在一个复杂的可定制的类中支持所有可预见的特征,相反,我们可以定义一个简单的类,并且用Decorator类给它逐渐地添加功能。
  3. Decorator与它的Component不一样。如果我们从对象标识的观点出发,一个被装饰了的组件与这个组件是有差别的,因此,使用装饰时不应该依赖对象标识。
  4. 有许多小对象。采用Decorator模式进行系统设计往往会产生许多看上去类似的小对象,这些对象仅仅在他们相互连接的方式上有所不同,而不是它们的类或是它们的属性值有所不同。尽管对于那些了解这些系统的人来说,很容易对它们进行定制,但是很难学习这些系统,排错也很困难。

同样,我们在使用这个方法时,也要注意:

  1. 接口的一致性。装饰对象的接口必须与它所装饰的Component的接口是一致的,因此,所有的ConcreteDecorator类必须有一个公共的父类(至少在C++中如此)。
  2. 保持Component类的简单性。为了保证接口的一致性,组件和装饰必须有一个公共的Component父类,因此保持这个类的简单性是很重要的。它应集中于定义接口而不是存储数据。否则Component类会变得过于复杂和庞大,因而难以大量使用
  3. 省略抽象的Decorator类。当我们仅需要添加一个职责时,没有必要定义抽象Decorator类。我们常常需要处理现存的类层次结构而不是设计一个新系统,这时我们可以把Decorator向Component转发请求的职责合并到ConcreteDecorator中。

写在最后

今天的笔记就到这里了,欢迎大家批评指正!如果觉得可以的话,好文推荐一下,我会非常感谢的!

转载于:https://www.cnblogs.com/xieziyu/p/3592285.html

[学习笔记]设计模式之Decorator相关推荐

  1. [学习笔记]设计模式之Command

    为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 在上篇Chain of Responsibility(职责链)模式笔记中,我们学习了一种行为型设计模式.今天,我们继续这一主题,来学习 ...

  2. [学习笔记]设计模式之Singleton

    写在前面 为方便读者,本文已添加至索引: 设计模式 魔法手札索引 在前几篇笔记中,我们有了解了部分对象创建型模式,包括Builder(建造者).Abstract Factory(抽象工厂)和Facto ...

  3. [学习笔记]设计模式[6]-{适配器模式外观模式}

    设计原则 最少知识原则:只和你的密友谈话 这个原则的意思是,在系统设计的过程中,不要让太多的类耦合在一起,免得对系统一部分的修改会影响到其他部分.在设计系统之前,应该首先注意对象与对象之间的交互关系, ...

  4. sheng的学习笔记-设计模式-代理模式

    原理图: 代理模式的定义:由于某些原因需要给某对象提供一个代理以控制对该对象的访问.这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介. 至少在以下集中情况下可以用 ...

  5. 学习笔记 ---- 设计模式之观察者模式

    转载: http://www.cnblogs.com/shitouer/archive/2011/09/02/2164048.html Head First里边给出的场景是: 客户需要这样一个App: ...

  6. sheng的学习笔记-设计模式-单例模式

    转载文章:​​​​​​Java单例模式的5种实现方法_明朗晨光的专栏-CSDN博客_单例模式java​​​​​​ 饿汉 类加载的时候就创建了实例 优点:类加载的时候创建一次实例,避免了多线程同步问题 ...

  7. 阿里大神最佳总结Flutter进阶学习笔记,技术详细介绍

    开头 很多人工作了十年,但只是用一年的工作经验做了十年而已. 高级工程师一直是市场所需要的,然而很多初级工程师在进阶高级工程师的过程中一直是一个瓶颈. 移动研发在最近两年可以说越来越趋于稳定,因为越来 ...

  8. 设计模式学习笔记——装饰(Decorator)模式

    设计模式学习笔记--装饰(Decorator)模式 @(设计模式)[设计模式, 装饰模式, decorator] 设计模式学习笔记装饰Decorator模式 基本介绍 装饰案例 类图 实现代码 Dis ...

  9. 7 种 Javascript 常用设计模式学习笔记

    7 种 Javascript 常用设计模式学习笔记 由于 JS 或者前端的场景限制,并不是 23 种设计模式都常用. 有的是没有使用场景,有的模式使用场景非常少,所以只是列举 7 个常见的模式 本文的 ...

最新文章

  1. linux install g++
  2. JQuery面试题1
  3. app.vue中引用图片src=“../assets/logo.png“报错未找到图片
  4. java8 collect 类型转换_Java 8 新特性 Stream类的collect方法
  5. Android游戏开发系统控件-CheckBox
  6. windows安装zabbix客户端
  7. 如何以出售开源软件为生?
  8. RED HAT 7 性能监控工具
  9. 游戏服务器数据库踩过的坑
  10. 2000~2022年Java学习笔记
  11. 简单c语言程序例子与运行结果图,C语言程序第一次作业
  12. c语言逻辑运算符用法大全,C语言逻辑运算符介绍和示例
  13. 计算机太极图怎么设计,AI绘制太极图两种方法介绍
  14. JasperReport生成pdf文件 Java开发pdf文件 pdf文件生成及下载
  15. 信用卡葵花宝典 阅读笔记(二)
  16. c语言while(*p *q),C语言经典例题和答案
  17. 小数保留两位小数,第三位四舍五入
  18. 简单学习识谱(六线谱)
  19. linux bash语法检查,ShellCheck — Shell Script 语法检查工具
  20. 骄傲自满的联发科在中高端市场沦落,技术还是不如高通啊

热门文章

  1. 查看wifi密码-(鸡贼小技巧)
  2. jsp70877婚庆策划婚车预订网站
  3. javadoc使用方法
  4. ubuntu 16.04 LTS flash player播放器插件安装方法
  5. SQLite的使用------多张图片存储
  6. 堆排序-HeapSort
  7. matlab中 mean,matlabmean matlab中mean函数的使用
  8. 牛仔纯儿牛仔加盟怎么样?纯儿骗子?纯儿牛仔裤加盟骗子
  9. 水院运动场馆预约系统的设计与实现
  10. iOS 解决 dyld: Library not loaded: /System/Library/Frameworks/CoreMedia.framework/CoreMedia