摘要:該文提出了一種方便擴展,高可復用的類的序列化與反序列化模塊的框架設計,通過這種做法使程序開發人員從繁重的編碼過程中解放出來,使他們能夠更為關注程序中的業務邏輯。使整個程序系統結構清晰,方便維護。
關鍵詞:序列化;反序列化;軟件框架
中圖分類號:TP311文獻標識碼:A文章編號:1009-3044(2009)24-6739-03
A Framework of Serialization and De-serialization Module in Software Design
HU Chen-guang, YAN Jie-feng, GAO Zheng-dong, HU Bin, XU Rui
(CETC38, Hefei 230011, China)
Abstract: A new Framework of Serialization and De-serialization Module referred in this paper is extensible and reusable. It can help software designers to focus on the own logic business in development and make the other module structures of the whole system combine clearly and be maintained conveniently.
Key words: serialization; de-serialization; software framework
當前流行的開發工具Visual C++ Visual .Net、以及JAVA語言中,為解決對象與二進制數據的相互轉換提供了一些可用的方法,但如果使用這些方法進行類與二進制的轉換,有時并不能達到開發人員期望的效果,也不能滿足程序對多種通信協議的支持的需求。為了更好地把握所開發的程序結構,往往需要程序員自己開發的轉換模塊,以確保開發的程序的完整性和可維護性。
為了解決上述現實問題,本文中提出了一個對象與二進制的相互轉換模塊的框架及其實現。此框架具有高復用性,同時也可根據實際的應用協議對此模塊進行擴展,很方便地支持多種通信協議,減少代碼的編寫量,縮短整個軟件的開發周期。
1 概念
1.1 序列化
將程序運行過程中的對象轉換成二進制數據形式的過程。
1.2 反序列化
將二進制數據轉換成對象形式的過程。
2 序列化與反序列化框架介紹
利用面向對象程序設計中繼承與虛函數的概念,方便了其中各個元素可復用和擴展。本文將整個序列化與反序列化框架劃分為三個部分。
2.1 接口部分
接口就是框架中各個模塊的規格說明書,約定所有具體類必須實現的方法。
2.2 元數據類部分
將一些例如整型、布爾型等語言的內置類型進行封裝,同時實現上述接口,使其能夠方便使用。
2.3 組合數據類部分
元數據類組合同時實現接口。組合數據類型中的每一個類與所實際具體的通信協議中每個命令是一一對應的。
整個序列化與反序列化模塊中類的繼承引用關系UML如圖1所示。
3 框架說明與實現
下面對序列化與反序列化框架中的三個部分進行詳細說明并給出簡單的實現示例。
3.1 接口部分
框架包含一個接口定義ISerializable。其為所有可序列化與反序列化數據類的父類,包括Serialize方法以及Deserialize方法。所有需要具有序列化功能的類必須實現此接口。這可以更好對所有數據類進行組織和實現。GetSerializableDataSize方法為調用可序列化對象的外部程序獲取序列化后的對象的總字節數提供便利。
如下實現:
class ISerializable
{
public:
virtual LONG Serialize(PBYTE pSerialBuffer,ULONG ulBufferSize) = 0;
virtual LONG Deserialize(const BYTE* pSerialBuffer, ULONG ulDataSize) = 0;
virtual LONG GetSerializableDataSize() const = 0;
};
3.2 元數據類
將語言中各種內置類型(如int ,long ,bool等)進行類的封裝并實現ISerializable接口。元數據類是下面提到的組合數據類(具體的通信協議)的組成基礎。在封裝類中規定每個內置類型所占的字節數以及如何序列化與反序列化。如,整型(int)序列化后占四個字節、布爾型(bool)占一個字節等。下面給出整型元數據類(IntTypeMeta)的實現。
類的聲明
class IntTypeMeta : public ISerializable
類方法實現
LONG IntTypeMeta::Serialize(PBYTE pSerialBuffer,ULONG ulBufferSize)
{
*(INT*)(pSerialBuffer) =m_integer;
return sizeof(INT);
}
LONG IntTypeMeta::Deserialize(const BYTE* pSerialBuffer,ULONG ulDataSize)
{
m_integer = *(INT*)(pSerialBuffer);
return sizeof(INT);
}
LONG IntTypeMeta::GetSerializableDataSize()
{
return sizeof(INT);
}
在上述的Serialize成員函數中,整型數據m_integer保存到以pSerialBuffer為頭指針的緩沖區中。而在成員函數Deserialize中,將以pSerialBuffer為頭指針的緩沖區中的數據賦值到m_integer。這兩個成員函數就完成了對IntTypeMeta類對象的序列化與反序列化功能。成員函數GetSerializableDataSize()返回m_integer序列化后所占的字節數。按照同樣的方法可以實現ByteTypeMeta(字節類型),ShortTypeMeta(雙字節類型),LongTypeMeta(長整型類型)以及StringTypeMeta(字符串類型)等
3.3 組合數據類型
組合數據類型是按照約定的順序對一個或多個元數據類的排列組合。與元數據類不同的是,組合數據類同樣可以由一個或多個組合數據類排列組合,從而形成一個完整的通信協議類的表示。
3.3.1 組合數據類型的實現
在上述元數據類創建的基礎上,開始建立組合數據類型。如,若需要定義一個通信協議的數據包頭類。假設數據包頭包含數據包的類型和數據包體內容字節數兩個部分,其中數據包的類型用字節型數據(一個字節)表示,數據包體內容字節數為長整型(四個字節)表示。
通信協議的數據包頭對應類的詳細定義和實現如下:
class PackageHead : public ISerializable
{
public:
PackageHead();
virtual ~PackageHead();
LONG Serialize(PBYTE pSerialBuffer,ULONG ulBufferSize);
LONG Deserialize(const BYTE* pSerialBuffer, ULONG ulDataSize);
LONG GetSerializableDataSize() const;
private:
//存放成員的元數據對象以及組合數據對象
vector
};
在實現數據包頭類的構造函數中,采用將組成包頭的可序列化對象直接放入m_packageHeader數組中。
PackageHead:: PackageHead()
{
ByteTypeMeta* pPackageType = new ByteTypeMeta();
LongTypeMeta* pPackageSize = new LongTypeMeta();
m_packageHeader.push_back(pPackageType);
m_packageHeader.push_back(pPackageSize);
}
需要注意的是,上述m_packageHeader.push_back語句的執行順序直接影響到實際的數據序列組織和反序列化解析的順序。從構造函數可以看出,序列化后數據包的類型(*pPackageType)排在數據包字節數(*pPackageSize)之前。
組合數據類中各個接口的實現:
LONG PackageHead:: Serialize(PBYTE pSerialBuffer,ULONG ulBufferSize)
{
LONG lOffset = -1;
for(INT i = 0; i < m_packageHeader.size(); i++)
lOffset += m_packageHeader[i]->Serialize(pSeirialBuffer+lOffset,ulBufferSize-lOffset);
return lOffset;
}
LONG PackageHead::Deserialize(const BYTE* pSerialBuffer, ULONG ulDataSize)
{
LONG lOffset = -1;
for(INT i = 0; i < m_packageHeader.size(); i++)
lOffset += m_packageHeader[i]->Deserialize(pSeirialBuffer+lOffset, ulDataSize-lOffset);
return lOffset;
}
Long PackageHead::GetSerializableDataSize()
{
LONG lOffset = -1;
for(INT i = 0; i < m_packageHeader.size(); i++)
lOffset += m_packageHeader[i]->GetSerializableDataSize ();
return lOffset;
}
因m_packageHeader數組中的所有元素均為實現同一接口,所以通過遍歷m_packageHeader中各個成員,并調用每個成員的序列化和反序列化接口,就可以非常方便實現各種數據類型。代碼相當簡潔、清晰,這就是前期對元數據類封裝好處。在類的析構函數中,注意將保存在m_packageHeader中所有數據逐一釋放,避免內存泄漏。
4 框架擴展展望
在組合類的實現中,其組成成員的數據表示順序由各個構造函數的push操作順序來確定。通過這種“硬”編碼的方式,會導致框架與實現的協議之間的擴展性不強。為了更為方便的適應通信協議制訂中不同的數據組織,可以用XML文件來描述數據組織順序。在框架模塊初始化的過程中,加載相應的配置文件,根據文件中的描述進行數據的組織,使程序實現與協議的制訂無關,達到模塊的高擴展性。
5 結論
本文利用面向對象程序設計中的繼承以及虛函數概念,完成序列化與反序列化的框架功能。使整個框架具有可復用、高可維護的特點。在實現上可將模塊與XML配置文件結合后框架更具有可擴展性。通過這種方法使程序開發人員從繁重的編碼過程中解放出來,使其能夠更為關注程序中的業務邏輯,從而使整個程序系統結構之間松耦合,易擴展,方便維護。
參考文獻:
[1] 侯俊杰.深入淺出MFC[M].侯捷,譯.2版.武漢:華中科技大學出版社,2001.
[2] Eckel B.Thinking In Java[M].3rd ed.Prentice Hall PTR,2003.