设计模式——组合模式

组合模式

  1. 组合模式动机及定义

1.1模式动机

        在面向对象系统中,我们常常会遇到一类具有“容器”特征对对象——即它们在充当普通对象对同时,又可作为其他对象对容器,这些对象成为容器对象,而那些只能充当普通对象的对象则称为叶子对象。在容器对象中即可以包含叶子对象,又可以包含容器对象,为了更好地解决容器对象和叶子对象之间的关系,使之操作更加简单,我们需要学习一种新的结构设计模式,即组合模式。组合模式实际要解决的问题是:对整体与个体的操作应一致,类似对文件夹与文件对操作。

        组合模式描述了如何将容器对象和叶子对象进行递归组合,使得用户在使用时无须对它们进行区分,可以一致地对待容器对象和叶子对象,这就是组合模式的模式动机。

1.2模式定义

        组合模式定义(Composite Pattern)定义:组合多个对象形成树形结构以表示“部门-整体”的结构层次。组合模式对单个对象(即叶子对象)和组合对象(即容器对象)的使用具有一致性。组合模式又可以称为“部分-整体”(Part-Whole)模式,属于对象的结构模式,它将对象组织到树形结构中,可以用来描述整体与部分的关系。

  英文定义:“Compose objects into tree structures to represent part-whole hierarchies.Composite lets clients treat individual objects and compositions of objects uniformly.”

2.组合模式结构与分析

组合模式的核心在于引入了一个抽象类,它既是叶子类的父类,也是容器类的父类。

2.1模式结构

 

1.Component(抽象构件)

        抽象构件可以是接口或抽象类,为叶子构件和容器构件对象声明接口,在该角色中可以包含所有子类公有行为的声明和实现。在抽象构件中定义了访问及管理它的子构件的方法,如增加子构件、删除子构件、获取子构件等。

2.Leaf(叶子构件)

        叶子构件在组合结构中表示叶子节点对象,叶子节点没有子节点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过异常等方式进行处理。

3.Composite(容器构件)

        容器构件在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义对行为,包括那些访问及管理子构件对方法,在其业务方法中可以递归调用其子节点对业务方法。

4.Client(客户类)

        客户类可以通过抽象构件接口访问和控制构件中的对象。

3.组合模式实例与解析

题目:使用组合模式设计一个杀毒软件(AntiVirus)的框架,该软件既可以对某个文件夹(Folder)杀毒,也可以对某个指定的文件(File)进行杀毒,文件种类包括文本文件TextFile、图片文件ImageFile、音频视频文件MediaFile。绘制类图并编程实现。

3.1实例类图

 

3.2实例代码及解释

        1.抽象构件类AbstractFile(抽象文件类)

package composite_pattern;

/*

* 抽象文件类:抽象构件

* */

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

}

        AbstractFile是抽象构件类,在其中声明了killVirus()方法,在其子类中实现该方法。

        2.叶子构件类VideoFile(视频文件类)

package composite_pattern;



/*

* 视频文件类:叶子构件

* */



public class VideoFile extends AbstractFile{

    private String name;

    public VideoFile(String name) {

        this.name = name;

    }


    @Override

    public void add(AbstractFile file) {

        System.out.println("错误,无法进行该方法");

    }

    @Override

    public void remove(AbstractFile file) {

        System.out.println("错误,无法进行该方法");

    }

    @Override

    public AbstractFile getChild(int i) {

        System.out.println("错误,无法进行该方法");

        return null;

    }

    @Override

    public void killVirus() {

        System.out.println("正在对Video文件'" + name + "'进行杀毒操作");



    }

}

        VideoFile类是叶子构件类,它实现了在抽象构件类中定义的方法killVirus();

3.叶子构件类TextFile(文本文件类)

package composite_pattern;



/*

* 文本文件类:叶子构件

* */



public class TextFile extends AbstractFile{

    private String name;

    public TextFile(String name) {

        this.name = name;

    }

    @Override

    public void add(AbstractFile file) {

        System.out.println("错误,无法进行该方法");

    }

    @Override

    public void remove(AbstractFile file) {

        System.out.println("错误,无法进行该方法");

    }

    @Override

    public AbstractFile getChild(int i) {

        System.out.println("错误,无法进行该方法");

        return null;

    }

    @Override

    public void killVirus() {

        System.out.println("正在对Text文件'" + name + "'进行杀毒操作");



    }

}

        TextFile类也是叶子构件类,它实现了在抽象构件类中定义的方法killVirus();

4.叶子构件类ImageFile(图片文件类)

package composite_pattern;



/*

* 图像文件类:叶子构件

* */



public class ImageFile extends AbstractFile{

    private String name;

    public ImageFile(String name) {

        this.name = name;

    }

    @Override

    public void add(AbstractFile file) {

        System.out.println("错误,无法进行该方法");

    }

    @Override

    public void remove(AbstractFile file) {

        System.out.println("错误,无法进行该方法");

    }

    @Override

    public AbstractFile getChild(int i) {

        System.out.println("错误,无法进行该方法");

        return null;

    }

    @Override

    public void killVirus() {



        System.out.println("正在对Image文件'" + name + "'进行杀毒操作");



    }

}

        ImageFile类也是叶子构件类,它实现了在抽象构件类中定义的方法killVirus();

5.容器构件类Folder(文件夹类)

package composite_pattern;



/*

* 文件夹类:容器类

* */



import java.util.ArrayList;

public class Folder extends AbstractFile {

    private ArrayList<AbstractFile> fileList = new ArrayList<AbstractFile>();

    private String name;


    public Folder(String name) {

        this.name = name;

    }

    @Override

    public void add(AbstractFile file) {

        fileList.add(file);

    }

    @Override

    public void remove(AbstractFile file) {

        fileList.remove(file);

    }

    @Override

    public AbstractFile getChild(int i) {

        return (AbstractFile)fileList.get(i) ;

    }

    @Override

    public void killVirus() {

        System.out.println("正在对文件夹'"+ name + "'进行杀毒");



        //递归调用成员构件的killVirus()方法

        for (Object obj:fileList){

            ((AbstractFile)obj).killVirus();

        }

    }

}

        Folder类是容器构件类,在其代码中需要注意三个要点:首先它定义了一个抽象构件类型的集合,此处使用ArrayList来实现;它提供了用于操作子构件的相关方法,如增加子构件、删除子构件和获取子构件等方法;它实现了在抽象构件中定义的killVirus方法,且在该方法的内部递归调用其子构件的killVirus()方法。

3.3辅助代码

客户端测试类Client如下:

package composite_pattern;



public class Client {

    public static void main(String[] args) {

        //针对抽象构件编程

        AbstractFile file1,file2,file3,file4,file5,folder1,folder2,folder3,folder4;



        folder1 = new Folder("我的文件");

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



        //从“我的文件”节点开始进行杀毒操作

        folder1.killVirus();



    }

}

        在客户端代码中,实例化了一些叶子构件即文件类,也实例化了一些容器构件即文件夹类,通过文件夹类的add()方法可以将子构件添加到文件夹中,其子构件可以是文件对象,也可以是文件夹对象。

3.4结果

 

4.组合模式效果与应用

4.1 组合模式的优点

(1)组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,使得增加新构件也更容易,因为它让客户忽略了层次的差异,而它的结构又是动态的,提供了对象管理的灵活接口,因此组合模式可以方便地对层次结构进行控制。

(2)客户端调用简单,客户端可以一致地使用组合结构或其中单个对象,用户就不必关心自己处理的是单个对象还是整个组合结构,简化了客户端代码。

(3)定义了包含叶子对象和容器对象的类层次结构,叶子对象可以被组合成更复杂的容器对象,而这个容器对象又可以被组合,这样不断递归下去,可以形成复杂的树形结构。

(4)更容易在组合体内加人对象构件,客户端不必因为加人了新的对象构件而更改原有代码。

4.2组合模式的缺点

(1)使设计变得更加抽象,对象的业务规则如果很复杂,则实现组合模式具有很大挑战性,而且不是所有的方法都与叶子对象子类都有关联。

(2)增加新构件时可能会产生一些问题,很难对容器中的构件类型进行限制。有时候我们希望一个容器中只能有某些特定类型的对象,使用组合模式时,不能依赖类型系统来施加这些约束,因为它们都来自于相同的抽象层,在这种情况下,必须通过在运行时进行类型检杳来实现,这个实现过程较为复杂。

4.3模式适用环境

在以下情况可以使用组合模式。

  1. 需要表示一个对象的整体或部分层次,在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,可以一致地对待它们。
  2. 让客户能够忽略不同对象层次的变化,客户端可以针对抽象构件编程,无须关心对象层次结构的细节。
  3. 对象的结构是动态的并且复杂程度不一样,但客户需要一致地处理它们。
  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值