设计模式之组合模式

设计模式之组合模式

引入

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

其文件目录格式为树形结构

设计模式之组合模式_第1张图片

  • 称文件为叶子,它不含有子节点;

  • 称文件夹为容器:容器成员为容器或者叶子。

初始方案

初始的解决方案如下:

public class ImageFile
{
    private readonly string _name;

    public ImageFile(string name)
    {
        _name = name;
    }

    public void KillVirus()
    {
        Console.WriteLine("对图像文件" + _name + "进行杀毒");
    }
}

public class TextFile
{
    private readonly string _name;

    public TextFile(string name)
    {
        _name = name;
    }
    public void KillVirus()
    {
        Console.WriteLine("对文本文件" + _name + "进行杀毒");
    }
}

public class Folder
{
    private readonly string _name;

    private readonly IList<Folder> _folderList = new List<Folder>();
    private readonly IList<ImageFile> _imageList = new List<ImageFile>();
    private readonly IList<TextFile> _textList = new List<TextFile>();

    public Folder(string name)
    {
        _name = name;
    }
    public void KillVirus()
    {
        Console.WriteLine("对文件夹" + _name + "进行杀毒");

        foreach(Folder f in _folderList)
        {
            f.KillVirus();
        }
        foreach(ImageFile image in _imageList)
        {
            image.KillVirus();
        }
        foreach (TextFile text in _textList)
        {
            text.KillVirus();
        }
    }

}

这样的方案存在如下问题:

  • 系统未提供抽象层,客户端必须显式地区别容器Folder、ImageFile、TextFile,导致系统无法对这三种类型的文件做统一处理。
  • 系统的灵活性、可扩展性差:如果需要增加新的类型,则需要修改原有代码
  • Folder类过于复杂,定义了所有类型的操作,存在大量冗余代码

这一切都是由于,没有提供抽象层。

我们用组合模式来改进上述方案。

改进方案

设计模式之组合模式_第2张图片

通过提供一个抽象层,从而使得系统可以统一 处理各种类,解决了上述问题。

组合模式

组合模式(Composite Pattern):

组合多个对象形成 树形结构 以表示具有“整体-部分”关系的层次结构。

在组合模式中 对单个对象(叶子)和组合对象(容器) 的使用具有一致性。

通过组合模式,可以较好的处理树形结构,它描述了如何将容器和叶子进行递归组合,使得客户端在使用时无需对容器、叶子进行区分,而是可以统一地进行处理。

使用要点:

  • 提供一个抽象类,既可以表示叶子也可以表示容器,系统针对该抽象类进行编程,从而可以统一处理叶子和容器
  • 容器和抽象类之间具有关联关系,容器中既可以包含容器,也可以包含叶子,以此实现递归组合,形成一个树形结构。
模式结构图

设计模式之组合模式_第3张图片

  • Component(抽象类):叶子和容器的统一抽象类,定义访问和管理叶子和容器的方法。
  • Leaf(叶子):叶子结点对象,无子节点。实现了抽象构件中定义的行为。
  • Composite(容器):容器结点对象,容器既包含容器,也可以叶子结点。提供一个集合存储抽象类(即容器和叶子)。同样实现了抽象类中定义的行为,并且在其业务方法中可以递归的调用子节点的业务方法。

代码如下:

/// 
/// 抽象类
/// 
public 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();
}

/// 
/// 叶子
/// 
public class Leaf : Component
{
    public override void Add(Component c)
    {
        //
    }
    public override void Remove(Component c)
    {
        throw new NotImplementedException();
    }
    public override Component GetChild(int i)
    {
        throw new NotImplementedException();
    }
    public override void Operation()
    {
        throw new NotImplementedException();
    }
}

/// 
/// 容器
/// 
public class Composite : Component
{
    private readonly IList<Component> _children = new List<Component>();

    public override void Add(Component c)
    {
        _children.Add(c);
    }
    public override void Remove(Component c)
    {
        _children.Remove(c);
    }
    public override Component GetChild(int i)
    {
        return _children[i];
    }
    public override void Operation()
    {
        foreach (Component c in _children)
        {
            c.Operation();
        }
    }
}

但是,这样的设计也存在着一些问题,每一次新增新类型,都需要实现抽象类中的方法(如Add()、Remove()等),比较麻烦,而且有的时候,我们并不需要其中的一些方法,比如:叶子结点没有子节点,不需要GetChild()方法

透明组合模式和安全组合模式

为了解决上述问题,在一定程度上简化代码,有了两种解决方案。

  • 透明组合模式

    将Add()、Remove()等方法在抽象类体中实现。

    缺点:不够安全。叶子 并不需要Add()、Remove()方法,运行阶段如果调用这些方法可能会导致错误。

  • 安全组合模式

    在抽象类体中不声明 任何用于访问和管理成员构件 的方法(如Add()、Remove()),只声明其他方法,如KillVirus()。

    缺点:,这样做使得,客户端不得不用容器类或者叶子类本身来实现这些方法,导致必须使用容器类或者叶子类来声明容器对象或者叶子对象,从而无法完全针对抽象编程。

总结

组合模式通过提供一个抽象类,使用关联关系,组合多个对象,从而可以较好地处理树形结构。

你可能感兴趣的:(软件工程)