为不一致品类的文书提供不一致的杀毒方式,组合格局又足以叫做

读书难度:★★★☆☆,使用频率:★★★★☆】 

【学习难度:★★★☆☆,使用成效:★★★★☆】
直接出处:http://woquanke.com/books/gof/
梳理和上学:https://github.com/BruceOuyang/boy-design-pattern
简书日期: 2018/03/12
简书首页:https://www.jianshu.com/p/0fb891a7c5ed

定义

  组合形式(Composite
Pattern):组合五个目的形成树形结构以代表拥有“整体—部分”关系的层次结构。组合情势对单个对象(即叶子对象)和构成对象(即容器对象)的施用全部一致性,组合方式又可以称为“全体—部分”(Part-Whole)方式,它是一种对象结构型模式。

树形结构在软件中各处可知,例如操作系统中的目录结构、应用软件中的菜单、办公系统中的公司集体结构等等,怎么着行使面向对象的法门来拍卖那种树形结构是构成方式需求缓解的题材,组合情势通过一种高超的设计方案使得用户可以一致性地处理整个树形结构如故树形结构的一片段,也得以一致性地拍卖树形结构中的叶子节点(不包罗子节点的节点)和容器节点(包涵子节点的节点)。上边将学习这种用于拍卖树形结构的组合方式。

树形结构的处理——组合格局(一)

树形结构在软件中随地可知,例如操作系统中的目录结构、应用软件中的菜单、办公系统中的公司团队结构等等,怎么着接纳面向对象的艺术来拍卖那种树形结构是组成情势须要缓解的难题,组合情势通过一种高超的设计方案使得用户可以一致性地处理任何树形结构依然树形结构的一片段,也能够一致性地处理树形结构中的叶子节点(不包括子节点的节点)和容器节点(包括子节点的节点)。上面将学习那种用于拍卖树形结构的重组方式。

11.1 设计杀毒软件的框架结构

Sunny软件商店欲开发一个杀毒(AntiVirus)软件,该软件既可以对某些文件夹(Folder)杀毒,也得以对有个别钦定的公文(File)举行杀毒。该杀毒软件还能按照各个文件的风味,为差异品种的文本提供差其余杀毒格局,例如图像文件(ImageFile)和文书文件(TextFile)的杀毒格局就拥有出入。现须求提供该杀毒软件的完好框架设计方案。

在介绍Sunny公司开发人士指出的初步化解方案此前,大家先来分析一下操作系统中的文件目录结构,例如在Windows操作系统中,存在如图11-1所示目录结构:

图片 1

图11-1 Windows目录结构

图11-1方可简化为如图11-2所示树形目录结构:

图片 2

图11-2 树形目录结构示意图

我们可以看看,在图11-2中蕴藏文件(深黑节点)和文书夹(深青莲节点)两类不一样的元素,其中在文书夹中可以包涵文件,还是可以够三番五次包涵子文件夹,但是在文书中不可以再包括子文件恐怕子文件夹。在此,咱们得以称文件夹为容器(Container),而不同类其余各类文件是其成员,也叫做叶子(Leaf),三个文件夹也得以看做另多少个更大的文本夹的积极分子。倘使大家今天要对某多少个文件夹举行操作,如查找文件,那么须要对点名的文书夹举办遍历,若是存在子文件夹则打开其子文件夹继续遍历,假诺是文件则判断之后回来寻找结果。

萨妮软件公司的开发人员通过分析,决定采纳面向对象的艺术来贯彻对文本和文书夹的操作,定义了如下图像文件类ImageFile、文本文件类TextFile和文书夹类Folder:

//为了突出核心框架代码,我们对杀毒过程的实现进行了大量简化 
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<Folder> folderList = new ArrayList<Folder>(); 
//定义集合imageList,用于存储ImageFile类型的成员 
private ArrayList<ImageFile> imageList = new ArrayList<ImageFile>(); 
//定义集合textList,用于存储TextFile类型的成员 
private ArrayList<TextFile> textList = new ArrayList<TextFile>(); 

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(); 
} 
}

编译并运维程序,输出结果如下:

****对文件夹'Sunny的资料'进行杀毒
****对文件夹'图像文件'进行杀毒
----对图像文件'小龙女.jpg'进行杀毒
----对图像文件'张无忌.gif'进行杀毒
****对文件夹'文本文件'进行杀毒
----对文本文件'九阴真经.txt'进行杀毒
----对文本文件'葵花宝典.doc'进行杀毒

萨妮集团开发人员“成功”已毕了杀毒软件的框架设计,但由此仔细分析,发现该设计方案存在如下难题:

(1)
文件夹类Folder的布置和贯彻都分外复杂,须求定义几个汇集存储不相同品类的分子,而且亟需针对分化的积极分子提供伸张、删除和取得等管理和访问成员的方法,存在多量的冗余代码,系统爱护相比困难;

(2)
由于系统尚未提供抽象层,客户端代码必须有分别地对待充当容器的公文夹Folder和担任叶子的ImageFile和TextFile,无法统一对它们进行处理;

(3)
系统的油滑和可扩张性差,假若急需充实新的花色的纸牌和容器都须要对原有代码进行改动,例如若是急需在系统中追加一种新品类的录制文件VideoFile,则必须修改Folder类的源代码,否则不可以在文件夹中添加视频文件。

面对以上难题,Sunny软件公司的开发人士该如何来化解?那就须要用到本章将要介绍的组成形式,组合形式为处理树形结构提供了一种比较完美的缓解方案,它描述了何等将容器和叶子举行递归组合,使得用户在行使时毫不对它们举办区分,可以一如既往地对待容器和叶子。

结构图

图片 3

要素:

Component(抽象构件):它可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中得以包涵全体子类共有行为的宣示和贯彻。在空虚构件中定义了拜访及管制它的子构件的点子,如增添子构件、删除子构件、获取子构件等。

Leaf(叶子构件):它在结合结构中表示叶子节点目的,叶子节点没有子节点,它落成了在空洞构件中定义的行为。对于那多少个访问及管理子构件的点子,可以经过至极等方法展开拍卖。

Composite(容器构件):它在组成结构中象征容器节点目标,容器节点包涵子节点,其子节点可以是纸牌节点,也可以是容器节点,它提供1个凑合用于存储子节点,已毕了在空虚构件中定义的行事,包蕴那些访问及管理子构件的章程,在其工作方法中可以递归调用其子节点的业务方法。
整合形式的主要:概念了3个架空构件类,它既可以表示叶子,又足以象征容器,而客户端针对该抽象构件类举办编程,无须知道它到底意味着的是纸牌依然容器,可以对其进展联合处理

abstract class Component {  
    public abstract void add(Component c); //增加成员  
    public abstract void remove(Component c); //删除成员  
    public abstract Component getChild(int i); //获取成员  
    public abstract void operation();  //业务方法  
}  

class Leaf extends Component {  
    public void add(Component c) {   
        //异常处理或错误提示   
    }     

    public void remove(Component c) {   
        //异常处理或错误提示   
    }  

    public Component getChild(int i) {   
        //异常处理或错误提示  
        return null;   
    }  

    public void operation() {  
        //叶子构件具体业务方法的实现  
    }   
}  

class Composite extends Component {  
    private ArrayList<Component> list = new ArrayList<Component>();  

    public void add(Component c) {  
        list.add(c);  
    }  

    public void remove(Component c) {  
        list.remove(c);  
    }  

    public Component getChild(int i) {  
        return (Component)list.get(i);  
    }  

    public void operation() {  
        //容器构件具体业务方法的实现  
        //递归调用成员构件的业务方法  
        for(Object obj:list) {  
            ((Component)obj).operation();  
        }  
    }     
}  

 

树形结构的拍卖——组合情势(二)

11.2 组合情势概述

对此树形结构,当容器对象(如文件夹)的某一个艺术被调用时,将遍历整个树形结构,寻找也隐含这一个主意的成员对象(可以是容器对象,也得以是纸牌对象)并调用执行,牵一而动百,其中使用了递归调用的机制来对一切结构进行拍卖。由于容器对象和叶子对象在职能上的分别,在利用这一个目的的代码中务必有分别地对待容器对象和叶子对象,而实际半数以上动静下大家期待一致地拍卖它们,因为对于那些目的的界别对待将会使得程序万分复杂。组合情势为消除此类难点而诞生,它可以让叶子对象和容器对象的利用具有一致性。

构成情势定义如下:

结合方式(Composite
帕特tern):组合多个对象形成树形结构以象征全部“全体—部分”关系的层次结构。组合格局对单个对象(即叶子对象)和烧结对象(即容器对象)的使用全体一致性,组合格局又有什么不可叫做“全体—部分”(Part-Whole)形式,它是一种对象结构型格局。

在组合格局中引入了抽象构件类Component,它是全体容器类和叶子类的公家父类,客户端针对Component举行编程。组合格局社团如图11-3所示:

图片 4

图11-3 组合形式结构图

在结合形式结构图中带有如下多少个角色:

  • Component(抽象构件):它可以是接口或抽象类,为叶子构件和容器构件对象申明接口,在该角色中可以分包全体子类共有行为的注明和促成。在抽象构件中定义了访问及保管它的子构件的章程,如扩大子构件、删除子构件、获取子构件等。

  • Leaf(叶子构件):它在组成结构中象征叶子节点目的,叶子节点没有子节点,它达成了在空虚构件中定义的表现。对于那一个访问及管理子构件的主意,可以经过丰裕等办法展开拍卖。

  • Composite(容器构件):它在整合结构中代表容器节点目的,容器节点蕴含子节点,其子节点可以是纸牌节点,也足以是容器节点,它提供贰个凑合用于存储子节点,达成了在虚幻构件中定义的一坐一起,包括那么些访问及管理子构件的措施,在其工作方法中可以递归调用其子节点的工作方法。

重组形式的基本点是概念了二个虚无构件类,它既可以象征叶子,又足以象征容器,而客户端针对该抽象构件类进行编程,无须知道它到底意味着的是纸牌依然容器,可以对其开展统一处理。同时容器对象与画饼充饥构件类之间还建立1个会师关联关系,在容器对象中既可以涵盖叶子,也足以涵盖容器,以此完结递归组合,形成二个树形结构。

假诺不选用组合方式,客户端代码将过多地依靠于器皿对象复杂的中间贯彻社团,容器对象内部贯彻结构的生成将唤起客户代码的再三变更,带来了代码维护复杂、可增加性差等弊病。组合方式的引入将在必然水平上消除那个标题。

下边通过不难的示范代码来分析组合情势的一一角色的用途和贯彻。对于构成情势中的抽象构件角色,其独立代码如下所示:

abstract class Component {  
    public abstract void add(Component c); //增加成员  
    public abstract void remove(Component c); //删除成员  
    public abstract Component getChild(int i); //获取成员  
    public abstract void operation();  //业务方法  
}

相似将抽象构件类设计为接口或抽象类,将享有子类共有方法的扬言和兑现放在抽象构件类中。对于客户端而言,将本着抽象构件编程,而无须关心其实际子类是容器构件还是叶子构件。

若果继续抽象构件的是纸牌构件,则其一流代码如下所示:

class Leaf extends Component {  
    public void add(Component c) {   
        //异常处理或错误提示   
    }     

    public void remove(Component c) {   
        //异常处理或错误提示   
    }  

    public Component getChild(int i) {   
        //异常处理或错误提示  
        return null;   
    }  

    public void operation() {  
        //叶子构件具体业务方法的实现  
    }   
}

用作抽象构件类的子类,在叶子构件中须求实以后空虚构件类中宣称的保有办法,包罗工作方法以及管理和访问子构件的格局,不过叶子构件不可以再包罗子构件,由此在叶子构件中落实子构件管理和访问方法时要求提供十分处理或不当提醒。当然,那的确会给叶子构件的兑现带来麻烦。

要是两次三番抽象构件的是容器构件,则其杰出代码如下所示:

class Composite extends Component {  
    private ArrayList<Component> list = new ArrayList<Component>();  

    public void add(Component c) {  
        list.add(c);  
    }  

    public void remove(Component c) {  
        list.remove(c);  
    }  

    public Component getChild(int i) {  
        return (Component)list.get(i);  
    }  

    public void operation() {  
        //容器构件具体业务方法的实现  
        //递归调用成员构件的业务方法  
        for(Object obj:list) {  
            ((Component)obj).operation();  
        }  
    }     
}

在容器构件中贯彻了在抽象构件中扬言的拥有办法,既包蕴工作方法,也囊括用于访问和管理成员子构件的方法,如add()、remove()和getChild()等措施。需要专注的是在落成具体事务方法时,由于容器构件充当的是容器角色,包含成员构件,因而它将调用其成员构件的事体方法。在组成形式结构中,由于容器构件中依旧可以包含容器构件,由此在对容器构件举办拍卖时索要采用递归算法,即在容器构件的operation()方法中递归调用其成员构件的operation()方法。

思考

在组成格局协会图中,如若聚合关联关系不是从Composite到Component的,而是从Composite到Leaf的,如图11-4所示,会暴发哪些的结果?

图片 5

图11-4 组合形式思考题结构图

示例

  Sunny软件集团欲开发壹个杀毒(AntiVirus)软件,该软件既可以对某些文件夹(Folder)杀毒,也得以对某些钦命的公文(File)举行杀毒。该杀毒软件还是可以根据各个文件的特征,为不一致体系的文本提供差别的杀毒格局,例如图像文件(ImageFile)和文书文件(TextFile)的杀毒格局就持有差异。现须要提供该杀毒软件的总体框架设计方案。

图片 6

//为了突出核心框架代码,我们对杀毒过程的实现进行了大量简化  
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<Folder> folderList = new ArrayList<Folder>();  
    //定义集合imageList,用于存储ImageFile类型的成员  
    private ArrayList<ImageFile> imageList = new ArrayList<ImageFile>();  
    //定义集合textList,用于存储TextFile类型的成员  
    private ArrayList<TextFile> textList = new ArrayList<TextFile>();  

    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();  
    }  
}  

如下难点:
(1)
文件夹类Folder的宏图和促里昂非凡复杂,需要定义多个集聚存储不一致种类的成员,而且须要针对差其他分子提供增加、删除和得到等管理和访问成员的艺术,存在大量的冗余代码,系统保险相比较困难;
(2)
由于系统没有提供抽象层,客户端代码必须有分别地对待充当容器的文本夹Folder和担任叶子的ImageFile和TextFile,无法统一对它们举行拍卖;
(3)
系统的八面见光和可伸张性差,假诺急需追加新的品类的纸牌和容器都需求对原来代码举办改动,例如借使急需在系统中加进一种新品类的摄像文件VideoFile,则必须修改Folder类的源代码,否则无法在文件夹中添加视频文件。
消除方案:

图片 7

import java.util.*;  

//抽象文件类:抽象构件  
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();  
    }  
}  

11.1 设计杀毒软件的框架结构

 

      Sunny软件公司欲开发一个杀毒(AntiVirus)软件,该软件既可以对某个文件夹(Folder)杀毒,也可以对某个指定的文件(File)进行杀毒。该杀毒软件还可以根据各类文件的特点,为不同类型的文件提供不同的杀毒方式,例如图像文件(ImageFile)和文本文件(TextFile)的杀毒方式就有所差异。现需要提供该杀毒软件的整体框架设计方案。

 

 

     
在介绍Sunny集团开发人员指出的开首化解方案以前,大家先来分析一下操作系统中的文件目录结构,例如在Windows操作系统中,存在如图11-1所示目录结构:

图片 8

图11-1 Windows目录结构

      图11-1方可简化为如图11-2所示树形目录结构:

图片 9

图11-2 树形目录结构示意图

     
大家得以见到,在图11-2中含有文件(血牙红节点)和文件夹(土灰节点)两类差距的要素,其中在文书夹中得以分包文件,还足以一而再包涵子文件夹,可是在文件中不或者再蕴涵子文件恐怕子文件夹。在此,我们得以称文件夹为容器(Container),而各异品类的各个文件是其成员,也叫做叶子(Leaf),3个文书夹也得以当作另一个更大的公文夹的成员。假如大家今天要对某一个文书夹进行操作,如查找文件,那么须求对点名的公文夹举行遍历,假诺存在子文件夹则打开其子文件夹继续遍历,要是是文本则判断之后回来寻找结果。

     
Sunny软件商店的开发人士通过分析,决定利用面向对象的章程来完结对文件和文件夹的操作,定义了之类图像文件类ImageFile、文本文件类TextFile和文件夹类Folder:

//为了突出核心框架代码,我们对杀毒过程的实现进行了大量简化  
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<Folder> folderList = new ArrayList<Folder>();  
    //定义集合imageList,用于存储ImageFile类型的成员  
    private ArrayList<ImageFile> imageList = new ArrayList<ImageFile>();  
    //定义集合textList,用于存储TextFile类型的成员  
    private ArrayList<TextFile> textList = new ArrayList<TextFile>();  

    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();  
    }  
}  

编译并运维程序,输出结果如下:

 

****对文件夹’Sunny的资料’进行杀毒

****对文件夹’图像文件’进行杀毒

—-对图像文件’小龙女.jpg’进行杀毒

—-对图像文件’张无忌.gif’进行杀毒

****对文件夹’文本文件’进行杀毒

—-对文本文件’九阴真经.txt’进行杀毒

—-对文本文件’葵花宝典.doc’进行杀毒

 

 

     
Sunny公司开发人士“成功”完结了杀毒软件的框架设计,但透过周全分析,发现该设计方案存在如下难点:

     
(1) 文件夹类Folder的统筹和贯彻都极度复杂,需求定义五个汇集存储不一样门类的分子,而且亟需针对不一致的积极分子提供扩大、删除和取得等管理和访问成员的格局,存在大批量的冗余代码,系统吝惜比较困难;

     
(2) 由于系统没有提供抽象层,客户端代码必须有分别地对待充当容器的文书夹Folder和担任叶子的ImageFile和TextFile,无法统一对它们举办处理;

     
(3) 系统的灵活性和可增添性差,假诺须求充实新的类其余叶子和容器都急需对原本代码举行改动,例如倘若需求在系统中增添一种新品类的录像文件VideoFile,则必须修改Folder类的源代码,否则不能在文件夹中添加录制文件。

     
面对上述难题,Sunny软件公司的开发人员该怎么来缓解?这就须求用到本章将要介绍的结合格局,整合形式为拍卖树形结构提供了一种比较圆满的解决方案,它描述了怎么将容器和叶子举行递归组合,使得用户在应用时不要对它们进行区分,可以一如既往地对待容器和叶子

 

树形结构的拍卖——组合情势(三)

11.3 完整解决方案

为了让系统全数更好的无往不利和可增添性,客户端可以一样地对待文件和文件夹,Sunny公司开发人士使用组合方式来展开杀毒软件的框架设计,其基本社团如图11-5所示:

图片 10

图11-5 杀毒软件框架设计结构图

在图11-5中,
AbstractFile充当抽象构件类,Folder充当容器构件类,ImageFile、TextFile和VideoFile充当叶子构件类。完整代码如下所示:

import java.util.*;  

//抽象文件类:抽象构件  
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()方法,从而落成对全数树形结构的遍历。

如果急需转移操作节点,例如只需对文件夹“文本文件”举行杀毒,客户端代码只需修改一行即可,将代码:

folder1.killVirus();

改为:

folder3.killVirus();

输出结果如下:

****对文件夹'文本文件'进行杀毒
----对文本文件'九阴真经.txt'进行杀毒
----对文本文件'葵花宝典.doc'进行杀毒

在现实落到实处时,我们可以创造图形化界面让用户选拔所需操作的根节点,无须修改源代码,符合“开闭原则”,客户端无须关怀节点的层次结构,可以对所选节点举办合并处理,进步系统的一帆风顺。

三种格局

  • 透明组合形式
      透明组合格局中,抽象构件Component中宣示了拥有用于管理成员对象的章程,包罗add()、remove()以及getChild()等方法,那样做的益处是保证全体的部件类都有一致的接口。在客户端看来,叶子对象与容器对象所提供的方法是均等的,客户端可以同样地对待全部的目的。

    图片 11

**缺点:**不够安全,因为叶子对象和容器对象在本质上是有区别的。叶子对象不可能有下一个层次的对象,即不可能包含成员对象,因此为其提供add()、remove()以及getChild()等方法是没有意义的,这在编译阶段不会出错,但在运行阶段如果调用这些方法可能会出错(如果没有提供相应的错误处理代码)
  • 康宁构成格局【使用频率较高】
      安全整合情势中,在架空构件Component中一直不申明任何用于管理成员对象的格局,而是在Composite类中申明并落到实处那么些办法。

    图片 12

**缺点:**不够透明,因为叶子构件和容器构件具有不同的方法,且容器构件中那些用于管理成员对象的方法没有在抽象构件类中定义,因此客户端不能完全针对抽象编程,必须有区别地对待叶子构件和容器构件。

11.2 组合方式概述

     
对于树形结构,当容器对象(如文件夹)的某三个艺术被调用时,将遍历整个树形结构,寻找也含有那么些方法的成员对象(可以是容器对象,也得以是纸牌对象)并调用执行,牵一而动百,其中使用了递归调用的机制来对全数结构进行拍卖。由于容器对象和叶子对象在效劳上的不一样,在动用这一个目的的代码中务必有分别地对待容器对象和叶子对象,而实际多数意况下我们期待一致地处理它们,因为对于这一个目的的分别对待将会使得程序卓殊复杂。组合方式为消除此类题材而诞生,它可以让叶子对象和容器对象的行使所有一致性。

      组合形式定义如下:

组合模式(Composite Pattern):组合多个对象形成树形结构以表示具有“整体—部分”关系的层次结构。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性,组合模式又可以称为“整体—部分”(Part-Whole)模式,它是一种对象结构型模式。

     
在重组形式中引入了抽象构件类Component,它是兼具容器类和叶子类的共用父类,客户端针对Component举办编程。组合形式结构如图11-3所示:

图片 13

图11-3  组合方式结构图

      在整合格局结构图中富含如下几个角色:

      ● Component(抽象构件):它可以是接口或抽象类,为叶子构件和容器构件对象评释接口,在该剧中人物中得以包蕴所有子类共有行为的宣示和落实。在虚幻构件中定义了走访及管制它的子构件的方法,如增添子构件、删除子构件、获取子构件等。

      ● Leaf(叶子构件):它在组合结构中象征叶子节点目的,叶子节点没有子节点,它完成了在架空构件中定义的表现。对于那2个访问及管理子构件的方法,可以通过充裕等办法展开处理。

      ● Composite(容器构件):它在整合结构中代表容器节点目的,容器节点包罗子节点,其子节点可以是纸牌节点,也足以是容器节点,它提供二个凑合用于存储子节点,完成了在虚幻构件中定义的一言一行,蕴涵那多少个访问及管理子构件的办法,在其工作方法中可以递归调用其子节点的事情方法。

      组合格局的要紧是概念了一个架空构件类,它既可以象征叶子,又有什么不可代表容器,而客户端针对该抽象构件类举办编程,无须知道它终归意味着的是纸牌如故容器,可以对其进展联合处理。而且容器对象与虚幻构件类之间还树立一个凑合关联关系,在容器对象中既可以分包叶子,也得以分包容器,以此已毕递归组合,形成二个树形结构。

     
假诺不利用组合情势,客户端代码将广大地依靠于器皿对象复杂的里边贯彻结构,容器对象内部贯彻社团的变通将引起客户代码的一再变动,带来了代码维护复杂、可扩张性差等弊端。组合形式的引入将在一定水平上消除那一个题材。

     
上面通过不难的示范代码来分析组合情势的次第剧中人物的用处和促成。对于构成方式中的抽象构件脚色,其一流代码如下所示:

abstract class Component {  
    public abstract void add(Component c); //增加成员  
    public abstract void remove(Component c); //删除成员  
    public abstract Component getChild(int i); //获取成员  
    public abstract void operation();  //业务方法  
}  

诚如将抽象构件类设计为接口或抽象类,将具备子类共有方法的申明和兑现放在抽象构件类中。对于客户端而言,将对准抽象构件编程,而无须关切其具体子类是容器构件如故叶子构件。

      如果后续抽象构件的是纸牌构件,则其出众代码如下所示:

class Leaf extends Component {  
    public void add(Component c) {   
        //异常处理或错误提示   
    }     

    public void remove(Component c) {   
        //异常处理或错误提示   
    }  

    public Component getChild(int i) {   
        //异常处理或错误提示  
        return null;   
    }  

    public void operation() {  
        //叶子构件具体业务方法的实现  
    }   
}  

作为抽象构件类的子类,在叶子构件中必要实将来空洞构件类中注解的持有办法,包蕴业务方法以及管理和访问子构件的格局,不过叶子构件不可以再包蕴子构件,因而在叶子构件中达成子构件管理和走访方法时索要提供丰富处理或错误提醒。当然,那活脱脱会给叶子构件的兑现带来劳动。

      假如继续抽象构件的是容器构件,则其独立代码如下所示:

class Composite extends Component {  
    private ArrayList<Component> list = new ArrayList<Component>();  

    public void add(Component c) {  
        list.add(c);  
    }  

    public void remove(Component c) {  
        list.remove(c);  
    }  

    public Component getChild(int i) {  
        return (Component)list.get(i);  
    }  

    public void operation() {  
        //容器构件具体业务方法的实现  
        //递归调用成员构件的业务方法  
        for(Object obj:list) {  
            ((Component)obj).operation();  
        }  
    }     
}  

 在容器构件中落到实处了在空虚构件中声称的具有办法,既包含工作方法,也包罗用于访问和管理成员子构件的不二法门,如add()、remove()和getChild()等艺术。必要专注的是在促成具体工作方法时,由于容器构件充当的是容器剧中人物,包括成员构件,因而它将调用其成员构件的工作方法。在组合情势结构中,由于容器构件中仍可以分包容器构件,由此在对容器构件举行处理时索要选用递归算法,即在容器构件的operation()方法中递归调用其成员构件的operation()方法。

 

 

思考

      在组合模式结构图中,如果聚合关联关系不是从Composite到Component的,而是从Composite到Leaf的,如图11-4所示,会产生怎样的结果?

图11-4   组合模式思考题结构图

 

11.3  完整解决方案

      为了让系统具有更好的灵活性和可扩展性,客户端可以一致地对待文件和文件夹,Sunny公司开发人员使用组合模式来进行杀毒软件的框架设计,其基本结构如图11-5所示:

图11-5  杀毒软件框架设计结构图

    在图11-5中, AbstractFile充当抽象构件类,Folder充当容器构件类,ImageFile、TextFile和VideoFile充当叶子构件类。完整代码如下所示:

 

 

import java.util.*;  

//抽象文件类:抽象构件  
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()方法,从而完结对全部树形结构的遍历。

     
倘使要求更换操作节点,例如只需对文本夹“文本文件”举行杀毒,客户端代码只需修改一行即可,将代码:

 

folder1.killVirus();

 

       改为:

 

folder3.killVirus();

 

       输出结果如下:

 

****对文件夹’文本文件’进行杀毒

—-对文本文件’九阴真经.txt’进行杀毒

—-对文本文件’葵花宝典.doc’进行杀毒

 

       在现实贯彻时,大家可以创造图形化界面让用户采纳所需操作的根节点,无须修改源代码,符合“开闭原则”,客户端无须关切节点的层次结构,可以对所选节点举行合并处理,升高系统的一帆风顺。

树形结构的拍卖——组合方式(四)

11.4 透明组合格局与安全构成形式

通过引入组合情势,Sunny集团安顿的杀毒软件具有杰出的可扩张性,在伸张新的文件类型时,无须修改现有类库代码,只需追加壹个新的文件类作为AbstractFile类的子类即可,不过出于在AbstractFile中评释了汪洋用以管理和访问成员构件的法门,例如add()、remove()等办法,大家不得不在疯长的文件类中达成这一个办法,提供对应的失实提醒和这么些处理。为了简化代码,我们有以下多少个缓解方案:

化解方案一:将纸牌构件的add()、remove()等措施的贯彻代码移至AbstractFile类中,由AbstractFile提供联合的默认完毕,代码如下所示:

//提供默认实现的抽象构件类  
abstract class AbstractFile {  
    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 abstract void killVirus();  
}

只要客户端代码针对抽象类AbstractFile编程,在调用文件对象的那些主意时将现出错误提醒。假设不希望出现其余不当提示,大家得以在客户端定义文件对象时不行使抽象层,而直白动用具体叶子构件本人,客户端代码片段如下所示:

class Client {  
    public static void main(String args[]) {  
        //不能透明处理叶子构件  
        ImageFile file1,file2;  
        TextFile file3,file4;  
        VideoFile file5;  
        AbstractFile folder1,folder2,folder3,folder4;  
        //其他代码省略  
      }  
}

诸如此类就生出了一种不透明的拔取方式,即在客户端不能全体针对抽象构件类编程,必要利用具体叶子构件类型来定义叶子对象。

化解方案二:除此之外,还有一种缓解格局是在空虚构件AbstractFile中不注明任何用于访问和保管成员构件的方法,代码如下所示:

abstract class AbstractFile {  
    public abstract void killVirus();  
}

那会儿,由于在AbstractFile中并未申明add()、remove()等做客和管制成员的格局,其叶子构件子类无须提供达成;而且无论客户端怎么着定义叶子构件对象都心有余而力不足调用到那些办法,不须求做此外不当和充裕处理,容器构件再根据需求增添访问和保管成员的法门,但此时也设有二个标题:客户端不得不动用容器类本人来声称容器构件对象,否则不可以访问其中新增的add()、remove()等艺术,假若客户端一致性地对待叶子和容器,将会促成容器构件的剧增对客户端不可知,客户端代码对于容器构件无法再采纳抽象构件来定义,客户端代码片段如下所示:

class Client {  
    public static void main(String args[]) {  

        AbstractFile file1,file2,file3,file4,file5;  
        Folder folder1,folder2,folder3,folder4; //不能透明处理容器构件  
        //其他代码省略  
    }  
}

在利用组合格局时,依照抽象构件类的概念方式,我们可将结合情势分为透明组合格局和随州整合方式三种形式:

(1) 透明组合情势

晶莹剔透组合方式中,抽象构件Component中宣示了全数用于管理成员对象的方法,包涵add()、remove()以及getChild()等格局,那样做的功利是承保全部的构件类都有一样的接口。在客户端看来,叶子对象与容器对象所提供的主意是如出一辙的,客户端可以一如既往地对待全数的靶子。透明组合格局也是结合格局的科班格局,即使上面的消除方案一在客户端可以有不透明的贯彻情势,不过出于在空虚构件中蕴藏add()、remove()等格局,由此它照旧晶莹剔透组合形式,透明组合格局的全体结构如图11-6所示:

图片 14

图11-6 透明组合格局结构图

透明组合方式的瑕疵是不够安全,因为叶子对象和容器对象在精神上是有分其他。叶子对象不能有下三个层次的靶子,即不容许含有成员对象,由此为其提供add()、remove()以及getChild()等措施是平素不意义的,那在编译阶段不会出错,但在运维阶段如若调用那一个办法恐怕会出错(假使没有提供相应的错误处理代码)。

(2) 安全构成格局

康宁整合格局中,在抽象构件Component中一贯不讲明任何用于管理成员对象的方法,而是在Composite类中宣示并落到实处这个方法。那种做法是安全的,因为向来不向叶子对象提供这么些管理成员对象的点子,对于叶子对象,客户端不或然调用到这几个措施,那就是消除方案二所运用的落到实处格局。安全构成形式的结构如图11-7所示:

图片 15

图11-7 安全构成方式结构图

有惊无险整合方式的短处是不够透明,因为叶子构件和容器构件具有差其余措施,且容器构件中那多少个用来管理成员对象的不二法门没有在空虚构件类中定义,由此客户端不或许完全针对抽象编程,必须有分别地对待叶子构件和容器构件。在实际应用中,安全整合方式的行使频率也要命高,在Java
AWT中接纳的结合形式就是安全构成形式。

总结

紧要优点:
(1)
组合形式可以驾驭地定义分层次的复杂性对象,表示对象的任何或部分层次,它让客户端忽略了层次的分裂,方便对全部层次结构举办支配。
(2)
客户端可以一如既往地拔取3个重组结构或内部单个对象,不必关切处理的是单个对象如故整个组合结构,简化了客户端代码。
(3)
在组合方式中加进新的器皿构件和叶子构件都很方便,无须对现有类库举行其余修改,符合“开闭原则”。
(4)
组合方式为树形结构的面向对象实现提供了一种灵活的化解方案,通过叶子对象和容器对象的递归组合,可以形成复杂的树形结构,但对树形结构的主宰却非常简单。

重点症结:
  在增多新构件时很难对容器中的构件类型进行限定。有时候大家意在1个容器中不得不有有个别特定类型的对象,例如在有个别文件夹中不得不分包文本文件,使用组合情势时,无法凭借类型系统来施加那个约束,因为它们都来源于于同一的抽象层,在那种处境下,必须透过在运作时举办项目检查来促成,那些已毕进度较为复杂。

11.4  透明组合方式与安全构成方式

     
通过引入组合方式,Sunny公司设计的杀毒软件具有出色的可伸张性,在增多新的文件类型时,无须修改现有类库代码,只需追加3个新的文本类作为AbstractFile类的子类即可,可是出于在AbstractFile中声称了汪洋用以管理和访问成员构件的章程,例如add()、remove()等方式,我们只可以在增产的文书类中落到实处这个主意,提供对应的谬误指示和充足处理。为了简化代码,我们有以下多个缓解方案:

      化解方案一:将纸牌构件的add()、remove()等措施的贯彻代码移至AbstractFile类中,由AbstractFile提供联合的私自认同达成,代码如下所示:

//提供默认实现的抽象构件类  
abstract class AbstractFile {  
    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 abstract void killVirus();  
}  

即便客户端代码针对抽象类AbstractFile编程,在调用文件对象的那么些方式时将应运而生谬误提示。如若不期望出现其余错误指示,大家得以在客户端定义文件对象时不使用抽象层,而直接使用具体叶子构件本身,客户端代码片段如下所示:

class Client {  
    public static void main(String args[]) {  
        //不能透明处理叶子构件  
        ImageFile file1,file2;  
        TextFile file3,file4;  
        VideoFile file5;  
        AbstractFile folder1,folder2,folder3,folder4;  
        //其他代码省略  
      }  
}  

 
那样就发生了一种不透明的运用办法,即在客户端无法整个针对性抽象构件类编程,要求运用具体叶子构件类型来定义叶子对象。

      消除方案二:除却,还有一种缓解方法是在空虚构件AbstractFile中不表明任何用于访问和管制成员构件的点子,代码如下所示:

abstract class AbstractFile {  
    public abstract void killVirus();  
}  

那时候,由于在AbstractFile中绝非表明add()、remove()等做客和治本成员的法门,其叶子构件子类无须提供达成;而且无论客户端怎么样定义叶子构件对象都爱莫能助调用到那么些艺术,不须求做任何错误和特别处理,容器构件再按照须求充实访问和管制成员的法子,但此刻也设有叁个难点:客户端不得不动用容器类本身来声称容器构件对象,否则不大概访问其中新增的add()、remove()等情势,若是客户端一致性地对待叶子和容器,将会导致容器构件的疯长对客户端不可知,客户端代码对于容器构件无法再利用抽象构件来定义,客户端代码片段如下所示:

class Client {  
    public static void main(String args[]) {  

        AbstractFile file1,file2,file3,file4,file5;  
        Folder folder1,folder2,folder3,folder4; //不能透明处理容器构件  
        //其他代码省略  
    }  
}  

 在行使组合方式时,依照抽象构件类的定义方式,大家可将整合方式分为透明组合情势和平安构成方式三种样式:

      (1) 透明组合情势

     
透明组合形式中,抽象构件Component中扬言了拥有用于管理成员对象的不二法门,包蕴add()、remove()以及getChild()等办法,那样做的功利是承保全部的预制构件类都有雷同的接口。在客户端看来,叶子对象与容器对象所提供的法门是均等的,客户端能够同样地对待全部的目的。透明组合形式也是组成方式的正规化格局,固然上边的消除方案一在客户端可以有不透明的兑现格局,不过出于在架空构件中含有add()、remove()等措施,由此它照旧透明组合格局,透明组合形式的一体化结构如图11-6所示:

图片 16

图11-6  透明组合格局结构图

      透明组合方式的症结是不够安全,因为叶子对象和容器对象在真相上是有分其余。叶子对象不容许有下二个层次的目的,即无法带有成员对象,由此为其提供add()、remove()以及getChild()等方法是尚未意思的,那在编译阶段不会出错,但在运作阶段如果调用这一个艺术或然会出错(假若没有提供对应的错误处理代码)。

      (2) 安全构成情势

     
安全整合情势中,在空洞构件Component中并未申明任何用于管理成员对象的方法,而是在Composite类中宣示并落到实处那么些方式。这种做法是安全的,因为根本不向叶子对象提供那个管理成员对象的法门,对于叶子对象,客户端无法调用到这几个措施,那就是缓解方案二所运用的贯彻情势。安全构成方式的布局如图11-7所示:

图片 17

图11-7  安全构成形式结构图

      
安全整合格局的老毛病是不够透明,因为叶子构件和容器构件具有区其余点子,且容器构件中那个用来管理成员对象的主意没有在虚幻构件类中定义,由此客户端不可以一心针对抽象编程,必须有分别地对待叶子构件和容器构件。在实际应用中,安全构成方式的施用频率也丰盛高,在Java AWT中利用的咬合方式就是安全整合方式。

树形结构的拍卖——组合形式(五)

11.5 公司集团结构

在攻读和应用组合情势时,Sunny软件公司开发人员发现树形结构其实四处可知,例如Sunny公司的团队结构就是“一棵标准的树”,如图11-8所示:

图片 18

图11-8 Sunny集团公司结构图

在Sunny软件集团的中间办公室系统SunnyOA系统中,有一个与信用协会队结构对应的树形菜单,行政人士可以给各级单位颁发布告,那个单位可以是总局的两个机构,也得以是三个分号,仍是可以够是分公司的一个部门。用户只需求选拔三个根节点即可已毕通知的发出操作,而无须关心具体的兑现细节。那不正是结合格局的“特长”吗?于是Sunny公司开发人士绘制了如图11-9所示结构图:

图片 19

图11-9 萨妮集团协会结构组成形式示意图

在图11-9中,“单位”充当了画饼充饥构件角色,“公司”充当了容器构件剧中人物,“研发部”、“财务部”和“人力能源部”充当了叶子构件剧中人物。

思考

怎么着编码完成图11-9中的“公司”类?

11.6 组合情势统计

组合形式选用面向对象的惦记来促成树形结构的打造与拍卖,描述了什么将容器对象和叶子对象进行递归组合,落成不难,灵活性好。由于在软件开发中设有大气的树形结构,由此组合格局是一种采用成效较高的结构型设计情势,Java
SE中的AWT和Swing包的筹划就依照组合方式,在这么些界面包中为用户提供了多量的容器构件(如Container)和成员构件(如Checkbox、Button和TextComponent等),其布局如图11-10所示:
![图11-10 AWT组合方式结构示意图

图片 20

java-awt-swing-composite.jpg

在图11-10中,Component类是望梅止渴构件,Checkbox、Button和TextComponent是纸牌构件,而Container是容器构件,在AWT中带有的叶子构件还有许多,因为篇幅限制没有在图中逐条列出。在三个器皿构件中可以涵盖叶子构件,也能够持续包涵容器构件,那几个叶子构件和容器构件一起组成了复杂的GUI界面。

除此以外,在XML解析、社团结构树处理、文件系统设计等领域,组合格局都得到了广泛应用。

  1. 重中之重优点

组合形式的要害优点如下:

(1)
组合形式可以领会地定义分层次的复杂对象,表示对象的总体或局地层次,它让客户端忽略了层次的出入,方便对全部层次结构举行控制。

(2)
客户端可以一样地应用二个重组结构或内部单个对象,不必关注处理的是单个对象照旧整个组合结构,简化了客户端代码。

(3)
在重组情势中扩张新的容器构件和叶子构件都很便宜,无须对现有类库进行任何改动,符合“开闭原则”。

(4)
组合形式为树形结构的面向对象完成提供了一种灵活的缓解方案,通过叶子对象和容器对象的递归组合,能够形成复杂的树形结构,但对树形结构的决定却万分不难。

  1. 关键缺点

组成情势的根本症结如下:

在增多新构件时很难对容器中的构件类型进行界定。有时候大家希望二个器皿中不得不有某个特定类型的靶子,例如在有些文件夹中不得不分包文本文件,使用组合格局时,不或然依赖类型系统来施加这么些约束,因为它们都源于于一致的抽象层,在那种状态下,必须透过在运作时展开项目检查来兑现,那些落成进度相比较复杂。

  1. 适用场景

在偏下景况下可以设想拔取组合情势:

(1)
在拥有完整和一部分的层次结构中,希望经过一种艺术忽略全体与一些的出入,客户端可以同样地对待它们。

(2) 在二个选用面向对象语言开发的连串中需求处理2个树形结构。

(3)
在2个连串中可见分离出叶子对象和容器对象,而且它们的项目不定点,要求扩大部分新的品种。

练习

Sunny软件商店欲开发1个界面控件库,界面控件分为两大类,一类是单元控件,例如按钮、文本框等,一类是容器控件,例如窗体、中间面板等,试用组合格局设计该界面控件库。

勤学苦练会在我的github上做掉

适用场景

(1)
在享有完全和一些的层次结构中,希望经过一种办法忽略全部与一些的歧异,客户端可以一如既往地对待它们。
(2) 在二个应用面向对象语言开发的系统中要求处理一个树形结构。
(3)
在多个体系中可见分离出叶子对象和容器对象,而且它们的档次不定点,须要追加部分新的系列。

11.5 集团社团结构

       在念书和运用组合情势时,Sunny软件集团开发人士发现树形结构其实随处可知,例如Sunny公司的团队结构就是“一棵标准的树”,如图11-8所示:

图片 21

图11-8  Sunny公司团队结构图

      在萨妮软件商店的其中办公室系统SunnyOA系统中,有三个与商店团队结构对应的树形菜单,行政人员可以给各级单位颁公布告,那么些单位可以是总店的七个机构,也足以是1个支行,还能是分公司的一个单位。用户只要求选拔二个根节点即可兑现公告的颁发操作,而无须关心具体的兑现细节。那不正是结合情势的“特长”吗?于是Sunny公司开发人士绘制了如图11-9所示结构图:

图片 22

图11-9  Sunny集团公司结构组成格局示意图

       在图11-9中,“单位”充当了抽象构件角色,“集团”充当了容器构件角色,“研发部”、“财务部”和“人力财富部”充当了叶子构件角色。

 

思考

如何编码实现图11-9中的“公司”类?

 

11.6 组合格局统计

     
组合形式接纳面向对象的构思来兑现树形结构的营造与处理,描述了何等将容器对象和叶子对象举办递归组合,完成简单,灵活性好。由于在软件开发中留存多量的树形结构,由此组合情势是一种接纳成效较高的结构型设计格局,Java
SE
中的AWT和Swing包的设计就依照组合方式,在那一个界面包中为用户提供了汪洋的容器构件(如Container)和成员构件(如Checkbox、Button和TextComponent等),其协会如图11-10所示:

图片 23

图11-10 AWT组合格局协会示意图

     
在图11-10中,Component类是抽象构件,Checkbox、Button和TextComponent是纸牌构件,而Container是容器构件,在AWT中包蕴的叶子构件还有很多,因为篇幅限制没有在图中各样列出。在一个容器构件中得以涵盖叶子构件,也能够一而再包罗容器构件,这么些叶子构件和容器构件一起构成了复杂的GUI界面。

     
除此以外,在XML解析、协会结构树处理、文件系统设计等领域,组合模式都得到了广泛应用。

      1. 根本优点

      组合形式的要害优点如下:

     
(1) 组合格局可以知晓地定义分层次的复杂对象,表示对象的满贯或局地层次,它让客户端忽略了层次的反差,方便对一切层次结构进行控制。

     
(2) 客户端可以一样地行使多个组合结构或内部单个对象,不必关怀处理的是单个对象照旧整个组合结构,简化了客户端代码。

     
(3) 在整合形式中扩充新的容器构件和叶子构件都很便宜,无须对现有类库举办任何改动,符合“开闭原则”。

     
(4) 组合方式为树形结构的面向对象完成提供了一种灵活的缓解方案,通过叶子对象和容器对象的递归组合,可以形成复杂的树形结构,但对树形结构的支配却分外简单。

      2. 主要弱点

      组合情势的重大症结如下:

     
在增多新构件时很难对容器中的构件类型举办界定。有时候大家期望3个容器中不得不有某个特定类型的目的,例如在有些文件夹中不得不分包文本文件,使用组合方式时,不可以依靠类型系统来施加那一个约束,因为它们都源于于同一的抽象层,在那种境况下,必须通过在运营时进行项目检查来达成,这么些完成进度较为复杂。

      3. 适用场景

      在以下境况下得以考虑选取组合情势:

     
(1) 在拥有完整和有个别的层次结构中,希望经过一种方法忽略全部与一些的出入,客户端可以一如既往地对待它们。

      (2) 在3个采取面向对象语言开发的系统中需求处理一个树形结构。

     
(3) 在壹个种类中可见分离出叶子对象和容器对象,而且它们的门类不定点,需求追加一些新的项目。

 

练习

Sunny软件公司欲开发一个界面控件库,界面控件分为两大类,一类是单元控件,例如按钮、文本框等,一类是容器控件,例如窗体、中间面板等,试用组合模式设计该界面控件库。

 

 

【作者:刘伟  http://blog.csdn.net/lovelion

 

相关文章