这几天一直在看common lisp,我发现它比c++抽象层次高很多,编程开发速度极快。不过目前而言,c++还是前面比较有用的,毕竟目前桌面程序用的比较多。
回到正题,前2篇基本上已经将基本功能实现了。今天需要进行实际的文件操作。
前面Folder类的操作都是我故意模拟文件系统的,今天利用boost/filesystem库来实现。
面向对象的一个重要原则是开闭原则(OCP),我希望我不再Folder中直接修改,而是通过继承的方式来进行。
重构代码:
1. 更改Folder的类名为MockFolder
2. 新建Folder类,该类为虚基类,MockFolder继承它
3. 修改SynchronousSystem,让其依赖于Folder,而不是之前的MockFolder。
于是所有类代码如下:
类Folder
class Folder { public: Folder(const std::string& path):itsPath(path){} //info virtual FileModifyTime getFileModifyTime(const std::string& filename) =0; virtual bool isDirectory(const std::string& filename) = 0; virtual bool isExist(const std::string& filename) = 0; //operator virtual void backUp(const std::string& filename) = 0; virtual void copyFileTo(const std::string& filename, Folder* destFolder) = 0; virtual std::string getAbsoluteFilePath(const std::string& filename) const { return itsPath + "\\" + filename;} std::string getPath(){return itsPath;} protected: std::string itsPath; };
类SynchronousSystem
class SynchronousSystem { public: SynchronousSystem(MockFolder* source,MockFolder* dest):itsSourceFolder(source),itsDestFolder(dest){} bool isNeedUpdate(const std::string& filename) { if(!itsDestFolder->isExist(filename)) return true; if(!itsSourceFolder->isDirectory(filename) && itsSourceFolder->getFileModifyTime(filename).isNewerThan(itsDestFolder->getFileModifyTime(filename)) ) return true; return false; } void Synchronous(const std::string& filename) { if (isNeedUpdate(filename)) { itsDestFolder->backUp(filename); itsSourceFolder->copyFileTo(filename,itsDestFolder); } } private: Folder* itsSourceFolder; Folder* itsDestFolder; };
类MockFolder
class MockFolder : public Folder { public: MockFolder(const std::string& path):Folder(path){} FileModifyTime getFileModifyTime(const std::string& filename) { if (filename == "FileSourceIsNew" && itsPath == "destFolder") return FileModifyTime(std::time(NULL)-1000); if (filename == "FileDestIsNew" && itsPath == "sourceFolder") return FileModifyTime(std::time(NULL)-1000); if (filename == "FolderExist" && itsPath == "destFolder") return FileModifyTime(std::time(NULL)-1000); return FileModifyTime(std::time(NULL)); } bool isDirectory(const std::string& filename) { return (filename == "FolderNotExist" || filename == "FolderExist"); } bool isExist(const std::string& filename) { if(filename == "FileNotExist" || filename == "FolderNotExist") return false; else return true; } //operator void backUp(const std::string& filename) { if(isExist(filename)) std::cout << "Backup \""<< filename << "\"" << std::endl; } void copyFileTo(const std::string& filename, Folder* destFolder) { std::cout << "Copy File \"" << filename << "\"" << std::endl; } };
这样,效果与之前一致,但是Folder接口提取出来,SynchronousSystem不再依赖Folder的具体实现。
至此为至,我们只需要新建一个新类Win32Folder(继承自Folder)来实现文件操作就行。
额,忘了写测试了。
为了实验,我在F盘建了2个文件夹,分别是source和dest,我得构建一些文件和文件夹来做这个实验。
source文件夹中有:
SourceIsNew.txt DestNotExist.txt DestIsSameAsSource.txt DestIsNew.txt DestIsExist SourceIsNew.txt DestNotExist.txt DestIsSameAsSource.txt DestIsNew.txt DestNotExist SourceIsNew.txt DestNotExist.txt DestIsSameAsSource.txt DestIsNew.txt
dest文件夹中有:
SourceIsNew.txt
DestIsSameAsSource.txt
DestIsNew.txt
DestIsExist
SourceIsNew.txt
DestIsSameAsSource.txt
DestIsNew.txt
我其他目录中新建好这些文件,然后每次运行的拷贝过来就行,我的测试代码变为:
//constructor remove_all(path("F:/source")); remove_all(path("F:/dest")); system("xcopy /E F:\\SynTest\\source F:\\source\\ >nul"); system("xcopy /E F:\\SynTest\\dest F:\\dest\\ >nul"); Win32Folder source("F:/source"); Win32Folder dest("F:/dest"); SynchronousSystem SS(&source,&dest); path sourcePath("F:/source"); for (directory_iterator it = directory_iterator(sourcePath); it != directory_iterator(); ++it) { std::string filename = it->path().string(); filename = filename.substr(sourcePath.string().length()+1); SS.Synchronous(filename); }
开始写Win32Folder的代码
class Win32Folder : public Folder { public: Win32Folder(const std::string& path): Folder(path){} //info FileModifyTime getFileModifyTime(const std::string& filename); bool isDirectory(const std::string& filename); bool isExist(const std::string& filename); //operator void backUp(const std::string& filename); void copyFileTo(const std::string& filename, Folder* destFolder); };
他的实现为:
#include "Win32Folder.h" #include <boost/filesystem.hpp> using namespace boost::filesystem; FileModifyTime Win32Folder::getFileModifyTime( const std::string& filename ) { return last_write_time(path(getAbsoluteFilePath(filename))); } bool Win32Folder::isDirectory( const std::string& filename ) { return is_directory(path(getAbsoluteFilePath(filename))); } bool Win32Folder::isExist( const std::string& filename ) { return exists(path(getAbsoluteFilePath(filename))); } void Win32Folder::backUp( const std::string& filename ) { path filePath(getAbsoluteFilePath(filename)); std::string backFilePathString = filePath.parent_path().string() + "\\" + filePath.stem().string() + ".bak" + filePath.extension().string(); path backFilePath(backFilePathString); if (exists(backFilePath)) remove(backFilePath); rename(filePath,backFilePath); } void Win32Folder::copyFileTo( const std::string& filename, Folder* destFolder ) { path file(getAbsoluteFilePath(filename)); path destFile(destFolder->getPath()); destFile /= filename; copy(file,destFile); }
好了,写到这里了。我开始生成,运行。
产生了一个异常错误:
boost::filesystem::rename: 系统找不到指定的文件。: "F:/dest\DestNotExist", "F:/dest\DestNotExist.bak"
怎么会产生这个错误呢,我在哪里调用rename函数呢。。。噢,在backUp函数中,我并没有检查它是否存在。这貌似告诉我上一篇疑问的问题,判断存在是在SynchronousSystem还是在Folder类中,现在貌似有了答案。
于是SynchronousSystem代码中的Synchronous代码变为:
void Synchronous(const std::string& filename) { if (isNeedUpdate(filename)) { if(itsDestFolder->isExist(filename)) itsDestFolder->backUp(filename); itsSourceFolder->copyFileTo(filename,itsDestFolder); } }
继续执行,又出现一个错误:
boost::filesystem::copy_directory: 文件名、目录名或卷标语法不正确。: "F:/source\DestNotExist", "F:/source\DestNotExist"
这是因为在copy目录的时候使用boost/filesystem的copy函数,它调用copy_directory,这个copy_directory不会新建directory,因此得调用创建directory的函数,Win32Folder的copyFileTo得修改成这样:
void Win32Folder::copyFileTo( const std::string& filename, Folder* destFolder ) { path file(getAbsoluteFilePath(filename)); path destFile(destFolder->getPath()); destFile /= filename; if(is_directory(destFile)) create_directory(destFile); else copy(file,destFile); }
其实,这还没有验证他的正确行。下面将写验证代码。
//check CPPUNIT_ASSERT(exists("F:/dest/DestNotExist.txt")); CPPUNIT_ASSERT(exists("F:/dest/SourceIsNew.bak.txt")); CPPUNIT_ASSERT(exists("F:/dest/DestNotExist")); CPPUNIT_ASSERT_EQUAL(last_write_time("F:/source/SourceIsNew.txt"),last_write_time("F:/dest/SourceIsNew.txt"));
测试通过了。