今回はデザインパターンの一つであるコンポジットパターンを応用してXMLを作ってみます。C言語で四苦八苦したソースが大分すっきりした形でかけます。前回と同様次のXMLを作成する事を目標とします。
<root> <tag1> <tag2>element_start</tag2> <tag2>element_middle</tag2> </tag1> <tag1> <tag3>element_last</tag3> </tag1> </root>
コンポジットパターンに当てはめてXMLの構造を拾ってみましょう。クラス図で書くと。
一般にコンポジットパターンはインターフェースを継承しますが、今回はElementを持つ最終ノード(CLastNode)もTagしか持たない通常ノード(CNode)もすべてTag情報を保持する構造なので、sTagメンバを格上げして抽象クラス(CAbstractNode)にしました。通常ノードは下位に同じく通常ノード、もしくは最終ノードを従えますのでCAbstractNode型のメンバ nodesを持ちます(コンテナクラスを保持してる場合、集約関係を使います。) showNode()メソッドは深さ優先探索に使います。判りにくいかもしれませんが、とりあえずこのクラス図をコードにプロットしてみます。
<Composite.h>
#pragma once
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class CAbstractNode
{
public:
virtual void showNode(void) = 0;
virtual void setTag(string sTag);
protected:
string sTag;
};
class CNode : public CAbstractNode {
public:
virtual ~CNode();
virtual void showNode();
void addNode(CAbstractNode* nodes);
private:
vector<CAbstractNode*> nodes;
};
class CLastNode : public CAbstractNode {
public:
virtual void showNode();
void setElement(string sEle);
private:
string sElement;
};
つぎにこれらのメソッドは
<Composite.cpp>
#include "Composite.h"
void CAbstractNode::setTag(string sTag)
{
this->sTag = sTag;
}
CNode::~CNode()
{
for(int i= 0; i<(int)nodes.size(); i++){
delete nodes[i];
}
}
void CNode::addNode(CAbstractNode* nodes){
this->nodes.push_back(nodes);
}
void CNode::showNode()
{
cout << "<" << sTag << ">" << endl;
for(int i=0; i<(int)nodes.size(); i++){
nodes[i]->showNode();
}
cout << "</" << sTag << ">" << endl;
}
void CLastNode::setElement(string sEle)
{
sElement = sEle;
}
void CLastNode::showNode()
{
cout << "<" << sTag << ">" << sElement << "</" << sTag << ">" << endl;
}
ポイントはaddNodeメソッドでアロケートしてメンバを増やしているところです。ここで木構造を実現しています。また最終ノードと通常ノードの型の違いを抽象クラスを使う事で多相的に処理をしています。デストラクタがありますがここではあまり注目しなくてもかまいません。では最後にクライアントコードを見てみましょう。
<cliant.cpp>
#include "Composite.h"
int _tmain(int argc, _TCHAR* argv[])
{
const string s_root = "root";
const string s_tag1 = "tag1";
const string s_tag2 = "tag2";
const string s_tag3 = "tag3";
CNode *cRoot = new CNode;
CNode *cNode1 = new CNode;
CNode *cNode2 = new CNode;
CLastNode *cElem1 = new CLastNode;
CLastNode *cElem2 = new CLastNode;
CLastNode *cElem3 = new CLastNode;
cRoot->setTag(s_root);
cRoot->addNode(cNode1);
cRoot->addNode(cNode2);
cNode1->setTag(s_tag1);
cNode1->addNode(cElem1);
cNode1->addNode(cElem2);
cNode2->setTag(s_tag1);
cNode2->addNode(cElem3);
cElem1->setTag(s_tag2);
cElem1->setElement("element_start");
cElem2->setTag(s_tag2);
cElem2->setElement("element_middle");
cElem3->setTag(s_tag3);
cElem3->setElement("element_last");
cRoot->showNode();
delete cRoot;
return 0;
}
C言語でXMLを実現したときと比べ非常にすっきりしているかと思います。最後のshowNode()が実行されると木構造の深さ優先探索が始まりコンソールにお題のXMLが出力されます。
※はてなキーワードをよける為ところどころ全角文字が含まれてますので注意下さい。