情况就是这样:
我想使用一类第三方库(即Poco
)来解析JSON字符串。这个Parser
类的设计方式是每次解析(操作)后都必须重新设置(通过调用reset()
,谁会想到它?)。我在我正在实现的类中的不同位置多次重用一个Parser
,因此这对我很重要。即使班级已经重置,调用reset()
也是无害的。
因此我的问题是:我是否应该确保在每次操作之前重置解析器,或者我应该确保在操作后重置它。
第一种情况很方便,因为它确保我的解析器在操作之前处于正确的状态,但同时我感觉很脏,因为我让解析器处于无效状态,而其他人可能希望我重置解析器。
我不喜欢第二种情况,因为我担心我可能会错过我的方法可能退出的情况(例如未捕获的异常),使解析器处于无效状态,而其他人则期望它确实有效。当然这可以通过使用RAII(就像一个范围锁)来解决,但这对我来说听起来有点过时了。
另一种解决方案可能是结合两种情况,所以:重置,解析和重置;但这是一种多余的IMO。
我真的不确定这里并且不能决定任何这些情况,我希望你的意见(基于与设计相关的考虑因素,你不关闭我的问题!;))将帮助我选择。
答案 0 :(得分:3)
好吧,我建议不要使用Poco,而是看JsonCpp。 通过让你调用重置来制作Poco的设计选择相当奇怪。 除此之外,RAII方法相当简单:
template <typename T>
struct ScopedParser {
T& parser; // parser object
ScopedParser(T& p) : parser(p) {} // set parser in constuctor
ScopedParser(ScopedParser&&) = delete; // no moving
ScopedParser(const ScopedParser&) = delete; // no copying
~ScopedParser() {
parser.reset(); // reset it
}
}
如何使用它的示例:
void myFunc() {
Poco::JSONParser p;
ScopedParser<Poco::JSONParser> pScoped(p);
}
答案 1 :(得分:1)
这似乎是将解析器嵌入RIIA样式类的合适案例。 我们的目标是创造一个独特的用户&#34;你的解析器。
这是一个快速模拟:
class MyParserBuilder
{
public:
class MyParser
{
public:
MyParser(poco::Parser& parser, MyParserBuilder& builder) :
_parser(parser),
_builder(builder)
{
}
~MyParser()
{
_builder.reset();
}
// Either expose some functions from the parser or use
// something like:
poco::Parser& operator*() { return _parser; }
private:
poco::Parser& _parser;
MyParserBuilder& _builder;
};
MyParserBuilder() : _used{false} {}
std::unique_ptr<MyParser> build()
{
if (_used) // Or lock ?
{
return std::unique_ptr<MyParser>();
}
_used = true;
return std::unique_ptr<MyParser>(new MyParser(_parser, *this));
}
void reset()
{
_parser.reset();
_used = false;
}
private:
bool _used;
poco::Parser _parser;
};
为了安全起见,我还要补充一些类似于&#34;脏&#34;状态,以便您只能执行每次MyParser
实例化时需要重置一次的操作。
答案 2 :(得分:1)
第一种情况听起来不错,只要“其他”只是在同一个私有Parser对象上运行的其他成员函数。
结合两种情况只会减少潜在的错误。可能仍然存在未捕获的异常,而其他人可能期望解析器被重置。
最佳选择仍然是RAII锁定式帮助对象。我不认为类似5行的类是过度工程。如果解析器被多个线程使用,你可以在那里添加一个真正的锁。
或者甚至为每个函数都有一个新的作用域Parser,如果它对你的应用程序的性能不是一个问题。这甚至可能比同步对同一解析器的访问更快。