I need to decode and encode TLV (Tag, Length, Value) data which is a composite part-whole tree structure.
Tag1 Len1 Tag2-Len2-Value2 Tag3-Len3-Value3 ... TagN-LenN-ValueN ------------------------Value1------------------------
Therefore, I've decided to use the Composite pattern for the TLV objects:
typedef std::unique_ptr<TLVComponent> TLVComponentPtr; class TLVComponent { public: TLVComponent(); virtual ~TLVComponent(); virtual void Decode(const std::string &packet) = 0; virtual std::string Encode() = 0; virtual void AddTLV(TLVComponentPtr tlv); // only used in the constructor TLV unsigned char GetTag(); unsigned char GetLen(); std::vector<unsigned char> GetBytes(); std::string GetBytesStr(); protected: unsigned char mTag; unsigned char mLen; std::vector<unsigned char> mBytes; }; class PrimitiveTLV : public TLVComponent { public: PrimitiveTLV(); PrimitiveTLV(const std::string &packet); PrimitiveTLV(int tag, const std::string &bytes); virtual ~PrimitiveTLV(); virtual void Decode(const std::string &packet); virtual std::string Encode(); virtual void AddTLV(TLVComponentPtr tlv); // throw exception if called }; class ConstructorTLV : public TLVComponent { public: ConstructorTLV(unsigned char tag); ConstructorTLV(std::string packet); virtual ~ConstructorTLV(); virtual void Decode(const std::string &packet); virtual std::string Encode(); virtual void AddTLV(TLVComponentPtr tlv); private: std::vector<TLVComponentPtr> mTLVs; };
And here is the way I create a sample composite TLV object (please note that the real-world TLVs are not this simple and their values could be parameterized):
std::string bytes = ""; bytes += 0x12; bytes += 0x34; unsigned char tag = 0x10; PrimitiveTLV pTLV1(tag, bytes); bytes = ""; bytes += 0x56; bytes += 0x78; tag = 0x20; PrimitiveTLV pTLV2(tag, bytes); bytes = ""; bytes += 0x91; bytes += 0xA1; tag = 0x30; PrimitiveTLV pTLV3(tag, bytes); tag = 0x20; ConstructorTLV cTLV1(tag); cTLV1.AddTLV(&pTLV1); cTLV1.AddTLV(&pTLV2); tag = 0x80; ConstructorTLV cTLV3(tag); cTLV3.AddTLV(&cTLV1); cTLV3.AddTLV(&pTLV3); std::string cTLV3Encoded = cTLV3.Encode();
As the creation of the cTLV3
is complex, error-prone and is done via several steps, I have decided to use the Builder Pattern to create my TLV objects:
The problem is that I don't know how the Builder
abstract and concrete classes fit in my design. I think the Director
class of the pattern suffices for me:
/// /// \brief The TLV maker. (Director role in the Builder pattern) /// class TLVMaker { public: TLVMaker(); virtual ~TLVMaker(); /// /// \brief Creates the primitive TLV A /// \return /// TLVComponentPtr MakeTLVA(); /// /// \brief Creates the primitive TLV B /// \return /// TLVComponentPtr MakeTLVB(); /// /// \brief Creates the primitive TLV C /// \return /// TLVComponentPtr MakeTLVC(); /// /// \brief Creates the constructor TLV D /// \return /// TLVComponentPtr MakeTLVD(); /// /// \brief Creates the constructor TLV E /// \return /// TLVComponentPtr MakeTLVE(); };
And the implementation of the Director
:
TLVComponentPtr TLVMaker::MakeTLVA() { std::string bytes = ""; bytes += 0x12; bytes += 0x34; uchar tag = 0x10; TLVComponentPtr tlv(new PrimitiveTLV(tag, bytes)); return tlv; } TLVComponentPtr TLVMaker::MakeTLVB() { std::string bytes = ""; bytes += 0x56; bytes += 0x78; uchar tag = 0x20; TLVComponentPtr tlv(new PrimitiveTLV(tag, bytes)); return tlv; } TLVComponentPtr TLVMaker::MakeTLVC() { std::string bytes = ""; bytes += 0x91; bytes += 0xA1; uchar tag = 0x30; TLVComponentPtr tlv(new PrimitiveTLV(tag, bytes)); return tlv; } TLVComponentPtr TLVMaker::MakeTLVD() { uchar tag = 0x20; TLVComponentPtr tlv(new ConstructorTLV(tag)); tlv->AddTLV(MakeTLVA()); tlv->AddTLV(MakeTLVB()); return tlv; } TLVComponentPtr TLVMaker::MakeTLVE() { uchar tag = 0x80; TLVComponentPtr tlv(new ConstructorTLV(tag)); tlv->AddTLV(MakeTLVD()); tlv->AddTLV(MakeTLVC()); return tlv; }
The creation of my sample TLV object becomes very simple now:
TLVMaker tlvMaker; TLVComponentPtr tlvE = tlvMaker.MakeTLVE(); std::string tlvE_Encoded = tlvE->Encode();
My questions:
- Have I correctly decided to use the Builder pattern to create my composite objects?
- I have only been able to use the
Director
class of theBuilder
pattern. How can/should I include the abstract and concreteBuilder
classes as well? - If using the
Director
class (TLVMaker
) in my design is enough, can I still claim I have used the Builder pattern?
cTLV1.AddTLV(&pTLV1);
can't possibly work, when the aregument is supposed to be astd::unique_ptr<TLVComponent>
\$\endgroup\$virtual void AddTLV(TLVComponentPtr tlv); // throw exception if called
... brrrrrr :-)\$\endgroup\$