組合模式定義:
將物件組合為樹狀結構,以表達 ”部份-整體” 的層次。組合模式讓使用者對單一物件或組合物件的存取具有統一性。
需求:
必須提供一個機制讓資料可以轉換成 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()));
}

