我正在实现一个表示遗留文件的类。这些文件是二进制文件,需要大量解析。可能会有数千行代码来解析文件。我在课堂上有两个构造函数:
class CrmxFile {
public:
CrmxFile(std::wstring filename);
CrmxFile(std::ifstream& file);
...
}
由于没有文件内容对象没有意义,所以在构造对象时需要完成所有解析。我打算实现一个庞大的构造函数(带有stream参数的构造函数),但是我的一位同事认为拥有非常大的构造函数并不是一个好习惯。相反,我应该创建一个私有方法来进行解析,并在检查流是可读的之后从构造函数中调用此方法,并且可能读取文件头以验证流包含正确的数据。
是否有任何指导方针/规则/惯例等。管理这种情况?基本上(在伪代码中),两种方法中的哪一种更好,
长构造函数:
CrmxFile::CrmxFile(std::ifstream& file) {
if (file is not readable) {
throw new CmrxException(read_error);
}
CmrxHeader header(file);
if(!header.isValid()) {
throw new CmrxException(invalid_file);
}
//now do all the reading/parsing of the file
//constructor may end up being a thousand lines of code
...
}
或简短的构造函数和辅助方法:
class CrmxFile {
public:
CrmxFile(std::wstring filename);
CrmxFile(std::ifstream& file);
private:
ParseFile(std::ifstream& file);
...
}
CrmxFile::CrmxFile(std::ifstream& file) {
if (file is not readable) {
throw new CrmxException(read_error);
}
CrmxHeader header(file);
if(!header.isValid()) {
throw new CrmxException(invalid_file);
}
ParseFile(file);
}
void CrmxFile::ParseFile(std::ifstream& file) {
//do all the reading/parsing of the file here
...
}
答案 0 :(得分:4)
这里的助手似乎是可行的..
但是,我不会把它变成私有的:而是将它分成几个,尽可能小的函数驻留在一个细节或解析器命名空间中,这样你就可以测试每个方法。由于您仍然使用流,因此这很容易:更改您的方法以接受istream
而不是ifstream
,然后测试的输入可以是纯字符串,然后您将其输入到您用作的istringstream方法论证。相信我,你会想要测试一下,正如你所说的那样现在已经有数百行了,你没有机会从第一次就把它弄好。
实际的辅助方法不仅仅是简单地结合了较小的方法,或者根据你拥有的方法,结合了较小的方法,而这些方法又结合了其他方法。
答案 1 :(得分:0)
如果你有多个构造函数,那么最好有一个私有方法来进行初始化。保存重复代码(从而有助于更易于维护,并且在发生任何更改时更不容易出错)。
如果只有一个构造函数只是根据需要使它变得复杂。
答案 2 :(得分:0)
除非你想为对象内部的其他用途或多个构造函数共享私有帮助器方法,否则我认为这不重要。
在构造函数中解析的方法对我来说听起来有问题。这是构造和初始化角色的混合。您可能希望将解析公开为公共接口,构造函数只会将对象的状态设置为非初始化,以将对象标记为对客户端不可用。或者,您可能希望尝试使用延迟评估来应用单例模式:实现getInstance
方法,如果尚未解析,将调用私有parse
- 从用户角度来看,它将是透明的(不包括开销)第一次访问)。
答案 3 :(得分:0)
我会说“不要在构造函数中执行它,期间”。
构造函数是奇怪的野兽,构造过程中的失败只能通过抛出异常。腐败/无效文件或解析错误太常见(在我看来)抛出 - 加上,在施工期间投掷和解析安全是一项挑战,人们经常会弄错。
因此,请遵循更简单的方法。
编写生成对象的工厂函数,甚至是工厂对象。它维护解析状态,并且具有解析对象的无数功能(或者,如果必须,还有一个大的功能)。然后,一旦解析完成,就返回该对象,并且该对象没有与从磁盘解析它相关联的一堆代码(或状态)。
这意味着代替
try {
MyObject foo("file.blah");
// ...
} catch (MyObject::ParseError err) {
// ...
}
你做了类似的事情:
MyObjectParser parser;
parser.Load("file.blah");
if (parser.Error()) {
// ...
} else {
MyObject foo = parser.Get();
// ...
}
并且您的情况是,唯一存在的MyObject
应该是已加载的MyObject
。