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\$