[学习笔记]设计模式之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(组合)模式中,用户可以使用多个简单的组件以形成较大的组件,而这些组件还可能进一步组合成更大的.它重要的特性是能够 ...
随机推荐
- map/reduce实现 排序
import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.co ...
- PHP两个字符串比较(人为出错),两字符串类型和数据表面相等,但strcmp()结果不为0
PHP中,比较两个字符串是否相等用:strcmp(): PHP strcmp() 函数 PHP String 函数 定义和用法 strcmp() 函数比较两个字符串. 该函数返回: 0 - 如果两个字 ...
- 3 weekend110的配置hadoop(格式化) + 一些问题解决 + 未免密码配置
由于,之前,已经在/etc/profile里,配置了hadoop的全局变量,所以,现在可以在任何路径下执行hadoop命令. 来玩玩, 其实啊,在这里,出现了错误, 参考解决链接: http://it ...
- SQL查询记录添加序号(HANA)
语法:ROW_NUMBER() OVER(PARTITION BY COLUMN ORDER BY COLUMN) row_number() OVER() 从1开始,为每一条分组记录返回一个数字,这里 ...
- PowerDesigner跟表的字段加注释
选择那个表 右键- >Properties- >Columns- >Customize Columns and Filter(或直接用快捷键Ctrl+U)- >Comment( ...
- PAT---1005. Spell It Right (20)
#include<iostream> #include<stack> #include<string.h> ]= {"zero", " ...
- 第一个Android项目——计算器
第一个Android项目——计算器 效果 开始学Android一两个星期了,学习了一下基本的Activity.简单控件及几个简单布局,打算找个东西来练练手,于是就选择发计算器.关于计算器中用到的四则运 ...
- [Javascript] MetaProgramming: function name
Each function should have a 'name' property. It can be anonymous, empty, the same as function name, ...
- android开发之国际化
国际化,听起来高大上,做起来很简单. 我们来实现一个简单的效果,让应用根据系统的语言来做不同的显示,假如android系统默认是英语,应用就以英文的形式显示,如果android系统默认是中文,则应用就 ...
- vi 使用笔记
基本A 当前行追加J 去除本行和下一行之间的换行符(写CSS利器)~ 光标所在处的字符进行大小写互换* 向前搜索目前光标所在的单词# 向后搜索目前光标所在的单词% 查找与光标所在处相匹配的反括号, 包 ...