组合模式小试

组合模式是一种功能比较单一的设计模式,一般与其他设计模式搭配使用。本篇简单模拟了一下自动构建xml文件的小程序。

转载请注明出处http://www.cnblogs.com/zrtqsk/p/3725154.html,谢谢!

一、介绍

  还是先来看一下《研磨设计模式》的介绍——将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

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

  什么是组合模式呢?说白了,就是用一个抽象类把对象的整体和部分的操作统一起来。在外部看来,整个对象无论是整体还是部分,操作都是一样的。最常见使用组合模式的譬如我们的数据结构——树。树有3种模块,分别是根、枝、叶。这3个都分别有各自的特征。我们就可以用组合模式构建一个抽象类,将3者一样的操作在抽象类中实现,不一样的操作构建成抽象方法留给子类实现。这样在外界看来,整棵树自成一体,无须区分哪个是根、哪个是枝、叶。

 

二、我的实现

  在这里,我们利用组合模式来做一个自动构建xml文件的小程序,也算是简单模拟了一下dom4j的功能。

  xml文件分别有哪些部分组成?完整的xml文件,最顶端是xml声明,下面是xml的处理指令,然后下面是文档类型声明,然后下面是一个根元素,根元素包括了一个或多个子元素。而作为元素而言,又包括了元素内容、属性键值对等等。同时,xml注释可以在任意地方添加。

  通常简单的xml文件只包括了xml声明、根元素(包含各种内容)以及xml注释。

  由于xml声明只出现一次,可以在总的构建类中再处理。这里我们主要处理元素和xml注释。因为注释可以在任何地方添加,也可以抽象为元素的一种。

1、组合模式最麻烦的,就是这个抽象类,下面是我的抽象类,包含了元素和xml注释所有可能的方法:

 1 package composite.qsk;
 2 
 3 import java.util.List;
 4 
 5 public abstract class AbstractElement {
 6 
 7     // 内容(包含在"<X>"和"</X>"之间的部分,没有用“<”和“>”括起来)
 8     protected String content = null;
 9 
10     // 元素层,根层数为0,其子元素层数为1,依次
11     private int level = 0;
12 
13     // 抽象方法、得到本元素所有内容(类似“<a c=d>efgh </a>”)
14     public abstract String getAllContent();
15 
16     public List<XMLElement> getChildElements()
17     {
18         throw new UnsupportedOperationException("对象不支持此功能");
19     }
20 
21     public int getLevel()
22     {
23         return level;
24     }
25 
26     public void setLevel(int level)
27     {
28         this.level = level;
29     }
30 
31     // 设置本元素内容
32     public boolean setContent(String content)
33     {
34         boolean flag = false;
35         if (content != null)
36         {
37             this.content = content;
38             flag = true;
39         }
40         return flag;
41     }
42 
43     // 得到本元素的内容
44     public String getContent()
45     {
46         return content;
47     }
48 
49     // 增加属性
50     public boolean addAttribute(XMLAttribute xmlAttribute)
51     {
52         throw new UnsupportedOperationException("对象不支持此功能");
53     }
54 
55     public boolean addAttribute(String aName, String aValue)
56     {
57         throw new UnsupportedOperationException("对象不支持此功能");
58     }
59 
60     // 根据属性名移除属性
61     public boolean removeAtrribute(String name)
62     {
63         throw new UnsupportedOperationException("对象不支持此功能");
64     }
65 
66     // 增加子元素
67     public boolean addElement(AbstractElement xmlElement)
68     {
69         throw new UnsupportedOperationException("对象不支持此功能");
70     }
71 
72     // 根据索引移除子元素
73     public AbstractElement removeElement(int index)
74     {
75         throw new UnsupportedOperationException("对象不支持此功能");
76     }
77 
78     // 根据索引得到子元素
79     public AbstractElement getElement(int index)
80     {
81         throw new UnsupportedOperationException("对象不支持此功能");
82     }
83 
84     // 得到本元素元素名
85     public String getName()
86     {
87         throw new UnsupportedOperationException("对象不支持此功能");
88     }
89 
90     // 设置元素名
91     public boolean setName(String elementName)
92     {
93         throw new UnsupportedOperationException("对象不支持此功能");
94     }
95 }

如上,由于我们将注释定位为元素的一种,那么原本一些元素有的方法,注释都是不支持的,那么注释操作这些方法时,就会自动调用父类的方法,抛出异常UnsupportedOperationException("对象不支持此功能")

2、接下来就是我们的元素类,继承了AbstractElement,当然要重写里面的很多方法,如下:

  1 package composite.qsk;
  2 
  3 import java.util.ArrayList;
  4 import java.util.List;
  5 
  6 public class XMLElement extends AbstractElement {
  7 
  8     // 元素名
  9     private String name = null;
 10     // 子元素列表
 11     protected List<AbstractElement> childElements = null;
 12     // 子属性列表
 13     protected List<XMLAttribute> attributes = null;
 14 
 15     public XMLElement(String name)
 16     {
 17         this.name = name;
 18     }
 19 
 20     // 增加子元素
 21     public boolean addElement(AbstractElement xmlElement)
 22     {
 23         boolean flag = false;
 24         if (childElements == null)
 25         {
 26             childElements = new ArrayList<AbstractElement>();
 27         }
 28         if (xmlElement != null)
 29         {
 30             xmlElement.setLevel(this.getLevel() + 1);
 31             childElements.add(xmlElement);
 32             flag = true;
 33         }
 34         return flag;
 35     }
 36 
 37     // 根据索引移除子元素
 38     public AbstractElement removeElement(int index)
 39     {
 40         if (childElements == null)
 41         {
 42             return null;
 43         }
 44         return childElements.remove(index);
 45     }
 46 
 47     // 根据索引得到子元素
 48     public AbstractElement getElement(int index)
 49     {
 50         if (childElements == null)
 51         {
 52             return null;
 53         }
 54         return childElements.get(index);
 55     }
 56 
 57     // 得到本元素元素名
 58     public String getName()
 59     {
 60         return name;
 61     }
 62 
 63     // 设置元素名
 64     public boolean setName(String elementName)
 65     {
 66         boolean flag = false;
 67         if (elementName != null)
 68         {
 69             this.name = elementName;
 70             flag = true;
 71         }
 72         return flag;
 73     }
 74 
 75     // 增加属性
 76     public boolean addAttribute(XMLAttribute attribute)
 77     {
 78         boolean flag = true;
 79         if (attributes == null)
 80         {
 81             attributes = new ArrayList<XMLAttribute>();
 82         }
 83         if (attribute != null)
 84         {
 85             flag = attributes.add(attribute);
 86         }
 87         return flag;
 88     }
 89 
 90     public boolean addAttribute(String name, String value)
 91     {
 92         return addAttribute(new XMLAttribute(name, value));
 93     }
 94 
 95     // 根据属性名移除属性
 96     public boolean removeAtrribute(String name)
 97     {
 98         boolean flag = false;
 99         if (attributes == null)
100         {
101             return false;
102         }
103         if (name != null)
104         {
105             for (XMLAttribute a : attributes)
106             {
107                 if (a.getAttributeName().equals(name))
108                 {
109                     attributes.remove(a);
110                     flag = true;
111                     return flag;
112                 }
113             }
114         }
115         return flag;
116     }
117 
118     // 得到元素所有内容
119     @Override
120     public String getAllContent()
121     {
122         StringBuilder allContent = new StringBuilder();
123         // 行首空白
124         StringBuilder space = new StringBuilder();
125         for (int i = 0; i < getLevel(); i++)
126         {
127             space.append("    ");
128         }
129         // 拼接元素名
130         allContent.append(space).append("<" + name);
131         // 拼接属性
132         if (attributes != null && attributes.size() > 0)
133         {
134             for (XMLAttribute a : attributes)
135             {
136                 allContent.append(" " + a.getAttributeName() + ":" + a.getAttributeContent());
137             }
138         }
139         allContent.append(">\n");
140         // 拼接元素内容
141         if (content != null)
142         {
143             allContent.append(space).append(content).append("\n");
144         }
145         // 拼接子元素
146         if (childElements != null && childElements.size() > 0)
147         {
148             for (AbstractElement e : childElements)
149             {
150                 allContent.append(e.getAllContent());
151             }
152         }
153         // 拼接元素名后缀
154         allContent.append(space).append("</" + name + ">\n");
155         return allContent.toString();
156     }
157 }

如上,AbstractElement类的所有抛异常的方法,这里都重写了。

3、然后就是我们的注释元素类了,如下:

 1 package composite.qsk;
 2 
 3 public class AnnotationElement extends AbstractElement{
 4 
 5     public AnnotationElement(String content) {
 6         super.content = content;
 7     }
 8 
 9     @Override
10     public String getAllContent()
11     {
12         StringBuilder allContent = new StringBuilder();
13         //行首空白
14         StringBuilder space = new StringBuilder();
15         for(int i = 0;i<getLevel();i++) {
16             space.append("    "); 
17         }
18         //拼接元素名
19         allContent.append(space).append("<!--");
20         //拼接元素内容
21         if(content != null) {
22             allContent.append(content);
23         }
24         //拼接元素名后缀
25         allContent.append("-->\n");
26         return allContent.toString();
27     }
28 }

这个类很简单。只是做了简单的拼接。

4、此外,还有一个简单的属性类,代表元素的属性,如下:

 1 package composite.qsk;
 2 
 3 public class XMLAttribute {
 4 
 5     private String attributeName = null;
 6 
 7     private String attributeContent = null;
 8 
 9     public String getAttributeName()
10     {
11         return attributeName;
12     }
13 
14     public void setAttributeName(String attributeName)
15     {
16         this.attributeName = attributeName;
17     }
18 
19     public String getAttributeContent()
20     {
21         return attributeContent;
22     }
23 
24     public void setAttributeContent(String attributeContent)
25     {
26         this.attributeContent = attributeContent;
27     }
28 
29     public XMLAttribute(String attributeName, String attributeContent)
30     {
31         super();
32         this.attributeName = attributeName;
33         this.attributeContent = attributeContent;
34     }
35 
36 }

5、完成构建文件的功能需要一个总的类负责,也负责组装xml声明,如下:

 1 package composite.qsk;
 2 
 3 import java.io.BufferedWriter;
 4 import java.io.File;
 5 import java.io.FileOutputStream;
 6 import java.io.IOException;
 7 import java.io.OutputStreamWriter;
 8 
 9 public class XMLParser {
10 
11     // 文件名
12     private String fileName = "";
13     // 文件
14     private File xmlFile = null;
15     // XML声明
16     private String xmlDeclaration = "<?xml version=\"1.0\" encoding=\"UTF-8\">";
17     // 根元素
18     private AbstractElement root = null;
19     // 所有内容
20     private String allContent;
21 
22     public XMLParser(String fileName)
23     {
24         if (fileName.endsWith(".xml"))
25         {
26             this.fileName = fileName;
27         }
28         else
29         {
30             this.fileName = fileName + ".xml";
31         }
32         xmlFile = new File(fileName);
33     }
34 
35     public XMLParser()
36     {
37         this("newXmlFile.xml");
38     }
39 
40     // 拼接所有内容
41     public String getAllContent()
42     {
43         return allContent = xmlDeclaration + "\n" + root.getAllContent();
44     }
45 
46     public String getXmlDeclaration()
47     {
48         return xmlDeclaration;
49     }
50 
51     public void setXmlDeclaration(String xmlDeclaration)
52     {
53         this.xmlDeclaration = xmlDeclaration;
54     }
55 
56     public AbstractElement getRoot()
57     {
58         return root;
59     }
60 
61     public void setRoot(AbstractElement root)
62     {
63 
64         if (root != null && root instanceof XMLElement)
65         {
66             this.root = root;
67         }
68         else
69         {
70             throw new UnsupportedOperationException("对象不支持此功能");
71         }
72     }
73 
74     public String getFileName()
75     {
76         return fileName;
77     }
78 
79     public void setFileName(String fileName)
80     {
81         this.fileName = fileName;
82     }
83 
84     // 输出文件
85     public void createFile() throws IOException
86     {
87         BufferedWriter bos = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(xmlFile)));
88         bos.write(getAllContent());
89         if (bos != null)
90             bos.close();
91     }
92 
93 }

6、下面是简单的测试了:

 1 package composite.qsk;
 2 
 3 import java.io.IOException;
 4 
 5 public class Test {
 6 
 7     public static void main(String[] args)
 8     {
 9         XMLParser xml = new XMLParser("myFile");
10         //
11         AbstractElement root = new XMLElement("qsk");
12         // 设置根
13         xml.setRoot(root);
14         // 各个元素
15         AbstractElement e1 = new XMLElement("e1");
16         AbstractElement e2 = new XMLElement("e2");
17         AbstractElement e3 = new XMLElement("e3");
18         AbstractElement e4 = new XMLElement("e4");
19         AbstractElement e5 = new XMLElement("e5");
20         AbstractElement e6 = new XMLElement("e6");
21         AbstractElement e7 = new AnnotationElement("这是一个注释");
22         AbstractElement e8 = new AnnotationElement("这是一个注释");
23         // 组合各个元素
24         root.addElement(e8);
25         root.addElement(e1);
26         root.addElement(e2);
27         e2.addElement(e7);
28         e1.addElement(e3);
29         e2.addElement(e4);
30         e3.addElement(e5);
31         e4.addElement(e6);
32         // 设置内容
33         e5.setContent("e5的内容啊");
34         e2.setContent("bbbbbbbb");
35         e6.setContent("e6的内容啊");
36         // 设置属性
37         root.addAttribute(new XMLAttribute("root的属性名", "root的属性值"));
38         e1.addAttribute("e1的属性名", "e1的属性值");
39         // 打印
40         System.out.println(xml.getAllContent());
41         // 输出xml文件
42         try
43         {
44             xml.createFile();
45         } catch (IOException e)
46         {
47             e.printStackTrace();
48         }
49     }
50 }

7、结果如下:

<?xml version="1.0" encoding="UTF-8">
<qsk root的属性名:root的属性值>
    <!--这是一个注释-->
    <e1 e1的属性名:e1的属性值>
        <e3>
            <e5>
            e5的内容啊
            </e5>
        </e3>
    </e1>
    <e2>
    bbbbbbbb
        <!--这是一个注释-->
        <e4>
            <e6>
            e6的内容啊
            </e6>
        </e4>
    </e2>
</qsk>

除了如上控制台的输出外,我们在工程目录也可以看到一个qsk.xml的文件。里面的内容与上相同。

在这个示例中,我们可以看到,通过一个root元素来得到其全部内容是通过方法getAllContent()的递归,这是一种变形的递归。

 

三、安全性和透明性

在上面的例子中,我们很明显可以发现,在抽象类中将子类所有可能的方法列出是一件很繁琐的事,看起来也不够优雅。不过这样带来的好处是用户根本不用区分使用的AbstractElement到底是元素还是注释。直接使用就可以了。这就叫做为了透明性而牺牲了安全性,使用注释调用不属于注释的方法,就会报异常。

同样的,我们可以将抽象类完全的抽象,只放子类共有的方法,这样会带来的好处是不会出上述的异常,变的安全,不过子类不再透明。使用时,需要区分到底是哪个类型。各种操作也不再方便。

如下所示:

1、将抽象类AbstractElement的所有报异常的方法都删去:

 1 package composite.qsk;
 2 
 3 import java.util.List;
 4 
 5 public abstract class AbstractElement {
 6 
 7     // 内容(包含在"<X>"和"</X>"之间的部分,没有用“<”和“>”括起来)
 8     protected String content = null;
 9 
10     // 元素层,根层数为0,其子元素层数为1,依次
11     private int level = 0;
12 
13     // 抽象方法、得到本元素所有内容(类似“<a c=d>efgh </a>”)
14     public abstract String getAllContent();
15 
16     public List<XMLElement> getChildElements()
17     {
18         throw new UnsupportedOperationException("对象不支持此功能");
19     }
20 
21     public int getLevel()
22     {
23         return level;
24     }
25 
26     public void setLevel(int level)
27     {
28         this.level = level;
29     }
30 
31     // 设置本元素内容
32     public boolean setContent(String content)
33     {
34         boolean flag = false;
35         if (content != null)
36         {
37             this.content = content;
38             flag = true;
39         }
40         return flag;
41     }
42 
43     // 得到本元素的内容
44     public String getContent()
45     {
46         return content;
47     }
48 
49 }

除此之外,只有测试类需要改动,这里就不再演示了。结果没有区别。

 

 

posted @ 2014-05-13 11:34  Chandler Qian  阅读(1068)  评论(0编辑  收藏  举报