【设计模式】组合模式与装饰模式

本文最后更新于:2023年12月17日 下午

Composite组合 —— 对象结构型模式

  1. 意图

将对象组合成树形结构以表示“部分——整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。

  1. 使用性
  • 你想表示对象的部分 - 整体层次结构
  • 你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有独享
  1. 结构
    类图设计

典型结构如下:
典型结构

  1. 参与者
  • Component(Graphic)
    • 为组合中的对象声明接口
    • 在适当情况下,实现所有类共有接口的缺省行为
    • 声明一个接口用于访问和管理Component的子组件
    • (可选)在递归结构中定义一个接口,用于访问一个父组件,并在合适情况下实现它
  • Leaf(Rectangle,Line,Text等)
    • 在组合中表示叶节点对象,叶节点没有子节点
    • 在组合中定义图元对象的行为
  • Composite(Picture)
    • 定义有子部件的那些部件的行为
    • 存储子部件
    • 在Component接口中实现与子部件有关的操作
  • Cilent
    • 通过Component接口操作组合部件的对象
  1. 相关模式

通常,部件 - 父部件连接用于 Responsibility of Chain模式

  • Decorator模式经常与Composite模式一起使用。当装饰和组合一起使用时,它们通常有一个公共的父类。因此装饰必须支持具有Add、Remove、GetChild操作的Component接口
  • Flyweight 让你共享组件,但不再能引用其父组件
  • Iterator 可用来遍历Composite
  • Visitor 将本来应该分布在Composite和Leaf类中的操作和行为局部化
  1. 示例代码
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Decorator.h
#include <iostream>
#include <string>
#include <vector>

#ifndef PATTERNDESIGN_DECORATOR_H
#define PATTERNDESIGN_DECORATOR_H


void test_composite() ;


/**
* @brief 组合模式
* 粗略地将计算机作为例子,一些基本的外设作为Leaf,主板作为一个小整体 Composite(里面又包含了cpu、内存、显卡等)
* 以查询价格作为公共接口
*/

// Component类
class Equipment {
public:
Equipment(std::string name, int price) : name(name),price(price) {}
virtual ~Equipment() = default;
virtual std::string get_name() {
return name;
}
virtual int get_price() {
return price;
}

virtual void add(Equipment* equipment) {}
virtual void remove(Equipment* equipment) {}

// TODO:可以更近一步,使用 Iterator 模式
virtual Equipment* get_child(int index) {
return nullptr;
}

protected:
std::string name;
int price;
};

// 鼠标,Leaf类
class Mouse : public Equipment{
public:
Mouse(std::string name, int price) : Equipment(name,price) {

}
virtual int get_price() {
return price * 0.9 ; // 比如这段时间鼠标平均价下降
}
};

// CPU,Leaf类
class CPU : public Equipment {
public:
CPU(std::string name, int price, std::string type) : Equipment(name, price), type(type) {

}
private:
std::string type;
};

// 显卡,Leaf类
class GraphicCards :public Equipment {
public :
GraphicCards(std::string name, int price) : Equipment(name, price) {

}
virtual int get_price() {
return price * 2; // 这段时间显卡溢价
}
};

// Composite类,主板
class MainBoard : public Equipment {
public:
MainBoard(std::string name, int price) : Equipment(name, price) {

}
virtual void add(Equipment* equipment) {
if (equipment == nullptr)
return;
children.push_back(equipment);
}
virtual void remove(Equipment* equipment) {
if (equipment == nullptr)
return ;
for (auto iter = children.begin(); iter != children.end(); iter++) {
if (*iter == equipment) {
children.erase(iter);
break;
}
}

}
virtual Equipment* get_child(int index) {
if (index < 0 || index >= children.size()) {
return nullptr;
}
return children[index];
}

// 累加该设备上附带的所有设备的价值
virtual int get_price() {
int sum = price;
for (Equipment* p : children) {
sum += p->get_price();
}
return sum;
}

private:
std::vector<Equipment*> children;
};

#endif //PATTERNDESIGN_DECORATOR_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Decorator.cpp

#include <iostream>
#include "Composite.h"
using namespace std;

void test_composite() {
cout << "——————————开始测试装饰模式——————————" << endl;
Equipment* cpu = new CPU("CPU", 1000, "Intel");
Equipment* graphic_card = new GraphicCards("显卡",4000);
Equipment* main_board = new MainBoard("主板",800);
main_board->add(cpu);
main_board->add(graphic_card);
cout << main_board->get_name() << " with " ;
cout << main_board->get_child(0)->get_name() << " " << main_board->get_child(1) ->get_name() << endl;
cout << "all price:" << main_board->get_price() << endl;
main_board->remove(cpu);
cout << "主板(带有显卡)当前价格:" << main_board->get_price() << endl;
cout << "——————————结束测试装饰模式——————————" << endl;
}

Decorator(装饰)——对象结构型模式

  1. 意图

动态地给一个对象添加一些额外的职责。就添加功能来说,Decorator模式相比生成子类更加灵活

  1. 别名

包装器(wrapper)

  1. 动机

有时我们希望给某个对象而不是整个类添加一些功能,例如一个图形用户界面工具箱允许你对任意一个用户界面组件添加一些特性(例如边框),或是一些行为(例如窗口滑动)。

使用继承机制是添加功能的一种有效途经,从其他类继承过来的边框特性可以被多个子类的实例所使用,但这种方法不够灵活,因为边框的选择时静态的,用户不能控制对组件加边框的方式和时机。

一种较为灵活的方式就是将组件嵌入另一个对象中,由这个对象添加边框。我们称这个嵌入的对象为装饰。这个装饰与它所装饰的组件接口一致,因此它对使用该组件的客户透明。它将客户请求转发给该组件,并且可能在转发前后执行一些额外的动作(例如画一个边框)。透明性使得你可以递归地嵌套多个装饰,从而可以添加任意多的功能。

  1. 适用性

以下情况下使用Decorator模式:

  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责
  • 处理那些可以撤销的职责
  • 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是,类定义被隐藏,或类定义不能用于生成子类(这个意思就是装饰的意思?)。
  1. 结构

类图设计

  1. 参与者
  • Component(VisualComponent)
    • 定义一个对象接口,可以给这些对象动态地添加职责
  • ConcreteComponent(TextView)
    • 定义一个对象,可以给这个对象添加一些职责
  • Decorator
    • 维持一个指向Component对象的指针,并定义一个与Component接口一致的接口
  • ConcreteDecorator(BorderDecorator、ScrollDecorator)
    • 向组件添加职责
  1. 协作

Decorator将请求转发给它的Component对象,并有可能在转发请求前后执行一些附加的动作

  1. 相关模式
  • Adapter:Decorator模式不同于Adapter模式,因为装饰仅改变对象的职责而不改变它的接口;而适配器将给对象一个全新的接口
  • Composite:可以将装饰视为一个退化的、仅有一个组件的组合。然而, 装饰仅给对象添加一些额外的职责——它的目的不在于对象聚集。
  • Strategy:用一个装饰可以改变对象的外表;而Strategy模式使你可以改变对象的内核。这是改变对象的两种途经。
  1. 示例代码
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// Decorator.h 
#include <iostream>

#ifndef STRUCTURALPATTERNDESIGN_DECORATOR_H
#define STRUCTURALPATTERNDESIGN_DECORATOR_H

/**
* @brief 装饰模式
* 以GUI组件为例,定义可视化组件为基本的component类,文本框为具体的一个组件类,
* 边框和滚轮则是装饰器类
*/

void test_decorator();

// Component类
class VisualComponent {
public:
virtual void draw() = 0;
~VisualComponent() = default;
};

class TextLine : public VisualComponent {
public :
virtual void draw() {
std::cout << "画出一个文本框" << std::endl;

}
};

// 简单修饰器类
class Decorator : public VisualComponent{
public:
Decorator(VisualComponent* component) : component(component) {

}
virtual ~Decorator() = default;
virtual void draw() {
component->draw();
}

private:
VisualComponent* component;

};

// 边框修饰器
class BorderDecorator : public Decorator {
public:
BorderDecorator(VisualComponent* component, int width) : Decorator(component), width(width) {

}
virtual void draw() {
Decorator::draw();
add_border();
}

private:
int width;
void add_border() {
std::cout << "添加边框," << "厚度为" << width << std::endl;
}


};

// 滚动条修饰器
class ScrollDecorator : public Decorator {
public:
ScrollDecorator(VisualComponent* component) : Decorator(component) {

}

virtual void draw() {
Decorator::draw();
add_scroll();
}
private:
void add_scroll() {
std::cout << "添加滚动条" << std::endl;
}
};

// 窗口window,可以将一个可视化组件放入窗口中显示
class Window {
public:
void set_content(VisualComponent* component) {
component->draw();
}
};

#endif //STRUCTURALPATTERNDESIGN_DECORATOR_H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Decorator.cpp
#include <iostream>
#include "Decorator.h"

using namespace std;
void test_decorator() {
cout << "——————————开始测试装饰模式——————————" << endl;
Window window;

window.set_content(
new ScrollDecorator(
new BorderDecorator(
new TextLine, 3)));
cout << "——————————结束测试装饰模式——————————" << endl;
}

【设计模式】组合模式与装饰模式
https://2017zhangyuxuan.github.io/2022/03/20/2022-03/2022-03-20 【设计模式】组合模式与装饰模式/
作者
Zhang Yuxuan
发布于
2022年3月20日
许可协议