设计模式---数据结构模式之组合模式(Composite)
前提:数据结构模式
常常有一些组建在内部具有特定的数据结构,如果让客户程序依赖这些特定的数据结构,将极大的破坏组件的复用。这时候,将这些数据结构封装在内部,在外部提供统一的接口,来实现与特定数据结构无关的访问,是一种行之有效的解决方案。
典型模式
组合模式:Composite
迭代器模式:Iterator
责任链模式:Chain of Responsibility
一:组合模式
(一)概念
通过递归手段来构造树形对象结构并可以通过一个对象来访问整个对象
(二)动机
软件在某些情况下,客户代码过多地依赖于对象容器复杂的内部实现结构,对象容器内部实现结构(而非抽象接口)的变化将因其客户代码的频繁变化,带来了代码的维护性、扩展性等弊端。
如何将“客户代码与复杂的对象容器结构”解耦?让对象容器自己来实现自身的复杂结构,从而使得客户代码就像处理简单对象一样来处理复杂的对象容器?
(三)模式定义
将对象组合成树形结构以表示“部分-整体”的层级结构。Compisite使得用户对单个对象和组合对象的使用具有一致性 (稳定)。
——《设计模式》GoF
(四)代码讲解
1.抽象接口
class Component
{
public:
virtual void process() = ;
virtual ~Component(){}
};
2.两种类型之一:树形结构
//树节点
class Composite : public Component{ string name;
list<Component*> elements; //表达了一个树形结构
public:
Composite(const string & s) : name(s) {} void add(Component* element) {
elements.push_back(element);
}
void remove(Component* element){
elements.remove(element);
} void process(){
//1. process current node //2. process leaf nodes
for (auto &e : elements) //会出现多态递归调用,可能是树节点也可能是叶子节点
e->process(); //多态调用
}
};
2.两种类型之一:叶子节点
//叶子节点
class Leaf : public Component{
string name;
public:
Leaf(string s) : name(s) {} void process(){
//process current node
}
};
3.客户程序
void Invoke(Component & c){ //可以是树节点或者叶子节点
//...
c.process(); //多态调用
//...
}
4.实现构建树
int main()
{ Composite root("root");
Composite treeNode1("treeNode1");
Composite treeNode2("treeNode2");
Composite treeNode3("treeNode3");
Composite treeNode4("treeNode4");
Leaf leat1("left1");
Leaf leat2("left2"); root.add(&treeNode1);
treeNode1.add(&treeNode2);
treeNode2.add(&leaf1); root.add(&treeNode3);
treeNode3.add(&treeNode4);
treeNode4.add(&leaf2); Invoke(root);
Invoke(leaf2);
Invoke(treeNode3); }
问题:若是不用composite模式,如何实现?
//树节点
class Composite : public Component{ string name;
list<Component*> elements;
public:
Composite(const string & s) : name(s) {} void add(Component* element) {
elements.push_back(element);
}
void remove(Component* element){
elements.remove(element);
} void process(){ //1. process current node
}
}; //叶子节点
class Leaf : public Component{
string name;
public:
Leaf(string s) : name(s) {} void process(){
//process current node
}
};
会导致:
void Invoke(Component & c){
//...
c.process(); //这个地方的处理要做分别处理,进行判断类别,然后进行递归,依赖于对象容器内部的复杂结构
//...
}
(五)类图(结构)

类比文件系统:

(六)要点总结
(一)Composite模式采用树形结构来实现普遍存在的对象容器,从而将“一对多”的关系转化为“一对一”的关系,使得客户代码可以一致地(复用)处理对象和对象容器,无需关心处理的是单个对象还是组合的对象容器。
若是不用composite模式,我们在Invoke中需要进行多种判断类型,是一对多,但是我们使用了composite模式,我们只需要使用一个process即可,无需更新内部是什么,统一处理
(二)将“客户代码与复杂的对象容器结构”解耦是Composite的核心思想,解耦之后,客户代码将与纯粹的抽象接口——而非对象容器的内部实现结构——发生依赖,从而更能“应对变化”。
(三)Composite模式在具体实现中,可以让父对象中的子对象反向追溯;如果父对象有频繁的遍历需求,可使用缓存技巧来改善效率
(七)案例实现(文件列表)
1.接口实现
//抽象接口
class Component
{
public:
virtual void display() = ;
virtual ~Component(){}
};
2.树形结构:文件夹
//树形结构,文件夹
class Folder :public Component
{
private:
string f_name;
list<Component*> f_list;
public:
Folder(string name)
{
f_name = name;
} virtual void display()
{
cout << "d\t" << f_name << endl;
for (auto &f : f_list)
f->display();
} void Add(Component* comp)
{
f_list.push_back(comp);
} void Remove(Component* comp)
{
f_list.remove(comp);
} list<Component*> GetChild()
{
return f_list;
}
};
3.叶子节点:文件夹
//叶子节点,文件
class File :public Component
{
private:
string f_name;
public:
File(string name)
{
f_name = name;
} virtual void display()
{
cout << "f\t" << f_name << endl;
}
};
4.创建演示
void main()
{
Folder* root = new Folder("C:"); Folder* dir1 = new Folder("System32");
Folder* dir2 = new Folder("drivers");
Folder* dir3 = new Folder("etc"); File* file1 = new File("SAII_LOG.TXT");
File* file2 = new File("hosts"); root->Add(dir1);
dir1->Add(dir2);
dir1->Add(file1);
dir2->Add(dir3);
dir3->Add(file2); root->display();
cout << "==========================" << endl;
dir3->Remove(file2);
File* file3 = new File("config.ini");
dir3->Add(file3);
root->display(); delete dir1;
delete dir2;
delete dir3;
delete file1;
delete file2;
delete file3; system("pause");
return;
}

设计模式---数据结构模式之组合模式(Composite)的更多相关文章
- 设计模式(七)组合模式Composite(结构型)
设计模式(七)组合模式Composite(结构型) 1. 概述 在数据结构里面,树结构是很重要,我们可以把树的结构应用到设计模式里面. 例子1:就是多级树形菜单. 例子2:文件和文件夹目录 2.问题 ...
- 设计模式的征途—9.组合(Composite)模式
树形结构在软件中随处可见,比如操作系统中的目录结构,公司组织结构等等,如何运用面向对象的方式来处理这种树形结构是组合模式需要解决的问题.组合模式通过一种巧妙的设计方案来使得用户可以一致性地处理整个树形 ...
- Java设计模式(8)组合模式(Composite模式)
Composite定义:将对象以树形结构组织起来,以达成“部分-整体” 的层次结构,使得客户端对单个对象和组合对象的使用具有一致性. Composite比较容易理解,想到Composite就应该想到树 ...
- 迭代器模式和组合模式(head first设计模式——8)
把迭代器模式和组合模式放在同一篇的原因是其联系比较紧密. 一.迭代器模式 1.1迭代器模式定义 迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而不是暴露其内部的表示. 这个模式提供了一种方法 ...
- 设计模式GOF23(结构型模式:代理模式,适配模式,桥接模式,组合模式,装饰模式,外观模式,享元模式)
结构型模式: – 分类: • 适配器模式.代理模式.桥接模式.装饰模式.组合模式.外观模式.享元模式 – 核心作用:是从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题. 结构 ...
- 设计模式(十)——组合模式(HashMap源码解析)
1 看一个学校院系展示需求 编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院, 一个学院有多个系.如图: 2 传统方案解决学校院系展示 3 传统方案解决 ...
- js设计模式(六)---组合模式
组合模式将对象组合成树形结构,以表示“部分-整体”的层次结构.除了用来表示树形结构之外,组合模式的另一个好处是通过对象的多态性表现,使得用户对单个对象和组合对象的使用具有一致性.基本图例 1.组合模式 ...
- JS设计模式(7)组合模式
什么是组合模式? 定义:1.将对象组合成树形结构以表示"部分-整体"的层次结构.2.组合模式使得用户对单个对象和组合对象的使用具有一致性.3.无须关心对象有多少层,调用时只需在根部 ...
- Java计模模式之六 ----- 组合模式和过滤器模式
前言 在上一篇中我们学习了结构型模式的外观模式和装饰器模式.本篇则来学习下组合模式和过滤器模式. 组合模式 简介 组合模式是用于把一组相似的对象当作一个单一的对象.组合模式依据树形结构来组合对象,用来 ...
随机推荐
- ubuntu 14.04zabbix的安装
开始安装 64位 Ubuntu 14.04.5 LTS \n \l 安装zabbix的源,以下操作在root下进行 # wget http://repo.zabbix.com/zabbix/3.0/ ...
- MVC 动态菜单
直接上代码: 一,创建菜单 Action public ActionResult GetMenu() { //获取菜单 List<MenuItem> mainMenu = mm.GetMe ...
- Apache 跟踪用户会话
打开usertrack_module LoadModule usertrack_module modules/mod_usertrack.so 配置跟踪选项 <IfModule usertrac ...
- HNOI2019 游记
HNOI2019 游记 Day 0 其实考前几天,心里还是挺慌的.结果最后 Day 0 的时候,因为种种原因反而释然了.也许是觉得,在这一步退役,也没有什么好害怕的吧. OI 本身就是一项偶然性太大的 ...
- 从App业务逻辑中提炼API接口
2.1 从App业务逻辑中提炼API接口 业务逻辑思维导图 功能-业务逻辑思维导图 基本功能模块关系 功能模块接口UML(设计出API) 在设计稿标注API 编写API文档 2.2 设计API的要点 ...
- linux中文件的时间戳
touch命令 touch用来创建新文件,或者修改时间戳. linux中的文件有三个时间点,利用stat命令查看: 1.atime:最后访问时间:文件最后一次被存取或执行的时间. 2.mtime:最后 ...
- [2017-7-28]Android Learning Day6
常用控件 Spinner DatePickerDialog TimePickerDiaog RadioButton CheckBox spinner(下拉菜单) <Spinner android ...
- cf455C Civilization (并查集)
并查集维护每个联通块的直径和最小的最大深度,每次连得时候连的肯定是最大深度最小的那两个点 #pragma GCC optimize(3) #include<bits/stdc++.h> # ...
- luogu1919 A*BProblem升级版 (FFT)
把一个n位数看做n-1次的多项式,每一项的系数是反过来的每一位最后每一项系数进进位搞一搞就行了(数组一定要开到2的次数..要不然极端数据会RE) #include<cstdio> #inc ...
- [NOI2016]优秀的拆分&&BZOJ2119股市的预测
[NOI2016]优秀的拆分 https://www.lydsy.com/JudgeOnline/problem.php?id=4650 题解 如果我们能够统计出一个数组a,一个数组b,a[i]表示以 ...