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

    组合模式

    组合模式是一种结构型设计模式,它的使用形式比较固定,适合用来表示树形结构,或者说具有层级结构的数据。

    比如目录和文件,xml等。

    What?

    将一组对象组织(Compose)成树形结构,以表示一种“部分 - 整体”的层次结构。组合让客户端(在很多设计模式书籍中,“客户端”代指代码的使用者。)可以统一单个对象和组合对象的处理逻辑。

    How?

    练习1:如何统计目录下的文件数?

    以目录和文件为例,给定一个需求:想要统计目录下有多少文件。

    文件结构:

    如何实现?

    首先,我们抽象出一个FileSystemNode.class类,统一表示文件和目录

    public abstract class FileSystemNode {
        private String path;
    
        public FileSystemNode(String path) {
            this.path = path;
        }
    
        public String getPath() {
            return path;
        }
        /**
         * 统计指定目录下的文件个数
         *
         * @return
         */
        public abstract int countNumOfFiles();
    
    }
    

    再定义文件类

    public class File extends FileSystemNode {
        public File(String path) {
            super(path);
        }
    
        @Override
        public int countNumOfFiles() {
            return 1;
        }
    }
    

    文件时叶子节点,统计文件的个数,直接返回1即可。

    最后定义目录类

    public class Directory extends FileSystemNode {
        private List<FileSystemNode> subNodes = new ArrayList<>();
    
        public Directory(String path) {
            super(path);
        }
    
        @Override
        public int countNumOfFiles() {
            int numOfFiles = 0;
            for (FileSystemNode subNode : subNodes) {
                numOfFiles += subNode.countNumOfFiles();
            }
            return numOfFiles;
        }
    
        public void addSubNode(FileSystemNode fileOrDir) {
            subNodes.add(fileOrDir);
        }
    	/**
    	* 删除子节点
    	*/
        public void removeSubNode(FileSystemNode fileOrDir) {
            int size = subNodes.size();
            int i = 0;
            for (; i < size; ++i) {
                if (subNodes.get(i).getPath().equalsIgnoreCase(fileOrDir.getPath())) {
                    break;
                }
            }
            if (i < size) {
                subNodes.remove(i);
            }
        }
    }
    

    目录节点根节点,它维护一个list来存子节点,子节点也是FileSystemNode类。

    在统计文件个数countNumOfFiles的时候,如果是文件节点,直接遍历其子节点的countNumOfFiles方法即可。

    根节点还维护了添加子节点和删除子节点的方法。

    客户端调用

    public static void main(String[] args) {
            /**
             * /
             * /wz/
             * /wz/a.txt
             * /wz/b.txt
             * /wz/movies/
             * /wz/movies/c.avi
             * /xzg/
             * /xzg/docs/
             * /xzg/docs/d.txt
             */
            Directory fileSystemTree = new Directory("/");
            Directory node_wz = new Directory("/wz/");
            Directory node_xzg = new Directory("/xzg/");
            fileSystemTree.addSubNode(node_wz);
            fileSystemTree.addSubNode(node_xzg);
    
            File node_wz_a = new File("/wz/a.txt");
            File node_wz_b = new File("/wz/b.txt");
            Directory node_wz_movies = new Directory("/wz/movies/");
            node_wz.addSubNode(node_wz_a);
            node_wz.addSubNode(node_wz_b);
            node_wz.addSubNode(node_wz_movies);
    
            File node_wz_movies_c = new File("/wz/movies/c.avi");
            node_wz_movies.addSubNode(node_wz_movies_c);
    
            Directory node_xzg_docs = new Directory("/xzg/docs/");
            node_xzg.addSubNode(node_xzg_docs);
    
            File node_xzg_docs_d = new File("/xzg/docs/d.txt");
            node_xzg_docs.addSubNode(node_xzg_docs_d);
    
            System.out.println("/ files num:" + fileSystemTree.countNumOfFiles());
            System.out.println("/wz/ files num:" + node_wz.countNumOfFiles());
        }
    

    输出

    / files num:4
    /wz/ files num:3
    

    组合模式把具有层级关系的树节点用一个统一的数据结构表示(FileSystemNode),方便扩展(扩展新的类只要继承或者实现统一的抽象即可),代码可读性强。

    练习2:用树结构表示xml或者html

    首先抽象出一个节点的概念,表示xml的标签,它可以统一表示xml的标签,文本或者注释

    public interface Node {
        String toXml();
        Node addNode(Node node);
    }
    

    文本的表示:

    public class TextNode implements Node {
        private String text;
    
        public TextNode(String text) {
            this.text = text;
        }
    
        public String getText() {
            return text;
        }
    
        @Override
        public String toXml() {
            return text + "
    ";
        }
    
        @Override
        public Node addNode(Node node) {
            throw new UnsupportedOperationException();
        }
    }
    

    文本的toXml,直接返回自身的text内容即可;文本节点不能再addNode,抛异常

    标签的表示:

    public class ElementNode implements Node {
        private String name;
        private List<Node> childList = new ArrayList<>();
    
        public ElementNode(String name) {
            this.name = name;
        }
    
        public String getNodeName() {
            return this.name;
        }
    
        @Override
        public String toXml() {
            StringBuilder builder = new StringBuilder();
            builder.append("<").append(this.name).append(">
    ");
            for (Node child : this.childList) {
                builder.append(child.toXml());
            }
            builder.append("</").append(this.name).append(">
    ");
            return builder.toString();
        }
    
        @Override
        public Node addNode(Node node) {
            if (null == node) {
                return null;
            }
            this.childList.add(node);
            return this;
        }
    }
    

    根节点需要维护一个子节点的list。 标签的toXml方法直接遍历这个list节点的toXml方法即可。

    客户端调用:

    public class Test {
        public static void main(String[] args) {
            ElementNode root = new ElementNode("root");
            ElementNode html = new ElementNode("html");
            ElementNode head = new ElementNode("head");
            ElementNode body = new ElementNode("body");
            TextNode textNode = new TextNode("This is a text!");
            body.addNode(textNode);
            html.addNode(head);
            html.addNode(body);
            root.addNode(html);
            String s = root.toXml();
            System.out.println(s);
        }
    }
    

    输出:

    <root>
    <html>
    <head>
    </head>
    <body>
    This is a text!
    </body>
    </html>
    </root>
    

    如果想增加一个注释节点(CommentNode),只需要实现Node接口即可

    总结

    组合模式适合于树结构的表示,能够方便树结构的操作(遍历),能够提供更好的扩展性,且代码更加易读。Tomcat的多级容器实现的Container接口也是组合模式的实现。

  • 相关阅读:
    子程序的设计
    多重循环程序设计
    汇编语言的分支程序设计与循环程序设计
    代码调试之串口调试2
    毕昇杯模块之光照强度传感器
    毕昇杯之温湿度采集模块
    【CSS】盒子模型 之 IE 与W3C的盒子模型对比
    【css】盒子模型 之 概述
    【css】盒子模型 之 弹性盒模型
    【网络】dns_probe_finished_nxdomain 错误
  • 原文地址:https://www.cnblogs.com/SimonZ/p/14546265.html
Copyright © 2011-2022 走看看