将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
组合模式的使用类似于树的结构。在树结构中主要有两类对象,一个是子节点,一个是父节点。
父节点中可以包含子节点,而子节点之下则不能拥有父节点。(比较适用在递归结构中)
以文件的资料夹为例:
按照正常理解,单个文件如图片格式,文本格式,文件夹我们应该每个都独立的抽象出一个类,在他们的具体类中进行相应的操作,
但是如此一来功能类就会有些冗余。借鉴代码如下:
图片类
1 class ImageFile 2 { 3 private String name; 4 5 public ImageFile(String name) 6 { 7 this.name = name; 8 } 9 10 public void killVirus() { 11 //简化代码,模拟杀毒 12 Console.WriteLine("----对图像文件'" + name + "'进行杀毒"); 13 } 14 }
文本类
1 class TextFile 2 { 3 private String name; 4 5 public TextFile(String name) 6 { 7 this.name = name; 8 } 9 10 public void killVirus() { 11 //简化代码,模拟杀毒 12 Console.WriteLine("----对文本文件'" + name + "'进行杀毒"); 13 } 14 }
文件夹类
1 class Folder 2 { 3 private String name; 4 //定义集合folderList,用于存储Folder类型的成员 5 private List<Folder> folderList = new List<Folder>(); 6 //定义集合imageList,用于存储ImageFile类型的成员 7 private List<ImageFile> imageList = new List<ImageFile>(); 8 //定义集合textList,用于存储TextFile类型的成员 9 private List<TextFile> textList = new List<TextFile>(); 10 11 public Folder(String name) 12 { 13 this.name = name; 14 } 15 16 //增加新的Folder类型的成员 17 public void addFolder(Folder f) 18 { 19 folderList.Add(f); 20 } 21 22 //增加新的ImageFile类型的成员 23 public void addImageFile(ImageFile image) 24 { 25 imageList.Add(image); 26 } 27 28 //增加新的TextFile类型的成员 29 public void addTextFile(TextFile text) 30 { 31 textList.Add(text); 32 } 33 34 //需提供三个不同的方法removeFolder()、removeImageFile()和removeTextFile()来删除成员,代码省略 35 36 //需提供三个不同的方法getChildFolder(int i)、getChildImageFile(int i)和getChildTextFile(int i)来获取成员,代码省略 37 38 public void killVirus() { 39 Console.WriteLine("****对文件夹'" + name + "'进行杀毒"); //模拟杀毒 40 41 //如果是Folder类型的成员,递归调用Folder的killVirus()方法 42 foreach(Object obj in folderList) { 43 ((Folder)obj).killVirus(); 44 } 45 46 //如果是ImageFile类型的成员,调用ImageFile的killVirus()方法 47 foreach(Object obj in imageList) { 48 ((ImageFile)obj).killVirus(); 49 } 50 51 //如果是TextFile类型的成员,调用TextFile的killVirus()方法 52 foreach(Object obj in textList) { 53 ((TextFile)obj).killVirus(); 54 } 55 } 56 }
调用方法
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(); Console.Read();
对文件夹的病毒查杀也就这样完成了,但是现在看起来,文件夹类中的东西太多,对每一个类型的文件都有增,删,查,代码太过冗余。
系统没有提供抽象层,文件类和文件夹类需要当做两个对象对待。
灵活性太差,当需要增加或者改变文件类时需要大动干戈。
这时便能用上组合模式了!
组合模式的主要作用是将父节点和叶子节点当做同一种类型对待,将两者抽象出一种类,这样使用两者就有了一致性。
在组合模式结构图中包含如下几个角色:
● Component(抽象构件):
为组合中的对象声明接口。
在适当的情况下,实现所有类共有接口的缺省行为。
声明一个接口用于访问和管理Component的子组件。
(可选)在递归结构中定义一个接口,用于访问一个父部件,并在一个合适的情况下实现它。
● Leaf(叶子构件):它在组合结构中表示叶子节点对象,叶子节点没有子节点,它实现了在抽象构件中定义的行为。对于那些访问及管理子构件的方法,可以通过异常等方式进行处理。
● Composite(容器构件):它在组合结构中表示容器节点对象,容器节点包含子节点,其子节点可以是叶子节点,也可以是容器节点,它提供一个集合用于存储子节点,实现了在抽象构件中定义的行为,包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子节点的业务方法。
组合模式的关键是定义了一个抽象构件类,它既可以代表叶子,又可以代表容器,而客户端针对该抽象构件类进行编程,无须知道它到底表示的是叶子还是容器,可以对其进行统一处理。同时容器对象与抽象构件类之间还建立一个聚合关联关系,在容器对象中既可以包含叶子,也可以包含容器,以此实现递归组合,形成一个树形结构。
抽象出的类:
1 abstract class Component 2 { 3 public abstract void add(Component c); //增加成员 4 public abstract void remove(Component c); //删除成员 5 public abstract Component getChild(int i); //获取成员 6 public abstract void operation(); //业务方法 7 }
文本类
1 class TextFile:Component 2 { 3 private String name; 4 5 public TextFile(String name) 6 { 7 this.name = name; 8 } 9 public override void add(Component c) 10 { 11 Console.WriteLine("对不起,不支持该方法!"); 12 } 13 public override void remove(Component c) 14 { 15 Console.WriteLine("对不起,不支持该方法!"); 16 } 17 public override Component getChild(int i) 18 { 19 return null; 20 } 21 public override void operation() 22 { 23 Console.WriteLine("----对文本文件'" + name + "'进行杀毒"); 24 } 25 }
图片类
1 private String name; 2 public ImageFile(String name) 3 { 4 this.name = name; 5 } 6 public override void add(Component c) 7 { 8 Console.WriteLine("对不起,不支持该方法!"); 9 } 10 public override void remove(Component c) 11 { 12 Console.WriteLine("对不起,不支持该方法!"); 13 } 14 public override Component getChild(int i) 15 { 16 return null; 17 } 18 public override void operation() 19 { 20 Console.WriteLine("----对图片文件'" + name + "'进行杀毒"); 21 }
文件夹类
1 class Folder:Component 2 { 3 private List<Component> list = new List<Component>(); 4 5 private String name; 6 7 public Folder(String name) 8 { 9 this.name = name; 10 } 11 public override void add(Component c) 12 { 13 list.Add(c); 14 } 15 public override void remove(Component c) 16 { 17 list.Remove(c); 18 } 19 public override Component getChild(int i) 20 { 21 return (Component)list[i]; 22 } 23 public override void operation() 24 { 25 Console.WriteLine("****对文件夹'" + name + "'进行杀毒"); //模拟杀毒 26 27 //递归调用成员构件的killVirus()方法 28 foreach (Object obj in list) 29 { 30 ((Component)obj).operation(); 31 } 32 } 33 }
测试
1 //针对抽象构件编程 2 Component file1, file2, file3, file4, file5, folder1, folder2, folder3, folder4; 3 4 folder1 = new Folder("Sunny的资料"); 5 folder2 = new Folder("图像文件"); 6 folder3 = new Folder("文本文件"); 8 9 file1 = new ImageFile("小龙女.jpg"); 10 file2 = new ImageFile("张无忌.gif"); 11 file3 = new TextFile("九阴真经.txt"); 12 file4 = new TextFile("葵花宝典.doc"); 13 14 folder2.add(file1); 15 folder2.add(file2); 16 folder3.add(file3); 17 folder3.add(file4); 18 folder1.add(folder2); 19 folder1.add(folder3); 20 folder1.add(folder4); 21 22 //从“Sunny的资料”节点开始进行杀毒操作 23 folder1.operation(); 24 Console.ReadLine();
组合模式又分为透明组合模式与安全组合模式。
上述的组合模式为透明组合。将子节点与父节点的功能全部放入一个抽象类中,如果有一方(子节点或父节点)没有此功能,则使用错误处理代码。
安全组合模式则是将父节点与子节点的相同类抽象出来,而每个对象的特殊类在各自现实类中完成。
安全组合模式:
将相同功能抽象出来
1 abstract class AbstractFile { 2 public abstract void operation();
3 }
具体实现片段
1 AbstractFile file1,file2,file3,file4,file5; 2 Folder folder1,folder2,folder3,folder4; //不能透明处理容器构件 3 //其他代码省略
还是比较考虑优先使用透明组合安全模式。应为这样抽象的程度加大,便于后期维护。
学习于 https://www.cnblogs.com/lfxiao/p/6816026.html