组合模式小试
来源:互联网 发布:驱动精灵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 }
除此之外,只有测试类需要改动,这里就不再演示了。结果没有区别。
- 组合模式小试
- 小话设计模式(九)组合模式
- 组合模式
- 组合模式
- 组合模式
- 组合模式
- 组合模式
- 组合模式
- 组合模式
- 组合模式
- 组合模式
- 组合模式
- 组合模式
- 组合模式
- 组合模式
- 组合模式
- 组合模式
- 组合模式
- J2EE环境安装配置
- 策略模式小试
- 摩托车继承自行车和机动车
- 美国VPN你懂的!!
- the C programming language 练习
- 组合模式小试
- mini2440下载到nandflash中时地址设置是0x30000000
- ADO.net数据库访问技术(一)
- Nginx中502和504错误详解
- lubuntu下安装SDL
- 代码收藏2
- 目标检测中背景建模方法
- 第一次接触CorePlot
- socket