假设我有Archive
界面和File
界面。
File
保证至少有std::string name
。Archive
可以std::vector<File*> Archive::list() const
其文件。Archive
都可以Archive::extract(std::vector<File*> files)
。然后我有ZipArchive
和ZipFile
,ZipFile
包含档案文件中的偏移量和其他实现细节。然后是TarArchive
/ TarFile
等等。其中每个填充std::vector<File*> list() const
的实例为ZipFile
,TarFile
等。
list()
旨在让用户有机会选择要解压缩的文件。他们从该向量中选择元素,然后将此向量传递给extract()
。
此时,ZipArchive
需要假设它已传递正确的类型并执行dynamic_cast<ZipFile*>(file)
以访问实现细节。
这感觉很糟糕。这可以接受吗?还有其他选择吗?
答案 0 :(得分:2)
根据评论中的建议,您可以将提取界面从Archive
移至File
。归档将返回std::vector<File*>
,但实际上每个对象将是例如ZipFile
,并且将知道它属于哪个归档以及它的类型,并且将能够调用适当的提取方法。
因此,您可以拥有代码而无需检查存档类型:
struct File;
struct Archive {
virtual std::vector<File*> fileList() = 0;
};
struct File {
File(std::string name_) : name(name_) {}
virtual void extract() = 0;
std::string name;
};
struct ZipFile;
struct ZipArchive: public Archive {
void extractFile(ZipFile& file);
virtual std::vector<File*> fileList();
};
struct ZipFile: public File {
ZipArchive* archive;
virtual void extract() { archive->extractFile(*this); }
ZipFile(std::string name_, ZipArchive* archive_) : File(name_), archive(archive_) {}
};
如果你想通过一次调用提取很多文件,那可能会更加困难,但你可以让档案extractFile
只记住那个文件,然后在Archive
类中使用一个特殊的方法来提取所有文件记住的文件一下子。我认为这甚至可以隐藏在一个相当简单的界面下。
答案 1 :(得分:1)
您的ZipArchive
可以在其文件列表中搜索传递的指针。如果它在那里,它可以使用存储的指针(已经是类型ZipFile
)或static_cast
传递指向ZipFile
的指针(因为你已经证明它的类型)。如果传递的指针不在列表中,那么它显然不是该存档所拥有的文件,因此您可以继续进行错误处理。
您还可以为每个Archive*
添加File
类型的后向指针。具体的ZipArchive
实现可以通过简单的指针比较来检查它的一个文件。
void ZipArchive::extract(std::vector<File*> files)
{
for (auto file : files)
{
if (file->archive() == this)
{
// one of my files
auto zipFile = static_cast<ZipFile*>(file);
// do something with zipFile
}
else
{
// file is owned by some other archive
}
}
}
答案 2 :(得分:0)
class Archive {
public:
static int registerArchiveType(const std::string &name) {
// generate a unique int for the requested name archive type
// and store it in a map or whatever
return uniqueInt;
}
int archiveType() const;
protected:
Archive(int type) : _type(type) {}
private:
int _type;
public:
virtual extract(std::vector<File*> files);
// your implementation details
};
class File {
public:
int archiveType() { return _archType; }
protected:
// force implementations to pass the same type
// they received from the call to Archive::registerArchiveType
File() {}
void setArchiveType(const std::string &archiveType) {
// multiple calls to registerArchiveType return the
// same identifier if passed the same string
_archiveType = Archive::registerArchiveType(archiveType);
}
private:
int _archiveType;
};
然后在ZipArchive
实现中,如果extract
返回的int是archiveType
方法,则可以执行 static_cast 而不是动态的 static_cast 。与为Zip档案类型注册的相同。
static const char* ZIP_TYPE = "zip";
// specialize your ZipFile making sure
// you pass the correct archive type identifier
// in the constructor
class ZipFile {
public:
ZipFile() : File() {
setArchiveType(ZIP_TYPE);
}
// bla bla
};
void ZipArchive::extract(std::vector<File*> files) {
for (int i = 0; i < files.count(); i++) {
if (files[i]->archiveType() == Archive::registerArchiveType(ZIP_TYPE)) {
ZipFile *zipFile = static_cast<ZipFile*>(files[i]);
// do something with zipFile
}
}
}
答案 3 :(得分:0)
你需要分析你对Archive
的处理方式:你是否需要在某些地方因某种不确定的类型而有一些共同的行为,或者你不是吗?这将带来两种不同的设计选择,因此请谨慎选择。
正如评论中所说,你似乎不需要前者
让File
代表文件句柄,ZipFile
,TarFile
是其派生。然后,对于每种类型的文件,让Archive
处理它。
struct ZipFile
{
File handle;
// zip-specific implementation details
};
struct TarFile
{
File handle;
// tar-specific implementation details
};
class ZipArchive
{
public:
std::vector<ZipFile> list() const;
void extract(std::vector<ZipFile>);
private:
std::vector<ZipFile> archive;
};
TarArchive
也一样。不再需要处理所有权,指针等等;你也获得了强大的类型安全性。