组合模式小试

组合模式是一种功能比较单一的设计模式,一般与其他设计模式搭配使用。本篇简单模拟了一下自动构建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 }

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

 

 

你可能感兴趣的:(组合模式)