53 | 组合模式:如何设计实现支持递归遍历的文件系统目录树结构?
王争
该思维导图由 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
《设计模式之美》,新⼈⾸单¥98
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(61)
- 最新
- 精选
- 墨鱼组合模式: 当数据结构呈现树状,可以使用递归处理每一处节点的数据。 感觉名字有点迷惑,应该叫做树状模式
作者回复: 你想叫它啥就叫它啥,反正道理你懂就行
2020-04-181 - Laughing把子数抽象成一类对象,并且增加数量等属性,这样就避免了多次的迭代。
作者回复: 嗯嗯 说的没错
2020-11-20 - 下雨天课堂讨论: 实质是"递归代码要警惕重复计算"问题!可以用散列表存储每个(path,size),通过路径直接返回对应的size,删除或者添加的时候,维护这个size即可。 参看争哥《数据结构与算法之美》第十讲:为了避免重复计算,我们可以通过一个数据结构(比如散列表)来保存已经求解过的 f(k)。当递归调用到 f(k) 时,先看下是否已经求解过了。如果是,则直接从散列表中取值返回,不需要重复计算,这样就能避免刚讲的问题了。2020-03-067131
- 八戒课堂讨论 可以把计算文件数量和大小的逻辑抽出来,定义两个成员变量文件大小和文件数量; 在每次addSubNode()和removeSubNode()的时候去调用计算逻辑,更新文件大小和文件数量; 这样在调用countNumOfFiles和countSizeOfFiles的时候直接返回我们的成员变量就好了; 当然如果这么做的话,那countNumOfFiles和countSizeOfFiles这两个方法的名字也不合适了,应该叫numOfFiles和sizeOfFiles2020-03-04259
- 业余爱好者tomcat的多层容器也是使用了组合模式。只需要调用最外层容器Server的init方法,整个程序就起来了。客户端只需要处理最外层的容器,就把整个系统拎起来了。 组合模式使用了树形的数据结构以及递归算法,这里也可以看出知识的相通性(算法和设计模式)。想到这方面的另外一个例子就是责任链模式,责任链模式就是使用了数据结构中的链表和递归算法。2020-03-2547
- test把计算逻辑放在addSubNode和removeSubNode里面2020-03-0425
- 辣么大我想的一个思路是:每个节点新增一个field:parent,父链接指向它的上层节点,同时增加字段numOfFiles,sizeOfFiles。对于File节点:numOfFiles=1, sizeOfFiles=它自己的大小。对于Directory节点,是其子节点的和。删除、增加subnode时,只需要从下向上遍历一个节点的parent link,修改numOfFiles和sizeOfFiles。这样的话删除、新增subnode修改值的复杂度为树的深度,查询返回numOfFiles和sizeOfFiles复杂度为O(1)。2020-03-04620
- 南山真的是没有最适合,只有更适合 实际工作中碰到过一个场景需要抽象出条件和表达式来解决的。一个表达式可以拥有N个子表达式以及条件,这个表达式还有一个属性and、or来决定所有子表达式/条件是全部成立还是只要有一个成立,这个表达式就成立。 当时做的时候真是各种绕,这种场景真的非常适合组合模式,能大大简化代码的实现难度,提高可读、可维护性2020-03-04416
- 李小四设计模式_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-1513
- 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-0546
收起评论