受这个问题的启发,我决定编写一个使用多重形态和工厂模式的替代方案。该代码适用于已实现类型的子集,即BYTE、WORD和STRING,但我有以下几点考虑:
TLVFactory中那个丑陋的大开关。TLVFactory::create成为TLVObject的静态成员吗?#include <iostream>
#include <iomanip>
#include <fstream>
#include <cstdint>
#include <string>
#include <vector>
#include <memory>
#include <stdexcept>
class TLVObject
{
public:
enum TLV_TYPE {UNDEFINED = 0, BYTE, WORD, DWORD, QWORD, STRING, BLOB};
TLVObject(TLV_TYPE type) :
m_type{type}
{}
virtual ~TLVObject() {}
virtual size_t len() const { return 0; }
virtual std::ostream &write(std::ostream &out) const {
writeTag(out);
writeLen(out);
writeData(out);
return out;
}
virtual std::ostream &writeTag(std::ostream &out) const {
return out.put(m_type);
}
virtual std::ostream &writeLen(std::ostream &out) const {
return out.put(len());
}
virtual std::ostream &writeData(std::ostream &out) const {
return out;
}
virtual std::istream &read(std::istream &in) {
readTag(in);
readLen(in);
readData(in);
return in;
}
virtual std::istream &readTag(std::istream &in) {
char ch;
in.get(ch);
m_type = static_cast<TLV_TYPE>(ch);
return in;
}
virtual std::istream &readLen(std::istream &in) { return in; }
virtual std::istream &readData(std::istream &in) { return in; }
private:
TLV_TYPE m_type;
};
class TLVByte : public TLVObject
{
public:
TLVByte(uint8_t value) :
TLVObject{BYTE},
m_value{value}
{}
TLVByte() : TLVByte{0} {}
virtual ~TLVByte() {}
virtual size_t len() const { return sizeof(m_value); }
virtual std::ostream &writeData(std::ostream &out) const {
return out.put(m_value);
}
virtual std::istream &readLen(std::istream &in) {
if (len() != static_cast<size_t>(in.get())) {
std::out_of_range("TLV len error");
}
return in;
}
virtual std::istream &readData(std::istream &in) {
m_value = in.get(); return in;
}
private:
uint8_t m_value;
};
class TLVWord : public TLVObject
{
public:
TLVWord(uint16_t value) :
TLVObject{WORD},
m_value{value}
{}
TLVWord() : TLVWord{0} {}
virtual ~TLVWord() {}
virtual size_t len() const { return sizeof(m_value); }
virtual std::ostream &writeData(std::ostream &out) const {
return out.put(m_value >> 8).put(m_value & 0xff);
}
virtual std::istream &readLen(std::istream &in) {
if (len() != static_cast<size_t>(in.get())) {
std::out_of_range("TLV len error");
}
return in;
}
virtual std::istream &readData(std::istream &in) {
m_value = in.get() << 8;
m_value |= in.get();
return in;
}
private:
uint16_t m_value;
};
class TLVString : public TLVObject
{
public:
TLVString(std::string str) :
TLVObject{STRING},
m_value{str}
{}
TLVString() : TLVString("") {}
virtual ~TLVString() {}
virtual size_t len() const { return m_value.size(); }
virtual std::ostream &writeData(std::ostream &out) const {
return out << m_value;
}
virtual std::istream &readLen(std::istream &in) {
m_value.reserve(in.get());
return in;
}
virtual std::istream &readData(std::istream &in) {
for (int i = m_value.capacity(); i; --i) {
m_value += in.get();
}
return in;
}
private:
std::string m_value;
};
class TLVFactory
{
public:
static std::unique_ptr<TLVObject> create(TLVObject::TLV_TYPE type) {
switch (type) {
case TLVObject::BYTE:
return std::make_unique<TLVByte>();
break;
case TLVObject::WORD:
return std::make_unique<TLVWord>();
break;
case TLVObject::STRING:
return std::make_unique<TLVString>();
break;
case TLVObject::DWORD:
case TLVObject::QWORD:
case TLVObject::BLOB:
default:
std::out_of_range("Can't construct object of that type");
}
return std::make_unique<TLVObject>(TLVObject::UNDEFINED);
}
};
class TLVCollection : public std::vector<std::unique_ptr<TLVObject>> {
public:
void write(std::ostream &out) const {
for (const auto &item : *this) {
item->write(out);
}
}
void read(std::istream &in) {
while (in.good() && in.peek() > 0) {
push_back(TLVFactory::create(static_cast<TLVObject::TLV_TYPE>(in.peek())));
back()->read(in);
}
}
};int main()
{
TLVCollection vec;
vec.push_back(std::make_unique<TLVByte>(TLVByte{7}));
vec.push_back(std::make_unique<TLVString>(TLVString{"banana"}));
vec.push_back(std::make_unique<TLVWord>(TLVWord{0x1234}));
std::ofstream out("tlvtest.bin");
vec.write(out);
out.close();
TLVCollection v2;
std::ifstream in("tlvtest.bin");
v2.read(in);
in.close();
v2.write(std::cout);
}当我运行该程序并通过xxd将其输出管道(它只是将二进制输入转换为十六进制表示)时,我得到如下结果:
./tlv2 2区xxd 00000000: 0101 0705 0662 616 e 616 e 6102 0212 .....banana...4
发布于 2016-01-02 00:22:30
以下是一些建议。
const 在构造函数中,只需复制传入的值,而不对其进行变异。它应该声明为const,因为它没有发生变异。
我不明白允许调用方创建未定义的TLV类型的意义。它有0的长度,所以没有什么可写或读的。最好是根本不允许这样的东西被建造。我会从枚举中删除UNDEFINED值。(使基类的构造函数受到保护,这样就不会直接在子类之外实例化。)
中使用纯虚拟方法
如果TLVObject使virtual成为必须被子类覆盖的方法,编译器将阻止调用者构造基本对象。因此,我会让len()、writeData()、readLen()和readData()都是纯虚拟的:
virtual size_t len() const = 0;
...
virtual std::ostream &writeData(std::ostream &out) const = 0;
...
virtual std::istream &readLen(std::istream &in) = 0;
virtual std::istream &readData(std::istream &in) = 0;上使用override
当重写子类中的方法时,应将其标记为override,以便编译器知道您的意图,并警告您,如果基类中的方法签名发生了更改,或者在子类中拼写错误。所以看起来是这样:
class TLVByte : public TLVObject
{
...
virtual size_t len() const override { return sizeof(m_value); }
virtual std::ostream &writeData(std::ostream &out) const override {
return out.put(m_value);
}
virtual std::istream &readLen(std::istream &in) override {
if (len() != static_cast<size_t>(in.get())) {
std::out_of_range("TLV len error");
}
return in;
}
virtual std::istream &readData(std::istream &in) override {
m_value = in.get(); return in;
}..。
总的来说,你的工厂方法看起来是完全合理的。我最初说的是,“没有理由它不可能是TLVObject中的一个公共静态方法,您只需要转发声明子类”:
class TLVByte;
class TLVWord;
class TLVString;
class TLVObject
{
public:
enum TLV_TYPE {UNDEFINED = 0, BYTE, WORD, DWORD, QWORD, STRING, BLOB};
static std::unique_ptr<TLVObject> create(TLVObject::TLV_TYPE type) {
switch (type) {
case TLVObject::BYTE:
return std::make_unique<TLVByte>();
break;
case TLVObject::WORD:
return std::make_unique<TLVWord>();
break;
case TLVObject::STRING:
return std::make_unique<TLVString>();
break;
case TLVObject::DWORD:
case TLVObject::QWORD:
case TLVObject::BLOB:
default:
std::out_of_range("Can't construct object of that type");
}
return std::make_unique<TLVObject>(TLVObject::UNDEFINED);
}
...然而,我错了。由于试图使用尚未完全描述的类型作为模板(std::unique_ptr<T>)的参数,上面的内容将无法工作,这是行不通的。
另一个选择是让它成为一个独立的功能。虽然这样做没有问题,但没有理由需要自己在一个类中。
正如您可能从上述错误中猜测到的,我不是模板专家。因此,有一个问题--是否有可能使create()方法成为模板化的函数?类似于:
template<T>
static unique_ptr<T> create()
{
return std::make_unique<T>();
}或者干脆摆脱工厂函数,只使用std::make_unqiue<Whichever>();创建它们?
另外,您的create()的最后一个返回语句将永远不会到达,因为您有一个default case。正如上面提到的,它不应该是必要的,因为您不应该允许构造一个未定义的对象。
https://codereview.stackexchange.com/questions/115584
复制相似问题