完整解决方案
为了让系统具有更好的灵活性和可扩展性,客户端可以一致地对待文件和文件夹,Sunny公司开发人员使用组合模式来进行杀毒软件的框架设计,其基本结构如图所示:
以上是杀毒软件框架设计结构图
在图中, AbstractFile充当抽象构件类,Folder充当容器构件类,ImageFile、TextFile和VideoFile充当叶子构件类。完整代码如下所示:
//抽象文件类:抽象构件 abstract class AbstractFile { public abstract void add(AbstractFile file); public abstract void remove(AbstractFile file); public abstract AbstractFile getChild(int i); public abstract void killVirus(); }
//图像文件类:叶子构件 class ImageFile extends AbstractFile { private String name; public ImageFile(String name) { this.name = name; } public void add(AbstractFile file) { System.out.println("对不起,不支持该方法!"); } public void remove(AbstractFile file) { System.out.println("对不起,不支持该方法!"); } public AbstractFile getChild(int i) { System.out.println("对不起,不支持该方法!"); return null; } public void killVirus() { //模拟杀毒 System.out.println("----对图像文件'" + name + "'进行杀毒"); } }
//文本文件类:叶子构件 class TextFile extends AbstractFile { private String name; public TextFile(String name) { this.name = name; } public void add(AbstractFile file) { System.out.println("对不起,不支持该方法!"); } public void remove(AbstractFile file) { System.out.println("对不起,不支持该方法!"); } public AbstractFile getChild(int i) { System.out.println("对不起,不支持该方法!"); return null; } public void killVirus() { //模拟杀毒 System.out.println("----对文本文件'" + name + "'进行杀毒"); } }
//视频文件类:叶子构件 class VideoFile extends AbstractFile { private String name; public VideoFile(String name) { this.name = name; } public void add(AbstractFile file) { System.out.println("对不起,不支持该方法!"); } public void remove(AbstractFile file) { System.out.println("对不起,不支持该方法!"); } public AbstractFile getChild(int i) { System.out.println("对不起,不支持该方法!"); return null; } public void killVirus() { //模拟杀毒 System.out.println("----对视频文件'" + name + "'进行杀毒"); } }
//文件夹类:容器构件 class Folder extends AbstractFile { //定义集合fileList,用于存储AbstractFile类型的成员 private ArrayList<AbstractFile> fileList = new ArrayList<AbstractFile>(); private String name; public Folder(String name) { this.name = name; } public void add(AbstractFile file) { fileList.add(file); } public void remove(AbstractFile file) { fileList.remove(file); } public AbstractFile getChild(int i) { return (AbstractFile) fileList.get(i); } public void killVirus() { System.out.println("****对文件夹'" + name + "'进行杀毒"); //模拟杀毒软件进行杀毒
//递归调用成员构件的killVirus()方法 for (Object obj : fileList) { ((AbstractFile) obj).killVirus(); } } }
编写如下客户端测试代码:
class Client { public static void main(String args[]) { //针对抽象构件编程 AbstractFile file1, file2, file3, file4, file5, folder1, folder2, folder3,folder4; folder1 = new Folder("Sunny的资料"); folder2 = new Folder(" 图 像 文 件 "); folder3 = new Folder(" 文 本 文 件 "); folder4 = new Folder("视频文件"); file1 = new ImageFile(" 小 龙 女 .jpg"); file2 = new ImageFile(" 张 无 忌 .gif"); file3 = new TextFile("九阴真经.txt"); file4 = new TextFile("葵花宝典.doc"); file5 = new VideoFile("笑傲江湖.rmvb"); folder2.add(file1); folder2.add(file2); folder3.add(file3); folder3.add(file4); folder4.add(file5); folder1.add(folder2); folder1.add(folder3); folder1.add(folder4); //从“Sunny的资料”节点开始进行杀毒操作 folder1.killVirus(); } }
编译并运行程序,输出结果如下:
****对文件夹'Sunny的资料'进行杀毒 ****对文件夹'图像文件'进行杀毒 ----对图像文件'小龙女.jpg'进行杀毒 ----对图像文件'张无忌.gif'进行杀毒 ****对文件夹'文本文件'进行杀毒 ----对文本文件'九阴真经.txt'进行杀毒 ----对文本文件'葵花宝典.doc'进行杀毒 ****对文件夹'视频文件'进行杀毒 ----对视频文件'笑傲江湖.rmvb'进行杀毒
由于在本实例中使用了组合模式,在抽象构件类中声明了所有方法,包括用于管理和访问子构件的方法,如add()方法和remove()方法等,因此在ImageFile等叶子构件类中实现这些方法时必须进行相应的异常处理或错误提示。在容器构件类Folder的killVirus()方法中将递归调用其成员对象的killVirus()方法,从而实现对整个树形结构的遍历。
组合模式总结
组合模式的主要优点如下:
(1) 组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
(2) 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。
(3) 在组合模式中增加新的容器构件和叶子构件都很方便,无须对现有类库进行任何修改,符合“开闭原则”。
(4) 组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子对象和容器对象的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。
组合模式的主要缺点如下:
在增加新构件时很难对容器中的构件类型进行限制。有时候我们希望一个容器中只能有某些特定类型的对象,例如在某个文件夹中只能包含文本文件,使用组合模式时,不能依赖类型系统来施加这些约束,因为它们都来自于相同的抽象层,在这种情况下,必须通过在运行时进行类型检查来实现,这个实现过程较为复杂。
在以下情况下可以考虑使用组合模式:
(1) 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待它们。
(2) 在一个使用面向对象语言开发的系统中需要处理一个树形结构。
(3) 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型。