[学习笔记]设计模式之Decorator
写在前面
为方便读者,本文已添加至索引:
设计模式
学习笔记索引
Decorator(装饰)模式,可以动态地给一个对象添加一些额外的职能。为了更好地理解这个模式,我们将时间线拉回Bridge模式笔记的结尾。那时,白雪公主射出了充满魔法力量的一箭。如好莱坞大片一般,那支飞出的箭矢散发出各种你能想到的美丽光芒。当然,我不会告诉你那支箭华华丽丽地射偏了。因为我们这次更关心这绚烂的魔法效果。要说的是,时の魔导士在建立这个平行世界的时候,定义了一个所有可见物体的抽象类VisualObject:
class VisualObject {
public:
virtual void show() = ;
// ... other ...
}
只要是出现在这个世界中,并能被世界之外的人们(比如说我们)看到的物体,都是它的子类。同时,必须实现show()方法。show定义了这个物体呈现给观众的样式。当然我们可以看到时の魔导士在创建箭矢时是如何偷懒的:
class Arrow : public VisualObject {
public:
virtual void show() { cout << "你想它是什么样就是什么样。" << endl; }
// ... other ...
}
当然,这是不可行的。因为肯定有人会把Arrow想像成一个巨大的炮弹,甚至一头会飞的猪,以此来增强整个故事的动画性。但无论如何,当魔导士想要告诉大家,公主射出的箭矢是附着着冰霜效果的话,他必须明确地描述这支箭矢到底长什么样。如果偷懒到底,他大可以就创造个Arrow的子类IceArrow,只需多加个关于冰晶的描述就好了。
但是问题来了,既然可以附着魔法,箭矢就可以有更多更多的效果:附着火焰的,附着闪电的,长得更好看的,等等等等……如果每一个都用子类的话,必然会导致子类数目太多。况且,我们仅仅是希望给某个对象而不是整个类添加一些特效。同时别忘了我们还要考虑箭矢之外的可以附魔的东西。怎么办才好?时の魔导士采用了Decorator模式。
要点梳理
- 目的分类
- 对象结构型模式
- 范围准则
- 对象(该模式处理对象间的关系,这些关系在运行时刻是可以变化的,更具动态性)
- 主要功能
- 动态地给一个对象添加一些额外的职责。就增加功能来说, 它相比生成子类更为灵活。
- 适用情况
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责
- 处理那些可以撤消的职责
- 当不能采用生成子类的方法进行扩充时。例如,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长
- 参与部分
- Component:定义一个对象接口,可以给这些对象动态地添加职责
- ConcreteComponent:定义一个对象,可以给这个对象添加一些职责
- Decorator:维持一个指向Component对象的指针,并定义一个与Component接口一致的接口
- ConcreteDecorator:向组件添加职能
- 协作过程
- Decorator将请求转发给它的Component对象,并有可能在转发请求前后执行一些附加的动作
- UML图
示例分析 - 宛如大片中的魔法箭矢
请不要太在意上面这幅图到底是谁。为了能给箭矢(乃至更多物体)增加魔法效果,魔导士引入了ObjectDecorator类:
class ObjectDecorator : public VisualObject {
public:
ObjectDecorator(VisualObject *); virtual void show();
// ... other ...
private:
VisualObject* _component;
} void ObjectDecorator::show() {
_component->show();
}
ObjectDecorator装饰由_component实例变量引用的VisualObject,这个变量在构造器中被初始化。同时,ObjectDecorator将show的请求转发给_component。我们将看到ObjectDecorator的子类之一MagicDecorator,它将能给可视化物件添加魔法的神奇效果:
class MagicDecorator : public ObjectDecorator {
public:
MagicDecorator(VisualObject *, int);
virtual void show();
// ... other ...
private:
int _magicType; void addMagic(int);
} void MagicDecorator::show() {
ObjectDecorator::show();
addMagic(_magicType);
}
类似的,我们也可以实现别的装饰类。我们接下来要把这个箭矢放到这个世界中去。假设World类(一个单例类)已经为此提供了一个add2Me操作:
void World::add2Me(VisualObject* obj) {
// ...
obj->show();
// ...
}
现在我们要让身处世界之外的人也看到一根箭矢,只需这么做:
Arrow* arrow = new Arrow();
World::getInstance()->add2Me(arrow);
那么此刻,正是展现电影特效各种神奇绚丽效果的时刻了。我们在将箭矢放入世界之前,先给它加上一层寒冰魔法的装饰:
#define ICEMAGIC 1
World::getInstance()->add2Me(new MagicDecorator(arrow, ICEMAGIC));
OK,白雪公主射出了一支带有寒冰魔法的箭矢。下面的UML图说明了我们这个例子:
特点总结
可以看到Decorator装饰模式有以下2个主要优点和缺点:
- 比静态继承更灵活。它提供了更加灵活的向对象添加职能的方式,可以用添加和分离的方法,用装饰在运行时刻增加和删除职能。同时使用Decorator模式可以很容易地重复添加一个特性,比如让箭矢附上冰与火双重魔法。
- 避免在层次结构高层的类有太多的特征。它并不试图在一个复杂的可定制的类中支持所有可预见的特征,相反,我们可以定义一个简单的类,并且用Decorator类给它逐渐地添加功能。
- Decorator与它的Component不一样。如果我们从对象标识的观点出发,一个被装饰了的组件与这个组件是有差别的,因此,使用装饰时不应该依赖对象标识。
- 有许多小对象。采用Decorator模式进行系统设计往往会产生许多看上去类似的小对象,这些对象仅仅在他们相互连接的方式上有所不同,而不是它们的类或是它们的属性值有所不同。尽管对于那些了解这些系统的人来说,很容易对它们进行定制,但是很难学习这些系统,排错也很困难。
同样,我们在使用这个方法时,也要注意:
- 接口的一致性。装饰对象的接口必须与它所装饰的Component的接口是一致的,因此,所有的ConcreteDecorator类必须有一个公共的父类(至少在C++中如此)。
- 保持Component类的简单性。为了保证接口的一致性,组件和装饰必须有一个公共的Component父类,因此保持这个类的简单性是很重要的。它应集中于定义接口而不是存储数据。否则Component类会变得过于复杂和庞大,因而难以大量使用
- 省略抽象的Decorator类。当我们仅需要添加一个职责时,没有必要定义抽象Decorator类。我们常常需要处理现存的类层次结构而不是设计一个新系统,这时我们可以把Decorator向Component转发请求的职责合并到ConcreteDecorator中。
写在最后
今天的笔记就到这里了,欢迎大家批评指正!如果觉得可以的话,好文推荐一下,我会非常感谢的!
[学习笔记]设计模式之Decorator的更多相关文章
- [学习笔记]设计模式之Proxy
为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 “魔镜啊魔镜,谁是这个世界上最美丽的人?” 每到晚上,女王都会问魔镜相同的问题(见Decorator模式).这是她还曾身为女巫时留下的 ...
- [学习笔记]设计模式之Abstract Factory
写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 在上篇笔记Builder设计模式中,时の魔导士祭出了自己的WorldCreator.尽管它因此能创造出一个有山有树有房子的世界,但是白 ...
- [学习笔记]设计模式之Builder
写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 作为一个新入职的魔导士呢,哦不,是程序员,我以为并没有太多机会去设计项目的软件架构.但是,工作一段时间之后,自己渐渐意识到,哪怕是自己 ...
- [学习笔记]设计模式之Adapter
写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 Adapter(适配器)模式主要解决接口不匹配的问题.为此,让我们要回到最初Builder模式创建平行世界时,白雪公主和小霍比特人的谜 ...
- [学习笔记]设计模式之Bridge
写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 “魔镜啊魔镜,谁是这个世界上最美丽的人?”月光中,一个低沉的声音回荡在女王的卧室.“是美丽的白雪公主,她正和小霍比特人们幸福快乐地生活 ...
- [学习笔记]设计模式之Prototype
写在前面 为方便读者,本文已添加至索引: 设计模式 学习笔记索引 在笔记Builder模式中,我们曾见到了最初用于创建平行世界的函数createWorld,并且它是Mage类的成员函数(毕竟是专属于魔 ...
- [学习笔记]设计模式之Command
为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 在上篇Chain of Responsibility(职责链)模式笔记中,我们学习了一种行为型设计模式.今天,我们继续这一主题,来学习 ...
- [学习笔记]设计模式之Chain of Responsibility
为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 最近时间比较紧,所以发文的速度相对较慢了.但是看到园子里有很多朋友对设计模式感兴趣,我感觉很高兴,能够和大家一起学习这些知识. 之前的 ...
- [学习笔记]设计模式之Composite
为方便读者,本文已添加至索引: 设计模式 学习笔记索引 写在前面 在Composite(组合)模式中,用户可以使用多个简单的组件以形成较大的组件,而这些组件还可能进一步组合成更大的.它重要的特性是能够 ...
随机推荐
- Hadoop 配置好hive,第一次在conf能进入,第二次就不行了,怎么办?
问题描述: 在 Hadoop 配置好 hive 数据仓库,在conf目录下通过hive命令进入hive数据仓库,非常顺利. 但关闭终端,第二次按这种方式却显示,无次命令. 怎么办? 解决办法: 在h ...
- Tomcat内存不足的解决办法
Tomcat增加内存 -Xms512m -Xmx512m -XX:PermSize=128M -XX:MaxPermSize=512m -DCOM.HUATENG.PRODUCTION_MODE=fa ...
- 解决oracle_4031错误的方法
原因分析: 大量的硬解析出现,产生大量小的free chunk突然出现大的sql 1.临时方法 alter system flush shared_pool; 2.共享SQL alter system ...
- Linux下多任务间通信和同步-信号
Linux下多任务间通信和同步-信号 嵌入式开发交流群280352802,欢迎加入! 1.概述 信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式.信号可以直接进行用户空间进程和内核进程之间的 ...
- Nice validator领先的表单验证解决方案 转
Nice validator是一个简单智能的Web表单验证插件,可以验证现有的所有格式,比如邮箱地址.电话号码等,您还可以自定义规则验证,插件基于jQuery库,支持多种语言配置. 安装 1.您可以访 ...
- js发起长轮询获取推送消息
<!DOCTYPE HTML> <html lang="en-US"> <head> <meta charset="UTF-8& ...
- UIAlertController:弹框4步走
对于大多数的App来说,弹框这种交互方式基本上是必不可少的.在新的iOS系统中(具体版本我忘了),以前的UIAlertView被弃用,而换成了现在的UIAlertController. UIAlite ...
- Javascript substr方法在某些浏览器下行为出现BUG的补丁代码
主要思路是使用兼容性和稳定性都保持一致的substring方法重写/覆盖substr /** * String.substr() bug fix * @param start * @param len ...
- android考试题
一.选择题 1. Math.round(11.5)等于多少( ). Math.round(-11.5) 等于多少( C ). A.11 ,-11 B.11 ,-12 C.12 ,-1 ...
- android.util.Log说明和android 像素说明
1. android.util.Log常用的方法有以下5个:Log.v() Log.d() Log.i() Log.w() 以及 Log.e() .根据首字母对应VERBOSE,DEBUG,INFO, ...