设计模式的征途—9.组合(Composite)模式

树形结构在软件中随处可见,比如操作系统中的目录结构,公司组织结构等等,如何运用面向对象的方式来处理这种树形结构是组合模式需要解决的问题。组合模式通过一种巧妙的设计方案来使得用户可以一致性地处理整个树形结构或者树形结构的一部分,也可以一致地处理树形结构中的叶子节点(不包含子节点的节点)和容器节点(包含子节点的节点),本次我们就将学习一下用来处理树形结构的组合模式。

组合模式(Composite) 学习难度:★★★☆☆ 使用频率:★★★★☆

一、杀毒软件的框架设计

1.1 需求介绍

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

  首先,我们来了解一下Windows操作系统中的目录结构:

  

1.2 初始设计

  M公司程序猿们通过分析,决定使用面向对象的方式来实现对文件和文件夹的操作,定义了图像文件类ImageFile、文本文件类TextFile和文件夹类Folder,代码如下:

  (1)文件类:

    public class ImageFile
    {
        private string name;

        public ImageFile(string name)
        {
            this.name = name;
        }

        public void KillVirus()
        {
            // 此处模拟杀毒操作
            Console.WriteLine("---- 对图像文件‘{0}’进行杀毒", name);
        }
    }

    public class TextFile
    {
        private string name;

        public TextFile(string name)
        {
            this.name = name;
        }

        public void KillVirus()
        {
            // 此处模拟杀毒操作
            Console.WriteLine("---- 对文本文件‘{0}’进行杀毒", name);
        }
    }

  (2)文件夹类:

    public class Folder
    {
        private string name;

        private IList<Folder> folderList = new List<Folder>();
        private IList<ImageFile> imageList = new List<ImageFile>();
        private IList<TextFile> textList = new List<TextFile>();

        public Folder(string name)
        {
            this.name = name;
        }

        public void AddFolder(Folder f)
        {
            folderList.Add(f);
        }

        public void AddImageFile(ImageFile image)
        {
            imageList.Add(image);
        }

        public void AddTextFile(TextFile text)
        {
            textList.Add(text);
        }

        // 需要提供3个不同的方法 RemoveFolder, RemoveImageFile, RemoveTextFile来删除成员,代码省略

        // 需要提供3个不同的方法 GetChildFolder(int i), GetChildImageFile(int i), GetChildTextFile(int i)来获取成员,代码省略

        public void KillVirus()
        {
            Console.WriteLine("**** 对文件夹‘{0}‘进行杀毒", name);

            foreach (var item in folderList)
            {
                item.KillVirus();
            }

            foreach (var item in imageList)
            {
                item.KillVirus();
            }

            foreach (var item in textList)
            {
                item.KillVirus();
            }
        }
    }

  (3)客户端调用:

     public class Program
     {
        public static void Main()
        {
            Folder folder1 = new Folder("EDC的资料");
            Folder folder2 = new Folder("图像文件");
            Folder folder3 = new Folder("文本文件");

            ImageFile image1 = new ImageFile("小龙女.jpg");
            ImageFile image2 = new ImageFile("张无忌.gif");

            TextFile text1 = new TextFile("九阴真经.txt");
            TextFile 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)系统的灵活性和可扩展性差,如果需要增加新的类型的叶子和容器都需要对原有代码进行修改。

二、组合模式简介

2.1 模式概述

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

2.2 结构图

  在组合模式中引入了抽象构件类Component,它是所有容器类和叶子类的公共父类,客户端针对Component进行编程。组合模式结构如下图所示:

  组合模式包含以下几个角色:

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

  (2)Leaf(叶子构件):它在组合模式中表示叶子结点对象,叶子结点没有子节点,它实现了在抽象构件中定义的行为。

  (3)Composite(容器构件):它在组合模式中表示容器节点对象,容器节点包含子节点,其子节点可以使叶子结点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为。

三、重构杀毒软件框架设计

3.1 重构后的设计结构

  其中,AbstractFile充当抽象构件类,Folder充当容器类,ImageFile、TextFile以及VideoFile充当叶子构件类。

3.2 重构后的代码实现

  (1)抽象构件:AbstractFile

    /// <summary>
    ///  抽象文件类:抽象构件
    /// </summary>
    public abstract class AbstractFile
    {
        public abstract void Add(AbstractFile file);
        public abstract void Remove(AbstractFile file);
        public abstract AbstractFile GetChild(int index);
        public abstract void KillVirus();
    }

  (2)叶子构件:ImageFile、VideoFile、TextFile类

    /// <summary>
    /// 叶子构件:图像文件、文本文件 和 视频文件
    /// </summary>
    public class ImageFile : AbstractFile
    {
        private string name;

        public ImageFile(string name)
        {
            this.name = name;
        }

        public override void Add(AbstractFile file)
        {
            Console.WriteLine("对不起,系统不支持该方法!");
        }

        public override void Remove(AbstractFile file)
        {
            Console.WriteLine("对不起,系统不支持该方法!");
        }

        public override AbstractFile GetChild(int index)
        {
            Console.WriteLine("对不起,系统不支持该方法!");
            return null;
        }

        public override void KillVirus()
        {
            // 此处模拟杀毒操作
            Console.WriteLine("**** 对图像文件‘{0}’进行杀毒", name);
        }
    }

    public class TextFile : AbstractFile
    {
        private string name;

        public TextFile(string name)
        {
            this.name = name;
        }

        public override void Add(AbstractFile file)
        {
            Console.WriteLine("对不起,系统不支持该方法!");
        }

        public override void Remove(AbstractFile file)
        {
            Console.WriteLine("对不起,系统不支持该方法!");
        }

        public override AbstractFile GetChild(int index)
        {
            Console.WriteLine("对不起,系统不支持该方法!");
            return null;
        }

        public override void KillVirus()
        {
            // 此处模拟杀毒操作
            Console.WriteLine("**** 对文本文件‘{0}’进行杀毒", name);
        }
    }

    public class VideoFile : AbstractFile
    {
        private string name;

        public VideoFile(string name)
        {
            this.name = name;
        }

        public override void Add(AbstractFile file)
        {
            Console.WriteLine("对不起,系统不支持该方法!");
        }

        public override void Remove(AbstractFile file)
        {
            Console.WriteLine("对不起,系统不支持该方法!");
        }

        public override AbstractFile GetChild(int index)
        {
            Console.WriteLine("对不起,系统不支持该方法!");
            return null;
        }

        public override void KillVirus()
        {
            // 此处模拟杀毒操作
            Console.WriteLine("**** 对视频文件‘{0}’进行杀毒", name);
        }
    }

  (3)容器构件:Folder

    /// <summary>
    /// 文件夹类:容器构件
    /// </summary>
    public class Folder : AbstractFile
    {
        private IList<AbstractFile> fileList = new List<AbstractFile>();
        private string name;

        public Folder(string name)
        {
            this.name = name;
        }

        public override void Add(AbstractFile file)
        {
            fileList.Add(file);
        }

        public override void Remove(AbstractFile file)
        {
            fileList.Remove(file);
        }

        public override AbstractFile GetChild(int index)
        {
            return fileList[index];
        }

        public override void KillVirus()
        {
            // 此处模拟杀毒操作
            Console.WriteLine("---- 对文件夹‘{0}’进行杀毒", name);

            foreach (var item in fileList)
            {
                item.KillVirus();
            }
        }
    }

  (4)客户端调用

    public class Program
    {
        public static void Main()
        {
            AbstractFile folder1 = new Folder("EDC的资料");
            AbstractFile folder2 = new Folder("图像文件");
            AbstractFile folder3 = new Folder("文本文件");
            AbstractFile folder4 = new Folder("视频文件");

            AbstractFile image1 = new ImageFile("小龙女.jpg");
            AbstractFile image2 = new ImageFile("张无忌.gif");

            AbstractFile text1 = new TextFile("九阴真经.txt");
            AbstractFile text2 = new TextFile("葵花宝典.doc");

            AbstractFile video1 = new VideoFile("笑傲江湖.rmvb");
            AbstractFile video2 = new VideoFile("天龙八部.mp4");

            folder2.Add(image1);
            folder2.Add(image2);

            folder3.Add(text1);
            folder3.Add(text2);

            folder4.Add(video1);
            folder4.Add(video2);

            folder1.Add(folder2);
            folder1.Add(folder3);
            folder1.Add(folder4);

            folder1.KillVirus();
        }
    }

  执行结果如下图所示:

  

  如果只需更换操作节点,例如只需要针对文件夹“文本文件”进行杀毒,只需要修改客户端代码如下:

    //folder1.KillVirus();
    folder3.KillVirus();

  执行结果如下图所示:

  

  在具体实现时,可以创建图形界面让用户自己选择所需操作的根节点,无需修改源代码,符合开闭原则,客户端无须关心节点的层次结构,可以对所选节点进行统一处理,提高系统的灵活性。

四、组合模式小结

4.1 主要优点

  (1)可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,使客户忽略了层次的差异,方便对整个层次结构进行控制。

  (2)增加新的容器构件和叶子构件都十分方便,无需对现有类库代码进行任何修改,符合开闭原则

  (3)为树形结构的面向对象实现提供了灵活地解决方案,可以形成复杂的树形结构,但对树形结构的控制却很简单

4.2 主要缺点

  增加新构件时很难对容器中的构建类型进行限制。

4.3 适用场景

  (1)在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待他们。

  (2)在一个使用面向对象语言开发的系统中需要处理一个树形结构。

参考资料

  

  刘伟,《设计模式的艺术—软件开发人员内功修炼之道》

作者:周旭龙

出处:http://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

时间: 2024-10-11 05:37:23

设计模式的征途—9.组合(Composite)模式的相关文章

C++设计模式实现--组合(Composite)模式

一. 举例 这个例子是书上的,假设有一个公司的组结结构如下: 它的结构很像一棵树,其中人力资源部和财务部是没有子结点的,具体公司才有子结点. 而且最关健的是,它的每一层结构很相似. 代码实现如下: #include <iostream> #include <list> #include <string> using namespace std; //公司类,提供接口 class Company { public: Company(string name) { m_nam

二十三种设计模式之:组合(Composite)模式(部分&#183;整体模式)

组合(Composite)模式又叫部分·整体模式,主要用于实现树形结构. 例子如下: public class TreeNode{ private TreeNode parent; private String name; private List<TreeNode> children = new Arraylist<TreeNode>(); public TreeNode(String name){ this.name = name; } public String getNam

Java 实现组合(Composite)模式

类图 /** * 树 整体 * * @author stone * */ public class Tree { private TreeNode root; //根节点 public Tree(String name) { this.root = new TreeNode(name); } public TreeNode getRoot() { return root; } } /** * 树节点 部份 * 也可以自身代表树:一堆节点组成了一颗树 * * @author stone * */

Android与设计模式——组合(Composite)模式

定义(GoF<设计模式>):将对象组合成树形结构以表示"部分整体"的层次结构.组合模式使得用户对单个对象和组合对象的使用具有一致性.  涉及角色 1.Component:(View)是组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为.声明一个接口用于访问和管理Component子部件. 2.Leaf:(TextView.自定义View)在组合中表示叶子结点对象,叶子结点没有子结点(即不能在View内再添加View). 3.Composite:(ViewGr

组合(composite)模式

定义 将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性 组合模式(Composite)将小对象组合成树形结构,使用户操作组合对象如同操作一个单个对象.组合模式定义了"部分-整体"的层次结构,基本对象可以被组合成更大的对象,而且这种操作是可重复的,不断重复下去就可以得到一个非常大的组合对象,但这些组合对象与基本对象拥有相同的接口,因而组合是透明的,用法完全一致. 代码: #include <iostream>

Java设计模式透析之 —— 组合(Composite)

听说你们公司最近新推出了一款电子书阅读应用,市场反应很不错,应用里还有图书商城,用户可以在其中随意选购自己喜欢的书籍.你们公司也是对此项目高度重视,加大了投入力度,决定给此应用再增加点功能. 好吧,你也知道你是逃不过此劫了,没过多久你的leader就找到了你.他告诉你目前的应用对每本书的浏览量和销售量做了统计,但现在想增加对每个书籍分类的浏览量和销售量以及所有书籍总的浏览量和销售量做统计的功能,希望你可以来完成这项功能. 领导安排的工作当然是推脱不掉的,你只能硬着头皮上了,不过好在这个功能看起来

设计模式的征途—文章目录索引

1.预备篇 UML类图10分钟快速入门 2.创建型模式 ① 设计模式的征途-01.单例(Singleton)模式 ② 设计模式的征途-02.简单工厂(Simple Factory)模式 ③ 设计模式的征途-03.工厂方法(Factory Method)模式 ④ 设计模式的征途-04.抽象工厂(Abstract Factory)模式 ⑤ 设计模式的征途-05.原型(Prototype)模式 ⑥ 设计模式的征途-06.建造者(Builder)模式 3.结构型模式 ① 设计模式的征途-07.适配器(A

设计模式(九):Composite组合模式 -- 结构型模式

1. 概述 在数据结构里面,树结构是很重要,我们可以把树的结构应用到设计模式里面. 例子1:就是多级树形菜单. 例子2:文件和文件夹目录 2.问题 我们可以使用简单的对象组合成复杂的对象,而这个复杂对象有可以组合成更大的对象.我们可以把简单这些对象定义成类,然后定义一些容器类来存储这些简单对象.客户端代码必须区别对象简单对象和容器对象,而实际上大多数情况下用户认为它们是一样的.对这些类区别使用,使得程序更加复杂.递归使用的时候跟麻烦,而我们如何使用递归组合,使得用户不必对这些类进行区别呢? 3.

设计模式(七)组合模式Composite(结构型)

设计模式(七)组合模式Composite(结构型) 1. 概述 在数据结构里面,树结构是很重要,我们可以把树的结构应用到设计模式里面. 例子1:就是多级树形菜单. 例子2:文件和文件夹目录 2.问题 我们可以使用简单的对象组合成复杂的对象,而这个复杂对象有可以组合成更大的对象.我们可以把简单这些对象定义成类,然后定义一些容器类来存储这些简单对象.客户端代码必须区别对象简单对象和容器对象,而实际上大多数情况下用户认为它们是一样的.对这些类区别使用,使得程序更加复杂.递归使用的时候跟麻烦,而我们如何