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

    面向对象编程中,对象之间存在着种种关系,比如has-a, has-a关系又叫组合关系,比如公司和部门之间,集体和个人之间等等。公司一般都会有呈树状的组织架构,有时候,希望使用树状结构来表达整体和部分之间的关系,使客户能够不加以区分地处理结构中的每一个对象,这时候就会用到----组合模式


    1.组合模式

    组合模式(Composite pattern) 描述一组对象,这些对象以相同方式作为同一类对象的单个实例进行处理。

     组合模式的意图在于一致地处理整体和部分,所以,并不一定只要是hasA关系就可以使用组合模式的,只有“组件”和“零件”之间存在类似的行为时且具有明显的树形结构时,使用组合模式才是合适的。以下是组合模式的UML类图:

                 

    上图中,Client直接面向抽象接口Component来处理元素,不必区分到底是Leaf节点还是Composite节点。

    Leaf节点: 叶子结点, 没有子节点,直接实现抽象接口Component的display()方法,作为终止方法;

    Composite节点:复合节点,有一个或多个子节点,它持有一个列表来保存其子节点,将请求转发给每一个子节点。

    组合模式的树形结构,与解释器模式类似,参考解释器模式


    2.透明模式和安全模式

    透明模式:Component中定义子节点相关操作的抽象方法(add、remove等),使客户端一致地处理Leaf和Composite对象;但是由于Leaf节点没有子节点,所以这种方式类型安全问题需要注意。

    安全模式:子节点相关操作的方法(add,remove等),只定义在Composite对象中,保证了类型安全;但是需要客户端分别处理两种对象,也就失去了一致性。

     一般,相比类型安全来说,组合模式更加强调一致性

    下面的例子使用透明模式为例。 


    3.实现XML文档创建

    3.1 设计

    组合模式一般应用于有明显层级结构的对象上,比如公司的组织架构,目录结构等等。XML(可扩展标记语言)是开发中常常用来作为配置文件来使用,本例中我尝试应用组合模式来实现一个简单的XML结构的文档,文档根元素名称为root, 子元素可随意命名,子元素可包含文本,结构如下:

    <root>
      <A>
        <A1>
          组合模式就是好!
        <A1/>
      <A/>
      <B>
        对待每个对象都一样!
      <B/>
    <root/>

    文档有明显的层次结构,元素可分为两类:1.复合元素(包活根元素), 2.文本,  设计类图如下:

    DocumentHelper是一个工具类,提供了一系列静态方法来创建、修改、打印文档。

    3.2代码实现

    文档抽象类, 使用透明模式,定义了子节点相关的方法add,remove, 定义子类统一需要实现的方法display(int indent), indent是为了打印美观而设置的缩进。

    abstract class Document {
    
        public abstract void add(Document document);
        public abstract void remove(Document document);
        public abstract String display(int indent);
    }

    叶子结点---文本元素的实现, 注意到文本对象由于没有子节点,所以add,remove方法不支持;

    display方法返回该文本元素的文本字符串,包含缩进。

    class Text extends Document {
    
        private String content;
        public Text(String content) {
            this.content = content;
        }
    
        @Override
        public void add(Document document) {
            throw new UnsupportedOperationException("不支持次操作");
        }
    
        @Override
        public void remove(Document document) {
            throw new UnsupportedOperationException("不支持次操作");
        }
    
        @Override
        public String display(int indent) {
            StringBuilder builder = new StringBuilder();
            //缩进距离
            for (int i = 0; i < indent; i++) {
                builder.append(" ");
            }
            //叶子元素直接输出内容
            builder.append(content + "
    ");
            return builder.toString();
        }
    }

    复合节点对象实现,Element对象包含子节点,维护一个List,存放其子节点,并重写add,remove方法来设置子节点;

    display方法中,实现了<E>xxxx</E>这种形式的结构,实现方式是在<></>之间,调用了其子节点的display方法。

    class Element extends Document {
    
        private String name;
    
        private List<Document> documents = new ArrayList<>();
    
        public Element(String name) {
            this.name = name;
        }
    
        @Override
        public void add(Document document) {
            documents.add(document);
        }
    
        @Override
        public void remove(Document document) {
            documents.remove(document);
        }
    
        @Override
        public String display(int indent) {
            StringBuilder builder = new StringBuilder();
            //缩进距离
            for (int i = 0; i < indent; i++) {
                builder.append(" ");
            }
            //元素开始标签
            builder.append("<" + name + ">
    ");
            //子元素
            for (Document document : documents) {
                builder.append(document.display(indent + 2));
            }
            //结束标签缩进距离
            for (int i = 0; i < indent; i++) {
                builder.append(" ");
            }
            //元素结束标签
            builder.append("<" + name + "/>
    ");
            return builder.toString();
        }
    }

     工具类,帮助生成、修改、打印文档的元素

    class DocumentHelper {
    
        public static Document createRootDocument() {
            Document root = new Element("root");
            return root;
        }
    
        public static Document createElement(String name) {
            return new Element(name);
        }
    
        public static Document createText(String content) {
            return new Text(content);
        }
    
        public static void printDocument(Document root) {
            String xml = root.display(0);
            System.out.println(xml);
        }
    }

    客户端生成文档并打印:

    public class MyXml {
        public static void main(String[] args) {
            //创建根元素
            Document root = DocumentHelper.createRootDocument();
            //创建两个子元素A合B
            Document a = DocumentHelper.createElement("A");
            Document b = DocumentHelper.createElement("B");
            //存入子元素到根元素
            root.add(a);
            root.add(b);
            //在子元素A下再创建一个子元素A1
            Document a1 = DocumentHelper.createElement("A1");
            a.add(a1);
            //在子元素A1下创建文本
            Document text = DocumentHelper.createText("组合模式就是好!");
            a1.add(text);
            //在B元素下创建文本
            Document text2 = DocumentHelper.createText("对待每个对象都一样!");
            b.add(text2);
            //显示xml
            DocumentHelper.printDocument(root);
        }
    }

    输出结果

    <root>
      <A>
        <A1>
          组合模式就是好!
        <A1/>
      <A/>
      <B>
        对待每个对象都一样!
      <B/>
    <root/>

     多亏了多态,树形结构中的每一个对象都能够以相同的处理方式对待,因为我们只面向接口而不依赖实现,这是依赖倒置原则的体现啊。


    4.总结

    组合模式解决一下问题: 

    • 怎样表示整体-部分层级结构,从而使客户能一致地作用于层级中的每个对象;
    • 整体-部分层级结构如何表示成树状结构。

    具体实现的方式:

    • 定义一个Component接口,Leaf和Composite实现, Composite中维护一个List用于保存子节点。
    • Leaf对象直接实现客户请求,Composite对象将请求转发给子节点。

    在透明模式和安全模式的选择中,组合模式更加强调一致性,从而透明模式更具有“普世价值”。

  • 相关阅读:
    Jquery 验证 validate
    JQuery的父、子、兄弟节点查找,节点的子节点循环
    i386、i586、i686、noarch、x86_64
    Java 遍历类中的属性
    页面的缓存与不缓存设置
    JavaScript 判断输入是否为中文的函数
    检查radio/checkbox是否至少选择一项
    JavaScript 检查是否是数字
    JavaScript 检查IP
    Javascript 身份证号获得出生日期、获得性别、检查身份证号码
  • 原文地址:https://www.cnblogs.com/yxlaisj/p/10432715.html
Copyright © 2011-2022 走看看