zoukankan      html  css  js  c++  java
  • 迭代器模式和组合模式混用

    迭代器模式和组合模式混用

    前言

      园子里说设计模式的文章算得上是海量了,所以本篇文章所用到的迭代器设计模式和组合模式不提供原理解析,有兴趣的朋友可以到一些前辈的设计模式文章上学学,很多很有意思的。在Head First 设计模式这本书中,也有说迭代和组合模式混用的方法,但是使用的语言是JAVA,实现起来比起C#差异还是不少。本人在做几个C#小项目的时候需要用到树形结构,也看到了Head First 设计模式中混用迭代器和组合模式的例子,觉得如果能用C#实现,以后无疑会带来很大的帮助。下面就记录下实现过程,希望有不好的地方,各位前辈大力拍,晚生会努力改正,本文使用C#。

    效果与目标

      1.能自动遍历树形结构中的所有节点。

      2.对树中所有节点进行统一管理,统一到一个根节点上。

      举个例子:文件系统,有文件夹和文件这两个类型,形成一个文件系统树形结构,使用了本文章所说的混用模式以后,能轻松对每一个结点进行自动处理,最简单的就是打印各自的名字和子文件系统或排序等。

    实现过程

      下面以文件系统为例,可以根据需要灵活改变,主要讲得是思想而已。

        新建一个C#控制台,随便改个名字。

        搭建类和接口(实现组合模式)。

          1.IFileSystemNode(文件系统接口):

    interface IFileSystemNode
        {
            String Name { set; get; }
            String Path { set; get; }
            List<IFileSystemNode> Children { set; get; }
            IEnumerator getIterator();
            void ShowName();
        }
    View Code

          2.FolderNode(文件夹节点类):

    class FolderNode : IFileSystemNode
        {
            public FolderNode(String name, String path)
            {
                this.Name = name;
                this.Path = path;
            }
            private string _name;
            public string Name
            {
                get
                {
                    return _name;
                }
                set
                {
                    _name = value;
                }
            }
    
            private string _path;
            public string Path
            {
                get
                {
                    return _path;
                }
                set
                {
                    _path = value;
                }
            }
    
            private List<IFileSystemNode> _children = new List<IFileSystemNode>();
            public List<IFileSystemNode> Children
            {
                get
                {
                    return _children;
                }
                set
                {
                    _children = value;
                }
            }
    
            public void ShowName()
            {
                Console.WriteLine(Name);
            }
    
    
            public System.Collections.IEnumerator getIterator()
            {
                throw new NotImplementedException();
            }
        }
    View Code

          3.FileNode(文件节点类):

    class FileNode : IFileSystemNode
        {
            public FileNode(String name, String path)
            {
                this.Name = name;
                this.Path = path;
            }
            private string _name;
            public string Name
            {
                get
                {
                    return _name;
                }
                set
                {
                    _name = value;
                }
            }
    
            private string _path;
            public string Path
            {
                get
                {
                    return _path;
                }
                set
                {
                    _path = value;
                }
            }
    
            public List<IFileSystemNode> Children
            {
                get
                {
                    throw new NotImplementedException();
                }
                set
                {
                    throw new NotImplementedException();
                }
            }
    
            public void ShowName()
            {
                Console.WriteLine(Name);
            }
    
    
            public System.Collections.IEnumerator getIterator()
            {
                throw new NotImplementedException();
            }
        }
    View Code

                     注意:以上实现了组合模式,其中getIterator方法是还没有代码的,这个需要使用迭代器模式。

        编写迭代器(实现迭代器模式)。

        在FolderNode中有子节点,但是在FileNode中没有子节点,但是因为使用了组合器模式,对于FolderNode和FileNode,都需要实现相同的契(IFileSystemNode)。所以在FolderNode中返回的迭代器是用来迭代子节点,而FileNode则不同,它根本没有子节点,那么就使用一个叫做空迭代器来满足契约。

             下面分别实现具体迭代器(继承C#系统提供的IEnumerator接口)。

         1.为FolderNode提供具体迭代器:FolderNodeIterator

    class FolderNodeIterator : IEnumerator
        {
            private IEnumerable<IFileSystemNode> fileSystems { set; get; }
            public FolderNodeIterator(IEnumerable<IFileSystemNode> _fileSystems)
            {
                this.fileSystems = _fileSystems;
            }
            private int currentIndex = -1;
            public object Current
            {
                get { return fileSystems.ElementAt(currentIndex); }
            }
    
            public bool MoveNext()
            {
                if (currentIndex < fileSystems.Count() - 1)
                {
                    currentIndex++;
                    return true;
                }
                return false;
            }
    
            public void Reset()
            {
                currentIndex = -1;
            }
        }
    View Code

      新建完类以后,我们修改下FolderNode类中的getIterator方法,返回一个FolderNodeIterator实例,代码如下:

    public IEnumerator getIterator()
            {
                return new FolderNodeIterator(_children);
            }

          2.为FileNode提供具体迭代器:EmptyIterator

    class EmptyIterator : IEnumerator
        {
    
            public object Current
            {
                get { return null; }
            }
    
            public bool MoveNext()
            {
                return false;
            }
    
            public void Reset()
            {
    
            }
        }
    View Code

      新建完类以后,我们同样修改下FileNode类中的getIterator方法,返回一个EmptyIterator实例,代码如下:

    public IEnumerator getIterator()
            {
                return new EmptyIterator();
            }

      

      使用

      好像差不多了,我们使用使用一下这个模型:

        为了添加真实性,本演示映射了真实文件系统,请在Programe类上添加一个静态方法:Convert

    private static void Convert(FolderNode root, String directoryPath)
            {
                foreach (var filesystempath in Directory.EnumerateFiles(directoryPath))
                {
                    if (root.Children != null)
                    {
                        root.Children.Add(new FileNode(Path.GetFileName(filesystempath), filesystempath));
                    }
                }
    
                foreach (var filesystempath in Directory.EnumerateDirectories(directoryPath))
                {
                    FolderNode newFolderNode = new FolderNode(Path.GetFileName(filesystempath), filesystempath);
                    root.Children.Add(newFolderNode);
                    Convert(newFolderNode, filesystempath);
                }
            }
    View Code

        然后我们在Main方法中就能先映射,再使用,来看一下Main方法,其中使用了迭代器模式去迭代各个子项:

    static void Main(string[] args)
            {
                FolderNode root = new FolderNode(@"酷狗音乐", @"C:UsersAdministratorDesktop\酷狗音乐");
                Convert(root, @"C:UsersAdministratorDesktop\酷狗音乐");
    
                IEnumerator Myiterator = root.getIterator();
                while (Myiterator.MoveNext())
                {
                    IFileSystemNode ctextIterator = (IFileSystemNode)Myiterator.Current;
                    ctextIterator.ShowName();
                }
                Console.ReadKey();
            }

        我映射了桌面上的一个文件,看看文件结构:

          有一个hozin文件夹(里面有3首歌),一个L'theme文件夹(里面有一首歌),一个文件(08开头的歌)。

        好了,运行程序,看看结果:,好像有点问题,为什么只有一个层的呢?也就是说,为什么只迭代了root节点的一层子节点,子节点的子节点还没有遍历完,这是因为FolderNodeIterator的迭代只提供了一层迭代,而不会深层迭代。所以如果想要深层迭代,使用Head First设计模式中的思想就是:提供一个组合迭代器,用来包装FolderNodeIterator,提供深层迭代功能。下面是我在C#中的实现代码,添加这样一个类:CompositeIterator

    class CompositeIterator : IEnumerator
        {
            public CompositeIterator(IEnumerator ienumerator)
            {
                temp.Push(ienumerator);
                root = ienumerator;
            }
            IEnumerator root;
            Stack<IEnumerator> temp = new Stack<IEnumerator>();
            private IFileSystemNode _current;
    
            public object Current
            {
                get { return _current; }
            }
    
            public bool MoveNext()
            {
                if (temp.Count <= 0)
                {
                    return false;
                }
                if (temp.Peek().MoveNext())
                {
                    _current = temp.Peek().Current as IFileSystemNode;
                    IEnumerator ienumerator = _current.getIterator();
                    if (ienumerator.MoveNext())
                    {
                        ienumerator.Reset();
                        temp.Push(ienumerator);
                    }
                }
                else
                {
                    temp.Pop();
                    MoveNext();
                }
                if (temp.Count <= 0)
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }
    
            public void Reset()
            {
                temp.Clear();
                temp.Push(root);
            }
        }
    View Code

      然后,修改FolderNode类中的getIterator方法如下:

    public System.Collections.IEnumerator getIterator()
            {
                return new CompositeIterator(new FolderNodeIterator(this._children));
            }

      包装完以后,再次运行:。OK大功告成

    总结

        为了适应真实项目,可以改的点有很多:

          1.这里只有FolderNode和FileNode,在实际项目中可能不止,可以添加,只要继承同一个接口(这里是IFileSystemNode),那么就能像皇帝发号命令一样命令所有子子孙孙做事情了(在root节点上调用IFileSystemNode上声明有的方法)。

          2.添加契约方法,例如像要对每一个节点进行排序Sort,直接添加一个方法,然后子孙各自实现方法,最后调用root节点的Sort。

          3.这里的遍历使用前序遍历,遍历树的所有节点,但是也可以使用您喜欢的方式,只要修改组合迭代器。

                                       源码下载

  • 相关阅读:
    CF149D Coloring Brackets
    CF508D
    CF483C Diverse Permutation
    【纪念】我写过几乎最长的代码
    .net core图片上传详解
    layui插件croppers的使用
    关于日常操作中sql的性能
    leeCode 278
    leeCode刷题 1078
    leeCode刷题 lc184
  • 原文地址:https://www.cnblogs.com/Jarvin/p/3735460.html
Copyright © 2011-2022 走看看