设计模式之美
王争
前 Google 工程师,《数据结构与算法之美》专栏作者
123425 人已学习
新⼈⾸单¥98
登录后,你可以任选6讲全文学习
课程目录
已完结/共 113 讲
设计模式与范式:行为型 (18讲)
设计模式之美
15
15
1.0x
00:00/00:00
登录|注册

53 | 组合模式:如何设计实现支持递归遍历的文件系统目录树结构?

构建组织架构的代码
文件系统目录树结构示例
重构代码
核心逻辑
提高函数执行效率的方法
应用场景限制
组合模式特点
设计思路
OA系统组织架构
例子:文件系统目录
定义
课堂讨论
重点回顾
应用场景举例
原理与实现
组合模式

该思维导图由 AI 生成,仅供参考

结构型设计模式就快要讲完了,还剩下两个不那么常用的:组合模式和享元模式。今天,我们来讲一下组合模式(Composite Design Pattern)。
组合模式跟我们之前讲的面向对象设计中的“组合关系(通过组合来组装两个类)”,完全是两码事。这里讲的“组合模式”,主要是用来处理树形结构数据。这里的“数据”,你可以简单理解为一组对象集合,待会我们会详细讲解。
正因为其应用场景的特殊性,数据必须能表示成树形结构,这也导致了这种模式在实际的项目开发中并不那么常用。但是,一旦数据满足树形结构,应用这种模式就能发挥很大的作用,能让代码变得非常简洁。
话不多说,让我们正式开始今天的学习吧!

组合模式的原理与实现

在 GoF 的《设计模式》一书中,组合模式是这样定义的:
Compose objects into tree structure to represent part-whole hierarchies.Composite lets client treat individual objects and compositions of objects uniformly.
翻译成中文就是:将一组对象组织(Compose)成树形结构,以表示一种“部分 - 整体”的层次结构。组合让客户端(在很多设计模式书籍中,“客户端”代指代码的使用者。)可以统一单个对象和组合对象的处理逻辑。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入介绍了组合模式的原理与实现,重点讲解了如何设计实现支持递归遍历的文件系统目录树结构。组合模式是一种结构型设计模式,能够将一组对象组织成树形结构,表示“部分-整体”的层次结构。通过组合模式,客户端可以统一处理单个对象和组合对象的逻辑。文章以文件系统目录为例,演示了如何使用组合模式设计文件和目录类,并通过递归遍历算法实现统计文件个数和大小的功能。此外,还举了OA系统中部门和员工的例子,展示了组合模式在实际项目中的应用。总的来说,本文通过具体案例深入浅出地介绍了组合模式的原理和实现,对于理解和应用组合模式具有一定的参考价值。文章还提出了在文件系统例子中优化countNumOfFiles()和countSizeOfFiles()函数执行效率的问题,鼓励读者分享他们的想法。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《设计模式之美》
新⼈⾸单¥98
立即购买
登录 后留言

全部留言(61)

  • 最新
  • 精选
  • 墨鱼
    组合模式: 当数据结构呈现树状,可以使用递归处理每一处节点的数据。 感觉名字有点迷惑,应该叫做树状模式

    作者回复: 你想叫它啥就叫它啥,反正道理你懂就行

    2020-04-18
    1
  • Laughing
    把子数抽象成一类对象,并且增加数量等属性,这样就避免了多次的迭代。

    作者回复: 嗯嗯 说的没错

    2020-11-20
  • 下雨天
    课堂讨论: 实质是"递归代码要警惕重复计算"问题!可以用散列表存储每个(path,size),通过路径直接返回对应的size,删除或者添加的时候,维护这个size即可。 参看争哥《数据结构与算法之美》第十讲:为了避免重复计算,我们可以通过一个数据结构(比如散列表)来保存已经求解过的 f(k)。当递归调用到 f(k) 时,先看下是否已经求解过了。如果是,则直接从散列表中取值返回,不需要重复计算,这样就能避免刚讲的问题了。
    2020-03-06
    7
    131
  • 八戒
    课堂讨论 可以把计算文件数量和大小的逻辑抽出来,定义两个成员变量文件大小和文件数量; 在每次addSubNode()和removeSubNode()的时候去调用计算逻辑,更新文件大小和文件数量; 这样在调用countNumOfFiles和countSizeOfFiles的时候直接返回我们的成员变量就好了; 当然如果这么做的话,那countNumOfFiles和countSizeOfFiles这两个方法的名字也不合适了,应该叫numOfFiles和sizeOfFiles
    2020-03-04
    2
    59
  • 业余爱好者
    tomcat的多层容器也是使用了组合模式。只需要调用最外层容器Server的init方法,整个程序就起来了。客户端只需要处理最外层的容器,就把整个系统拎起来了。 组合模式使用了树形的数据结构以及递归算法,这里也可以看出知识的相通性(算法和设计模式)。想到这方面的另外一个例子就是责任链模式,责任链模式就是使用了数据结构中的链表和递归算法。
    2020-03-25
    47
  • test
    把计算逻辑放在addSubNode和removeSubNode里面
    2020-03-04
    25
  • 辣么大
    我想的一个思路是:每个节点新增一个field:parent,父链接指向它的上层节点,同时增加字段numOfFiles,sizeOfFiles。对于File节点:numOfFiles=1, sizeOfFiles=它自己的大小。对于Directory节点,是其子节点的和。删除、增加subnode时,只需要从下向上遍历一个节点的parent link,修改numOfFiles和sizeOfFiles。这样的话删除、新增subnode修改值的复杂度为树的深度,查询返回numOfFiles和sizeOfFiles复杂度为O(1)。
    2020-03-04
    6
    20
  • 南山
    真的是没有最适合,只有更适合 实际工作中碰到过一个场景需要抽象出条件和表达式来解决的。一个表达式可以拥有N个子表达式以及条件,这个表达式还有一个属性and、or来决定所有子表达式/条件是全部成立还是只要有一个成立,这个表达式就成立。 当时做的时候真是各种绕,这种场景真的非常适合组合模式,能大大简化代码的实现难度,提高可读、可维护性
    2020-03-04
    4
    16
  • 李小四
    设计模式_53: # 作业 可以做文件数和文件大小的缓存,更新缓存时要考虑实时性与性能的平衡。 # 感想 今天的内容,联想到Linux“一切皆文件”的设计思想。 好像天然就觉得应该这样做,但是,还能怎么做呢? 还能。。。把Directory和File设计成不想关的两个类,这样又有什么问题呢? 不过是Directory维护两个List(file/directory),维护两套方法,add/removeFile,add/removeDirectory。。。这当然没有以前简洁,但也没有特别复杂吧。。。 后面又想到,如果File还分很多类型: TxtFile/ImageFile/ExeFile/...,Directory(这里是广义的集合)也可以有多种: LinearDirectory/GridDirectory/CircleDirectory/... 这样会不会导致处理逻辑的爆炸,你会说:当然不会啊,所有的类型最终会抽象为File和Directory两种类型啊! 既然都抽象了,何不彻底一点,把File和Directory也抽象为一种类型: Everything is a File.
    2020-03-15
    13
  • webmin
    //每一级目录保存本级目录中的文件数和文件Size,Count时递归统计所有子目录 public class Directory extends FileSystemNode { private List<FileSystemNode> subNodes = new ArrayList<>(); private Map<String,FileSystemNode> subDirectory = new HashMap<>(); private int _numOfFiles = 0; private long _sizeofFiles = 0; public Directory(String path) { super(path); } @Override public int countNumOfFiles() { int numOfFiles = 0; for (FileSystemNode fileOrDir : subDirectory.values()) { numOfFiles += fileOrDir.countNumOfFiles(); } return numOfFiles + _numOfFiles; } @Override public long countSizeOfFiles() { long sizeofFiles = 0; for (FileSystemNode fileOrDir : subDirectory.values()) { sizeofFiles += fileOrDir.countSizeOfFiles(); } return sizeofFiles + _sizeofFiles; } public void addSubNode(FileSystemNode fileOrDir) { if(fileOrDir instanceof Directory) { subDirectory.put(fileOrDir.getPath(),fileOrDir); } else { _numOfFiles++; _sizeofFiles += fileOrDir.countSizeOfFiles(); subNodes.add(fileOrDir); } } public void removeSubNode(FileSystemNode fileOrDir) { if(fileOrDir instanceof Directory) { subDirectory.remove(fileOrDir.getPath()); return; } int size = subNodes.size(); int i = 0; for (; i < size; ++i) { if (subNodes.get(i).getPath().equalsIgnoreCase(fileOrDir.getPath())) { break; } } if (i < size) { subNodes.remove(i); _numOfFiles--; _sizeofFiles -= fileOrDir.countSizeOfFiles(); } } }
    2020-03-05
    4
    6
收起评论
显示
设置
留言
61
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部