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

    组合模式的定义:将对象组合成树形结构以表示“部分-整体”的层次结构。而客户端在使用时并不需要区分单个对象(叶子对象)和组合对象。

    我们来举一个简单的例子,模拟一下windows的文件系统,比如我们要遍历某一个盘符下的所有文件及文件夹,然后以树状结构输出文件及文件夹的名称。

    //先定义一个文件夹类
    public class Folder
    {  
         //文件夹的名称
         public string Name;    
         
         public Folder(string name)
         {
              Name=name;
         }
    
        //一个文件夹下可能有很多其他文件夹
        List<Folder> folders=new List<Folder>();
        
        //也可能有很多文件
        List<File> files=new List<File>();
        
        //向该文件夹下添加文件夹
        public void AddFolder(Folder folder)
        {
            folders.Add(folder);
        }
    
        //移出文件夹下的某个文件夹
        public void RemoveFolder(Folder folder)
        {
            folders.Remove(folder);
        }
        
        //向该文件夹下添加文件
        public void AddFile(File file)
        {
            files.Add(file);
        }
        
        //移出该文件夹下某个文件
        public void RemoveFile(File file)
        {
            files.Remove(file);
        }
        
        //显示该文件夹的名称及其下的文件夹、文件名称
        //文件夹名字前面加“+”,文件名字前面加"-"
        //每向里一层都会在前面加俩空格,用来展示层次结构
        //string的一个构造函数new string(char,num)表示输出num个字符
        public string ShowName(int depth)
        {
            //先显示自己的名字
            Console.WriteLine(new string(' ',depth)+“+”+Name);
            //下一层的名字前要多加俩空格
            depth=depth+2;
            //再显示旗下文件夹的名字
            foreach(var folder in folders)
            {
                folder.ShowName(depth);
            }
            //再显示旗下文件的名字
            foreach(var file in files)
            {
                file.ShowName(depth);
            }
        }
    }
    
    //文件类
    public class File
    {
        public string Name;
        public File(string name)
        {
            Name=name;
        }
        public void string ShowName(int depth)
        {
             Console.WriteLine(new string(' ',depth)+“-”+Name);
        }
    }
    
    //客户端
    public class Client
    {
        public static void Main(string[] args[])
        {
             Folder folder = new Folder("中外文化学习资料");
             Folder folder1_1 = new Folder("中日文化");
             Folder folder1_2 = new Folder("港台文化");
             File file1_1 = new File("傲慢与偏见.pdf");
             File file1_1_1 = new File("苍井空隐退大作.rmvb");
             File file1_2_1 = new File("艳照门.rar");
             folder.AddFolder(folder1_1);
             folder.AddFolder(folder1_2);
             folder.AddFile(file1_1);
             folder1_1.AddFile(file1_1_1);
             folder1_1.AddFile(file1_2_1);
             folder.ShowName(1);
        }
    }
    
    +中外文化学习资料
         +中日文化
              -苍井空隐退大作.rmvb
         +港台文化
              -艳照门.rar
         -傲慢与偏见.pdf

    虽然上述代码实现了功能,但可以看出必须区分叶子对象(File)与组合对象(Folder),在Folder与Client里都需要区别对待,区别对待组合对象与叶子对象,不仅让程序变得复杂,还对功能的扩展带来不便。比如现在叶子对象不仅有File,又加了一个其他什么叶子对象,部门不仅需要改Client的代码,还要在Folder里再加一个列表及对应的方法。

    所以结合组合模式的定义,这是一个“部分-整体”的树型结构,我们只需要保证对叶子对象与组合对象的操作的一致性就可以了。我们来看下其结构图:

    组合模式的关键就在于这个抽象类,它既可以代表叶子对象,也可以代表组合对象,这样客户端在操作的时候,不需要区分叶子对象和组合对象,而且在组合对象里也不需要区分。

    //由于要统一两种对象的操作,所以抽象类中的方法也主要是两种对象对外方法的和。
    //它里面既有叶子对象的方法,也有组合对象的方法。
    //常见做法是对于某些子类没有意义的方法,提供默认实现(或抛出异常)
    //这样如果子类需要这个方法,那就覆盖实现,不需要就默认父类的实现
    public abstract class AbstractClass
    {
        //子类的父类实例,也许有些功能会用到
        public AbstractClass parent{get;set;}
        
        //对所有子类都有的方法,定义成抽象方法
        public abstract showName(int depth);
        
         //对不是所有子类都有的方法,提供默认实现
        public add(AbstractClass a)
        {
            throw new UnsupportedOperationException("此对象不支持该方法");
        }
        public remove(AbstractClass a)
        {
            throw new UnsupportedOperationException("此对象不支持该方法");
        }
        ...
        
        public deleteMyself()
        {
            throw new UnsupportedOperationException("此对象不支持该方法");
        }
    
    }
    
    
    public class Folder:AbstractClass
    {  
         //文件夹的名称
         public string Name;    
         
         public Folder(string name)
         {
              Name=name;
         }
    
        //现在不需要有两个不同的列表了
        List<AbstractClass> childs=new List<AbstractClass>();
        
        //也不需要两类Add方法了
        public void Add(AbstractClass children)
        {
            childs.Add(children);
            //设置子类的父类实例,也许有些功能会用到
            children.parent=this;
        }
    
        //也不需要两类Remove方法了
        public void Remove(AbstractClass children)
        {
            //这里实现一个功能,就是将某一个节点删除掉的话,将这个节点下的子节点全部移到被删的节点的父节点下面
            foreach(var child in children.getChildren())
            {
                child.parent=this;
                this.childs.add(child);
            }
            childs.Remove(children);
        }
    
        public List<AbstractClass> getChildren()
        {
            return childs;
        }
             
        //显示该文件夹的名称及其下的文件夹、文件名称
        //文件夹名字前面加“+”,文件名字前面加"-"
        //每向里一层都会在前面加俩空格,用来展示层次结构
        //string的一个构造函数new string(char,num)表示输出num个字符
        public string ShowName(int depth)
        {
            //先显示自己的名字
            Console.WriteLine(new string(' ',depth)+“+”+Name);
            //下一层的名字前要多加俩空格
            depth=depth+2;
            //这里也不需要两个不同的循环了
            foreach(var children in childs)
            {
                children.ShowName(depth);
            }
         }
    }
    
    //文件类
    public class File
    {
        public string Name;
        public File(string name)
        {
            Name=name;
        }
        public void string ShowName(int depth)
        {
             Console.WriteLine(new string(' ',depth)+“-”+Name);
        }
        public void deleteMyself()
        {
        }
    }
    
    //客户端
    public class Client
    {
        public static void Main(string[] args[])
        {
             //客户端只需要Add就可以了不用区分不同的对象
             AbstractClass folder = new Folder("中外文化学习资料");
             AbstractClass folder1_1 = new Folder("中日文化");
             AbstractClass folder1_2 = new Folder("港台文化");
             AbstractClass file1_1 = new File("傲慢与偏见.pdf");
             AbstractClass file1_1_1 = new File("苍井空隐退大作.rmvb");
             AbstractClass file1_2_1 = new File("艳照门.rar");
             folder.Add(folder1_1);
             folder.Add(folder1_2);
             folder.Add(file1_1);
             folder1_1.Add(file1_1_1);
             folder1_1.Add(file1_2_1);
             folder.ShowName(1);
        }
    }
    
    +中外文化学习资料
         +中日文化
              -苍井空隐退大作.rmvb
         +港台文化
              -艳照门.rar
         -傲慢与偏见.pdf

    我们可以看到在Folder的showName方法里,类似递归,在设计上称作递归关联,与我们通常说的递归算法,递归算法是一个方法会调用方法自己,而这里是一个方法调用子类的同名方法,再在子类里调用子子类的同名方法而已。

    组合模式的本质:统一叶子对象和组合对象。


     

  • 相关阅读:
    云小课 | 搬迁本地数据至OBS,多种方式任你选
    云小课 | 磁盘容量不够用?小课教你来扩容!
    云图说 | 容器交付流水线ContainerOps,助力企业容器化转型
    云图说 | 华为云MCP多云容器平台,让您轻松灾备!
    【DevCloud · 敏捷智库】如何拆分用户故事
    十分钟从入门到精通(下)——OBS权限配置
    十分钟从入门到精通(上)——OBS权限配置
    云图说|全新华为云云备份服务:为您的数据提供三合一的保障
    费米问题——芝加哥有多少钢琴调音师?
    算法浅谈——人人皆知却很多人写不对的二分法
  • 原文地址:https://www.cnblogs.com/hanmeimei/p/4658260.html
Copyright © 2011-2022 走看看