树形结构在软件中随处可见,例如操做系统中的目录结构、应用软件中的菜单、办公系统中的公司组织结构等等,如何运用面向对象的方式来处理这种树形结构是组合模式须要解决的问题,组合模式经过一种巧妙的设计方案使得用户能够一致性地处理整个树形结构或者树形结构的一部分,也能够一致性地处理树形结构中的叶子节点(不包含子节点的节点)和容器节点(包含子节点的节点)。下面将学习这种用于处理树形结构的组合模式。
11.1 设计杀毒软件的框架结构
Sunny软件公司欲开发一个杀毒(AntiVirus)软件,该软件既能够对某个文件夹(Folder)杀毒,也能够对某个指定的文件(File)进行杀毒。该杀毒软件还能够根据各种文件的特色,为不一样类型的文件提供不一样的杀毒方式,例如图像文件(ImageFile)和文本文件(TextFile)的杀毒方式就有所差别。现须要提供该杀毒软件的总体框架设计方案。java |
在介绍Sunny公司开发人员提出的初始解决方案以前,咱们先来分析一下操做系统中的文件目录结构,例如在Windows操做系统中,存在如图11-1所示目录结构:框架

图11-1 Windows目录结构ide
图11-1能够简化为如图11-2所示树形目录结构:学习

图11-2 树形目录结构示意图测试
咱们能够看出,在图11-2中包含文件(灰色节点)和文件夹(白色节点)两类不一样的元素,其中在文件夹中能够包含文件,还能够继续包含子文件夹,可是在文件中不能再包含子文件或者子文件夹。在此,咱们能够称文件夹为容器(Container),而不一样类型的各类文件是其成员,也称为叶子(Leaf),一个文件夹也能够做为另外一个更大的文件夹的成员。若是咱们如今要对某一个文件夹进行操做,如查找文件,那么须要对指定的文件夹进行遍历,若是存在子文件夹则打开其子文件夹继续遍历,若是是文件则判断以后返回查找结果。this
Sunny软件公司的开发人员经过分析,决定使用面向对象的方式来实现对文件和文件夹的操做,定义了以下图像文件类ImageFile、文本文件类TextFile和文件夹类Folder:spa
- //为了突出核心框架代码,咱们对杀毒过程的实现进行了大量简化
- import java.util.*;
-
- //图像文件类
- class ImageFile {
- private String name;
-
- public ImageFile(String name) {
- this.name = name;
- }
-
- public void killVirus() {
- //简化代码,模拟杀毒
- System.out.println("----对图像文件'" + name + "'进行杀毒");
- }
- }
-
- //文本文件类
- class TextFile {
- private String name;
-
- public TextFile(String name) {
- this.name = name;
- }
-
- public void killVirus() {
- //简化代码,模拟杀毒
- System.out.println("----对文本文件'" + name + "'进行杀毒");
- }
- }
-
- //文件夹类
- class Folder {
- private String name;
- //定义集合folderList,用于存储Folder类型的成员
- private ArrayList folderList = new ArrayList();
-
- //定义集合imageList,用于存储ImageFile类型的成员
- private ArrayList imageList = new ArrayList();
-
- //定义集合textList,用于存储TextFile类型的成员
- private ArrayList textList = new ArrayList();
-
- public Folder(String name) {
- this.name = name;
- }
-
- //增长新的Folder类型的成员
- public void addFolder(Folder f) {
- folderList.add(f);
- }
-
- //增长新的ImageFile类型的成员
- public void addImageFile(ImageFile image) {
- imageList.add(image);
- }
-
- //增长新的TextFile类型的成员
- public void addTextFile(TextFile text) {
- textList.add(text);
- }
-
- //需提供三个不一样的方法removeFolder()、removeImageFile()和removeTextFile()来删除成员,代码省略
-
- //需提供三个不一样的方法getChildFolder(int i)、getChildImageFile(int i)和getChildTextFile(int i)来获取成员,代码省略
-
- public void killVirus() {
- System.out.println("****对文件夹'" + name + "'进行杀毒"); //模拟杀毒
-
- //若是是Folder类型的成员,递归调用Folder的killVirus()方法
- for(Object obj : folderList) {
- ((Folder)obj).killVirus();
- }
-
- //若是是ImageFile类型的成员,调用ImageFile的killVirus()方法
- for(Object obj : imageList) {
- ((ImageFile)obj).killVirus();
- }
-
- //若是是TextFile类型的成员,调用TextFile的killVirus()方法
- for(Object obj : textList) {
- ((TextFile)obj).killVirus();
- }
- }
- }
编写以下客户端测试代码进行测试:操作系统
- class Client {
- public static void main(String args[]) {
- Folder folder1,folder2,folder3;
- folder1 = new Folder("Sunny的资料");
- folder2 = new Folder("图像文件");
- folder3 = new Folder("文本文件");
-
- ImageFile image1,image2;
- image1 = new ImageFile("小龙女.jpg");
- image2 = new ImageFile("张无忌.gif");
-
- TextFile text1,text2;
- text1 = new TextFile("九阴真经.txt");
- text2 = new TextFile("葵花宝典.doc");
-
- folder2.addImageFile(image1);
- folder2.addImageFile(image2);
- folder3.addTextFile(text1);
- folder3.addTextFile(text2);
- folder1.addFolder(folder2);
- folder1.addFolder(folder3);
-
- folder1.killVirus();
- }
- }
编译并运行程序,输出结果以下:.net
****对文件夹'Sunny的资料'进行杀毒设计 ****对文件夹'图像文件'进行杀毒 ----对图像文件'小龙女.jpg'进行杀毒 ----对图像文件'张无忌.gif'进行杀毒 ****对文件夹'文本文件'进行杀毒 ----对文本文件'九阴真经.txt'进行杀毒 ----对文本文件'葵花宝典.doc'进行杀毒 |
Sunny公司开发人员“成功”实现了杀毒软件的框架设计,但经过仔细分析,发现该设计方案存在以下问题:
(1) 文件夹类Folder的设计和实现都很是复杂,须要定义多个集合存储不一样类型的成员,并且须要针对不一样的成员提供增长、删除和获取等管理和访问成员的方法,存在大量的冗余代码,系统维护较为困难;
(2) 因为系统没有提供抽象层,客户端代码必须有区别地对待充当容器的文件夹Folder和充当叶子的ImageFile和TextFile,没法统一对它们进行处理;
(3) 系统的灵活性和可扩展性差,若是须要增长新的类型的叶子和容器都须要对原有代码进行修改,例如若是须要在系统中增长一种新类型的视频文件VideoFile,则必须修改Folder类的源代码,不然没法在文件夹中添加视频文件。
面对以上问题,Sunny软件公司的开发人员该如何来解决?这就须要用到本章将要介绍的组合模式,组合模式为处理树形结构提供了一种较为完美的解决方案,它描述了如何将容器和叶子进行递归组合,使得用户在使用时无须对它们进行区分,能够一致地对待容器和叶子。
【做者:刘伟 http://blog.csdn.net/lovelion】