• OO编程实践之“同步文件夹”——实现(3)


    这几天一直在看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"));

    测试通过了。

  • 相关阅读:
    学习总结(二十六)
    学习总结(二十五)
    在知乎学习怎么参加工作
    连分数系列
    Kalman Filter
    五子棋的学习
    Dijkstra
    三等分角、化圆为方、倍立方体
    女朋友走丢数学模型
    传染病模型
  • 原文地址:https://www.cnblogs.com/zhangyonghugo/p/2575283.html
Copyright © 2020-2023  润新知