zoukankan      html  css  js  c++  java
  • 设计模式——组合模式

    将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

    组合模式的使用类似于树的结构。在树结构中主要有两类对象,一个是子节点,一个是父节点。

    父节点中可以包含子节点,而子节点之下则不能拥有父节点。(比较适用在递归结构中)

    以文件的资料夹为例:

    按照正常理解,单个文件如图片格式,文本格式,文件夹我们应该每个都独立的抽象出一个类,在他们的具体类中进行相应的操作,

    但是如此一来功能类就会有些冗余。借鉴代码如下:

    图片类

     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

  • 相关阅读:
    缩点【洛谷P1262】 间谍网络
    模板-割点
    Tarjan缩点+LCA【洛谷P2416】 泡芙
    模拟赛 10-20考试记
    BFS【bzoj1667】: [Usaco2006 Oct]Cows on Skates滑旱冰的奶牛
    最短路【bzoj2464】: 中山市选[2009]小明的游戏
    linux /dev/mapper/centos-root 被占满
    Centos7中安装Mysql8并修改密码策略并远程连接
    Centos7中PHP编译安装mysqli扩展报错
    Linux中Composer 在安装依赖包与本地php版本不符问题
  • 原文地址:https://www.cnblogs.com/cwmizlp/p/9052477.html
Copyright © 2011-2022 走看看