设计模式之组合模式
引入
Sunny 软件公司欲开发一个杀毒(AntiVirus)软件,该软件既可以对某个文件夹(Folder)杀毒,也可以对某个指定的文件(File)进
行杀毒。
该杀毒软件还可以根据各类文件的特点,为不同类型的文件提供不同的杀毒方式,例如图像文件(ImageFile)和文本文件(TextFile)的杀毒方式就有所差异。现需要提供该杀毒软件的整体框架设计方案。
其文件目录格式为树形结构:
-
称文件为叶子,它不含有子节点;
-
称文件夹为容器:容器成员为容器或者叶子。
初始方案
初始的解决方案如下:
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类过于复杂,定义了所有类型的操作,存在大量冗余代码
这一切都是由于,没有提供抽象层。
我们用组合模式来改进上述方案。
改进方案
通过提供一个抽象层,从而使得系统可以统一 处理各种类,解决了上述问题。
组合模式
组合模式(Composite Pattern):
组合多个对象形成 树形结构 以表示具有“整体-部分”关系的层次结构。
在组合模式中 对单个对象(叶子)和组合对象(容器) 的使用具有一致性。
通过组合模式,可以较好的处理树形结构,它描述了如何将容器和叶子进行递归组合,使得客户端在使用时无需对容器、叶子进行区分,而是可以统一地进行处理。
使用要点:
- 提供一个抽象类,既可以表示叶子也可以表示容器,系统针对该抽象类进行编程,从而可以统一处理叶子和容器
- 容器和抽象类之间具有关联关系,容器中既可以包含容器,也可以包含叶子,以此实现递归组合,形成一个树形结构。
模式结构图
- Component(抽象类):叶子和容器的统一抽象类,定义访问和管理叶子和容器的方法。
- Leaf(叶子):叶子结点对象,无子节点。实现了抽象构件中定义的行为。
- Composite(容器):容器结点对象,容器既包含容器,也可以叶子结点。提供一个集合存储抽象类(即容器和叶子)。同样实现了抽象类中定义的行为,并且在其业务方法中可以递归的调用子节点的业务方法。
代码如下:
/// <summary>
/// 抽象类
/// </summary>
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();
}
/// <summary>
/// 叶子
/// </summary>
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();
}
}
/// <summary>
/// 容器
/// </summary>
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()。
缺点:,这样做使得,客户端不得不用容器类或者叶子类本身来实现这些方法,导致必须使用容器类或者叶子类来声明容器对象或者叶子对象,从而无法完全针对抽象编程。
总结
组合模式通过提供一个抽象类,使用关联关系,组合多个对象,从而可以较好地处理树形结构。
发布评论