组合模式小试

来源:互联网 发布:驱动精灵for mac 编辑:程序博客网 时间:2024/05/22 03:48

组合模式是一种功能比较单一的设计模式,一般与其他设计模式搭配使用。本篇简单模拟了一下自动构建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     @Override120     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     @Override10     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         else29         {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         else69         {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 IOException86     {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         try43         {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 }
复制代码

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

0 0
原创粉丝点击