組合模式定義:
將物件組合為樹狀結構,以表達 ”部份-整體” 的層次。組合模式讓使用者對單一物件或組合物件的存取具有統一性。
需求:
必須提供一個機制讓資料可以轉換成 xml ,xml格式並不複雜,但可能會出現多層嵌套的情況。e.g.
Example 1
<result> <code>999</code> <format> <name>John</name> <id>0123345</id> <gender>Male</gender> <mobileNumber>0988767654</mobileNumber> <age>20</model> </format> <format> <name>Mary</name> <id>543210</id> <gender>Female</gender> <mobileNumber>0911721431</mobileNumber> <age>18</model> </format> </result>
Example 2
<queryResult>success</queryResult>
Example 3
<device> <id>000001</id> <id>000002</id> </device>
上面3種格式雖然看起來不同,但基本的xml組成格式是相同的。可以把1個 xml element 當作1個資料結構的單體,如下方的code。
<code>999</code>
因此以上3種 xml 格式組成的 xml element 可以視為2種種類。
第1種為 Leaf,代表本身沒有包含 child (xml element),如下面的code
<code>999</code>
第2種為 Composite,代表本身有包含其他 child (xml element),如下面的format。
format 包含了5個child(name, id, gender, mobileNumber, age),而這5個child 因為沒有再包含其他的child,因此他們都是Leaf。
<format> <name>John</name> <id>0123345</id> <gender>Male</gender> <mobileNumber>0988767654</mobileNumber> <age>20</age> </format>
對應的資料結構
public class XmlInfo { private String mTag; private String mValue; private List<XmlInfo> mChildren = null; public XmlInfo(String tag, String value) { mTag = tag; mValue = value; } public void addChild(XmlInfo child) { if (mChildren == null) { mChildren = new ArrayList<XmlInfo>(); } mChildren.add(child); } public String getTag() { return mTag; } public String getValue() { return mValue; } public List<XmlInfo> getChildren() { return mChildren; } }
重點就在XmlInfo,其中 mTag 對應 xml element 的 tag 內容,mValue 對應 xml element 的 value內容。
當該 xml element 是 Composite 時,mValue就填 null , 為 Leaf,mValue 就填相應數值。e.g.
1.Leaf 寫法:
queryResult 為 Leaf 。
<queryResult>success</queryResult>
建立對應資料寫法為
XmlInfo queryResult = new XmlInfo("queryResult", "success");
2.Composite 寫法:
format 為 Composite。
<format> <name>John</name> <id>0123345</id> <gender>Male</gender> <mobileNumber>0988767654</mobileNumber> <age>20</age> </format>
建立對應資料寫法為
XmlInfo format = new XmlInfo("format",null); format.addChild(new XmlInfo("name", "John")); format.addChild(new XmlInfo("id", "012345")); format.addChild(new XmlInfo("gender", "Male")); format.addChild(new XmlInfo("mobileNumber", "0988767643")); format.addChild(new XmlInfo("age","20"));
另外需要注意的是 XmlInfo 不完全是 Composite Pattern 的應用。
原始的 Composite Pattern 會有個父類別(Component),並讓其子類別代表不同的種類如,Leaf , Component。
所以起碼會有3個 class 一起運作。因為目前的需求不需要用到其他的類別,單一類別(XmlInfo)即可符合需求。
模擬建立資料結構的操作
Example 1
private XmlInfo makeExample1() { XmlInfo result = new XmlInfo("result" , null); XmlInfo resultCode = new XmlInfo("code", "999"); result.addChild(resultCode); XmlInfo format1 = new XmlInfo("format", null); format1.addChild(new XmlInfo("name", "John")); format1.addChild(new XmlInfo("id", "012345")); format1.addChild(new XmlInfo("gender", "Male")); format1.addChild(new XmlInfo("mobileNumber", "0988767654")); format1.addChild(new XmlInfo("age", "20")); XmlInfo format2 = new XmlInfo("format", null); format2.addChild(new XmlInfo("name", "Mary")); format2.addChild(new XmlInfo("id", "543210")); format2.addChild(new XmlInfo("gender", "Female")); format2.addChild(new XmlInfo("mobileNumber", "0911721431")); format2.addChild(new XmlInfo("age", "18")); result.addChild(format1); result.addChild(format2); return result; }
Example 2
private XmlInfo makeExample2() { XmlInfo queryResult = new XmlInfo("queryResult", "success"); return queryResult; }
Example 3
private XmlInfo makeExample3() { XmlInfo device = new XmlInfo("device", null); XmlInfo id = new XmlInfo("id", "000001"); XmlInfo id2 = new XmlInfo("id", "000002"); device.addChild(id); device.addChild(id2); return device; }
轉換資料為 xml
private String parseXmlData(XmlInfo xmlData) { result.append( "<"+xmlData.getTag()+">"); if(xmlData.getChildren() != null){ for (int i = 0; i < xmlData.getChildren().size(); ++i) { parseXmlData(xmlData.getChildren().get(i)); } result.append( "<"+"/"+xmlData.getTag()+">"); }else{ result.append(xmlData.getValue()); result.append( "<"+"/"+xmlData.getTag()+">"); } return result.toString(); }
Test Case
Example 1
@Test public void testExample1() { String target = "<result>" + "<code>999</code>" + "<format>" + "<name>John</name>" + "<id>012345</id>" + "<gender>Male</gender>" + "<mobileNumber>0988767654</mobileNumber>" + "<age>20</age>" + "</format>" + "<format>" + "<name>Mary</name>" + "<id>543210</id>" + "<gender>Female</gender>" + "<mobileNumber>0911721431</mobileNumber>" + "<age>18</age>" + "</format>" + "</result>"; assertEquals(target, parseXmlData(makeExample1())); }
Example 2
@Test public void testExample2() { String target = "<queryResult>success</queryResult>"; assertEquals(target, parseXmlData(makeExample2())); }
Example 3
@Test public void testExample3() { String target = "<device><id>000001</id><id>000002</id></device>"; assertEquals(target, parseXmlData(makeExample3())); }