装饰模式(结构型)
前言
在平时的生活中,手机几乎无处不在,有些比较细心的机友可能会给手机增加各种一些额外扩展的功能,如屏保防止手机碎屏,添加些小挂件增加美观等等。同时市面上有各种类型的手机,如Iphone,Nokia等等,,这样就会组合成带有装饰功能的各种手机了,如带屏保的iphone手机,带屏保又带挂件的Nokia手机等。对于此种现象的描述,可以考虑使用装饰模式。
装饰模式
装饰模式,是通过对象组合的功能复用方式来为已有的对象增加额外的功能。是一种用于替代继承的技术,它通过一种无须定义子类的方式来给对象动态增加职责,使用对象之间的关联关系取代类之间的继承关系。在装饰模式中引入了装饰类,在装饰类中既可以调用待装饰的原有类的方法,还可以增加新的方法,以扩充原有类的功能
通常给对象添加功能,要么直接修改对象添加相应的功能,要么派生对应的子类来扩展,抑或是使用对象组合的方式。
在面向对象的设计中,而我们也应该尽量使用对象组合而不是对象继承来扩展和复用功能。
装饰器模式就是基于对象组合的方式,可以很灵活的给对象添加所需要的功能。装饰器模式的本质就是动态组合。动态是手段,组合才是目的。是通过把复杂的功能简单化,分散化,然后再运行期间根据需要来动态组合的这样一个模式。
意图
动态地给一个对象添加一些额外的职责。就增加功能来说, 装饰模式相比生成子类更灵活
由于具体构件类和装饰类都实现了相同的抽象构件接口,因此装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任,换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不需要创造更多子类的情况下,将对象的功能加以扩展,是一种对象结构型模式
参与者
Component
具体构件和抽象装饰类的共同父类
声明了具体构件类中实现的业务方法,可以使客户端可以一致的处理装饰前和装饰后的对象ConcreteComponent
具体构件类,抽象构件类的子类
实现了抽象构件类的方法,同时装饰器可以给它增加额外的职责或方法Decorator
抽象装饰类,抽象构件类的子类
用于给具体构件增加职责,但是具体职责在其子类中实现
维护一个指向抽象构件类的指针或引用,通过它可以调用装饰之前构件对象的方法,并且通过抽象装饰类的子类来扩展该方法来达到装饰的目的ConcreteDecorator
具体装饰类, 抽象装饰类的子类
它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为
模式结构
代码实现
首先定义一个抽象构件类
Component
,及操作接口Operator()
:1
2
3
4
5
6
7
8// 抽象构件类
class Component
{
public:
Component() {};
public:
virtual void Operator() = 0;
};再定义具体构件类
ConcreteComponentA
和ConcreteComponentB
,并实现其相应接口Operator()
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//具体构件类A
class ConcreteComponentA : public Component
{
public:
ConcreteComponentA() {};
public:
void Operator()
{
cout << "I am Concrete Component A" << endl;
}
};
//具体构件类B
class ConcreteComponentB : public Component
{
public:
ConcreteComponentB() {};
public:
void Operator()
{
cout << "I am Concrete Component B" << endl;
}
};定义一个抽象装饰器类
Decorator
,其继承于抽象构件类Component
,以保持接口Operator()
一致性,客户端可以一致性的处理装饰前和装饰后的对象,同时具有一个抽象构件类的对象指针或引用,以调用装饰前的构件方法同时还可以扩展该方法达到装饰的目的:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 装饰类父类(非抽象,继承抽象构件类,与桥接模式的不同)
class Decorator : public Component
{
private:
Component *m_pC; // 与桥接模式类似,都是对象组合
public:
Decorator(Component *pC) : m_pC(pC) {};
void Operator()
{
if (NULL != m_pC)
{
m_pC->Operator();
}
}
};定义具体的装饰器类
ConcreteDecoratorA
和ConcreteDecoratorB
为构件增加具体的不同职责功能,可以是状态或操作:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29// 具体的装饰类A,增加状态
class ConcreteDecoratorA : public Decorator
{
private:
int m_AddedState;
public:
ConcreteDecoratorA(Component *pC, int state) : Decorator(pC), m_AddedState(state) {};
void Operator()
{
Decorator::Operator(); // 装饰之前的操作
cout << "Concrete Decorator A Added State: " << m_AddedState << endl;
//扩展状态
}
};
// 具体的装饰类B,增加操作
class ConcreteDecoratorB : public Decorator
{
public:
ConcreteDecoratorB(Component *pC) : Decorator(pC){};
void Operator()
{
Decorator::Operator(); // 装饰之前的操作
AddedOperator(); // 扩展的操作
}
void AddedOperator()
{
cout << "Concrete Decorator B Added Behavior:" << endl;
}
};测试装饰模式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32void DecoratorTest_General()
{
// 创建一个增加了装饰A的组件A
Component *pC_A = new ConcreteComponentA();
Component *pD_AA = new ConcreteDecoratorA(pC_A,10);
pD_AA->Operator();
// 创建一个增加了装饰B的组件A
Component *pD_BA = new ConcreteDecoratorB(pC_A);
pD_BA->Operator();
// 创建一个同时增加了装饰 A 和 B的组件A
Component *pD_AB_A = new ConcreteDecoratorB(pD_AA);
pD_AB_A->Operator();
// 创建一个增加了装饰A的组件B
Component *pC_B = new ConcreteComponentB();
Component *pD_AB = new ConcreteDecoratorA(pC_B,10);
pD_AB->Operator();
// 创建一个增加了装饰B的组件B
Component *pD_BB = new ConcreteDecoratorB(pC_B);
pD_BB->Operator();
delete pC_A; pC_A = NULL;
delete pC_B; pC_B = NULL;
delete pD_AA; pD_AA = NULL;
delete pD_BA; pD_BA = NULL;
delete pD_AB; pD_AB = NULL;
delete pD_BB; pD_BB = NULL;
delete pD_AB_A; pD_AB_A = NULL;
}
运行结果:
I am Concrete Component A
Concrete Decorator A Added State: 10
I am Concrete Component A
Concrete Decorator B Added Behavior:
I am Concrete Component A
Concrete Decorator A Added State: 10
Concrete Decorator B Added Behavior:
I am Concrete Component B
Concrete Decorator A Added State: 10
I am Concrete Component B
Concrete Decorator B Added Behavior:
对装饰模式的分类
透明装饰模式
上面描述的标准装饰模式即是透明的装饰模式,在透明装饰模式中,要求客户端完全针对抽象编程,装饰模式的透明性要求客户端程序不应该将对象声明为具体构件类型或具体装饰类型,而应该全部声明为抽象构件类型。对于客户端而言,具体构件对象和具体装饰对象没有任何区别
如:
1 | Component *c1, *c2; // 使用抽象构件类定义对象 |
此时,具体装饰类扩展的方法必须在构件的原有方法中得到显示的调用,但是客户端不能显示的单独调用新增加的业务方法
半透明装饰模式
半透明装饰模式中,对于具体装饰类,需要使用具体装饰类型来定义装饰之后的对象,而不完全针对抽象编程。有时我们需要单独调用新增的业务方法。为了能够调用到新增方法,我们不得不用具体装饰类型来定义装饰之后的对象,而具体构件类型还是可以使用抽象构件类型来定义,这种装饰模式即为半透明装饰模式,也就是说,对于客户端而言,具体构件类型无须关心,是透明的;但是具体装饰类型必须指定,这是不透明的,如:
1 | Component *c1; |
半透明装饰模式可以给系统带来更多的灵活性,设计相对简单,使用起来也非常方便;但是其最大的缺点在于不能实现对同一个对象的多次装饰,而且客户端需要有区别地对待装饰之前的对象和装饰之后的对象
使用场景
- 它使得我们可以给某个对象而不是整个类添加一些功能
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责
- 处理那些可以撤销的职责
-当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰模式,不能采用继承的情况是系统中存在大量独立的扩展,为支持每一种扩展或者扩展之间的组合将产生大量的子类
优缺点
- 优点
- 对于扩展一个对象的功能,装饰模式比继承更加灵活性, 可以通过一种动态的方式来扩展一个对象的功能,不会导致类的个数急剧增加
- 可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,得到功能更为强大的对象
- 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合“开闭原则”
- 缺点
- 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,大量小对象的产生势必会占用更多的系统资源,在一定程序上影响程序的性能
- 装饰模式提供了一种比继承更加灵活机动的解决方案,但同时也意味着比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为繁琐
与桥接模式的区别
二者都是为了防止过度的继承,从而造成子类泛滥的情况
桥接模式的定义是将抽象化与实现化分离(用组合的方式而不是继承的方式),使得两者可以独立变化,可以减少派生类的增长桥接模式中所说的分离,其实是指将结构与实现分离(当结构和实现有可能发生变化时)或属性与基于属性的行为进行分离;而装饰者只是对基于属性的行为进行封闭成独立的类,从而达到对其进行装饰,也就是扩展
桥接中的行为是横向的行为,行为彼此之间无关联,注意这里的行为之间是没有关联的,就比如异常和异常处理之间是没有行为关联的一样;而装饰者模式中的行为具有可叠加性,其表现出来的结果是一个整体,一个各个行为组合后的一个结果
比如:异常类和异常处理类之间就可以使用桥接模式来实现完成,而不能使用装饰模式来进行设计;如果对于异常的处理需要进行扩展时,我们又可以对异常处理类添加Decorator,从而添加处理的装饰,达到异常处理的扩展,这就是一个桥接模式与装饰模式的搭配;
装饰模式具体实例
手机装饰设计
实现前言所描述的不同手机的装饰设计,允许为手机添加特性,比如增加挂件、屏幕贴膜等
代码实现
抽象手机类:
1
2
3
4
5
6//抽象手机类
class Phone
{
public:
virtual void ShowPhone() = 0;
};具体手机类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// Nokia
class NokiaPhone : public Phone
{
public:
void ShowPhone()
{
cout << "I am Nokia Phone" << endl;
}
};
// Iphone
class ApplePhone : public Phone
{
public:
void ShowPhone()
{
cout << "I am Apple Phone" << endl;
}
};抽象手机装饰类,并继承与抽象手机类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 手机装饰类(非抽象,父类,继承Phone)
class DecoratorPhone : public Phone
{
private:
Phone *m_pPhone;
public:
DecoratorPhone(Phone *pP) : m_pPhone(pP) {};
public:
virtual void ShowPhone()
{
if (NULL != m_pPhone)
{
m_pPhone->ShowPhone();
}
}
};具体装饰类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33// 增加屏保的装饰类
class DecoratorPhoneA : public DecoratorPhone
{
public:
DecoratorPhoneA(Phone *pP) : DecoratorPhone(pP) {};
public:
void ShowPhone()
{
DecoratorPhone::ShowPhone();
AddDecoratorA();
}
void AddDecoratorA()
{
cout << "Add Decorator Phone of Screen Protection" << endl;
}
};
// 增加挂件的装饰类
class DecoratorPhoneB : public DecoratorPhone
{
public:
DecoratorPhoneB(Phone *pP) : DecoratorPhone(pP) {};
public:
void ShowPhone()
{
DecoratorPhone::ShowPhone();
AddDecoratorB();
}
void AddDecoratorB()
{
cout << "Add Decorator Phone of Pendant " << endl;
}
};测试Phone装饰模式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29void DecoratorTest_Phone()
{
// 装了屏保的Nokia手机
Phone *pP_Nokia = new NokiaPhone();
Phone *pD_Pendant_Nokia = new DecoratorPhoneA(pP_Nokia);
pD_Pendant_Nokia->ShowPhone();
// 装了屏保,和挂件的Nokia手机
Phone *pD_Pendant_ScnPrt_Nokia = new DecoratorPhoneB(pD_Pendant_Nokia);
pD_Pendant_ScnPrt_Nokia->ShowPhone();
// 装了挂件的Nokia手机
Phone *pD_ScnPrt_Nokia = new DecoratorPhoneB(pP_Nokia);
pD_ScnPrt_Nokia->ShowPhone();
// 装了屏保,和挂件的Apple手机
Phone *pP_Apple = new ApplePhone();
Phone *pD_Pendant_Apple = new DecoratorPhoneA(pP_Apple);
Phone *pD_Pendant_ScnPrt_Apple = new DecoratorPhoneB(pD_Pendant_Apple);
pD_Pendant_ScnPrt_Apple->ShowPhone();
delete pP_Nokia; pP_Nokia = NULL;
delete pP_Apple; pP_Apple = NULL;
delete pD_Pendant_Nokia; pD_Pendant_Nokia = NULL;
delete pD_ScnPrt_Nokia; pD_ScnPrt_Nokia = NULL;
delete pD_Pendant_ScnPrt_Nokia; pD_Pendant_ScnPrt_Nokia = NULL;
delete pD_Pendant_Apple; pD_Pendant_Apple = NULL;
delete pD_Pendant_ScnPrt_Apple; pD_Pendant_ScnPrt_Apple = NULL;
}运行结果:
I am Nokia Phone
Add Decorator Phone of Screen Protection
I am Nokia Phone
Add Decorator Phone of Screen Protection
Add Decorator Phone of Pendant
I am Nokia Phone
Add Decorator Phone of Pendant
I am Apple Phone
Add Decorator Phone of Screen Protection
Add Decorator Phone of Pendant