从重构到模式-使用策略模式扫描文件夹
来源:互联网 发布:js慕课网 编辑:程序博客网 时间:2024/05/17 03:32
在Windows程序开发中,我们经常要和文件夹打交道.而Windows也提供了一些API让我们来进行文件夹遍历访问操作。其中主要有以下几个函数:
1) FindFirstFile
声明:
HANDLE FindFirstFile(
LPCTSTR lpFileName, // file name
LPWIN32_FIND_DATA lpFindFileData // databuffer
);
功能说明:
该函数到一个文件夹(包括子文件夹)去搜索指定文件 如果要使用附加属性去搜索文件
的话 可以使用FindFirstFileEx函数
参数说明:
HANDLE hFindFile搜索的文件句柄 函数执行的时候搜索的是此句柄的下一文件
LPWIN32_FIND_DATA lpFindFileData 指向一个用于保存文件信息的结构体
返回值:
如果调用成功返回一个句柄,可用来做为FindNextFile或 FindClose参数
调用失败 返回为INVALID_HANDLE_VALUE(即-1) ,可调用GetLastError来获取错误信息
2) FindNextFile
声明:
BOOL FindNextFile(
HANDLE hFindFile, //searchhandle
LPWIN32_FIND_DATA lpFindFileData//databuffer
);
功能说明:
继续查找FindFirstFile函数搜索后的文件
HANDLE hFindFile搜索的文件句柄 函数执行的时候搜索的是此句柄的下一文件
LPWIN32_FIND_DATAlpFindFileData 指向一个用于保存文件信息的结构体
返回值:
非零表示成功,零表示失败。如不再有与指定条件相符的文件,会将GetLastError设
置成ERROR_NO_MORE_FILES
3) FindClose
声明:
BOOL FindClose(
HANDLE hFindFile // file search handle
);
功能说明
关闭FindFirstFile创建的搜索句柄
参数说明
HANDLE hFindFile FindFirstFile创建的句柄
返回值
调用成功 返回一个非0值
失败 返回0 可利用GetLastError来得到错误信息
一般我们处理文件夹的代码大致如下:
int folder_func(const string& folder_path,…) {…string find_name = folder_path + "\\*.*";HANDLE hFindFile = FindFirstFile(find_name.c_str(), &fd);if (INVALID_HANDLE_VALUE == hFindFile)return -1;//处理文件夹代码…string child_path("");do {if (_T('.') == fd.cFileName[0])continue;child_path = folder_path + _T("\\") + fd.cFileName;if (FILE_ATTRIBUTE_DIRECTORY & fd.dwFileAttributes) {folder_func (child_path,…);} else {//处理文件代码…}} while(FindNextFile(hFindFile, &fd));FindClose(hFindFile);…}
因为有各式各样的文件/文件夹处理方式,所以我们程序中可能存在很多这样的类似函数。这样的代码存在明显的臭味:代码重复,另外一般大的文件夹扫描时间比较长,如果要把他们放到单独的线程进行处理,这样的函数改动起来也比较麻烦。那么如何重构这样的代码呢?
仔细看看上面的示例代码,我们可以发现各种文件夹处理的扫描操作都是差不多的,所不同的是每个文件/文件夹处理的操作。根据把变化的部分封装起来的设计思想,我们可以把文件/文件夹处理的操作进行抽象:声明一个抽象父类,提供文件/文件夹处理的接口
class folder_handler{public:folder_handler(const string& parent_folder) : m_parentfolder(parent_folder) {}virtual bool handle_folder(const string&, const WIN32_FIND_DATA&){ return true; }virtual bool handle_file(const string&, const WIN32_FIND_DATA&){ return true; }virtual bool folder_first(){ return true; }private:string m_parentfolder;};
然后我们可以定义一个统一的文件夹操作函数:
void scan_folder(const string& folder_path, folder_handler* phandler) {string find_name = folder_path + "\\*.*";WIN32_FIND_DATA fd = {};HANDLE hFindFile = FindFirstFile(find_name.c_str(), &fd);if (INVALID_HANDLE_VALUE == hFindFile) {cout << "Cann't access folder " << folder_path << endl;return;}// 处理文件夹if (phandler && phandler->folder_first())phandler->handle_folder(folder_path, fd);string child_path("");do {if (_T('.') == fd.cFileName[0])continue;child_path = folder_path + _T("\\") + fd.cFileName;if (FILE_ATTRIBUTE_DIRECTORY & fd.dwFileAttributes) {scan_folder(child_path, phandler);} else {if (phandler)phandler->handle_file(child_path, fd);}} while(FindNextFile(hFindFile, &fd));FindClose(hFindFile);hFindFile = NULL;if (phandler && !phandler->folder_first())phandler->handle_folder(folder_path, fd);}
这其实这里用到了一个经典的设计模式:策略模式
我们看下策略模式的详细定义:
概念
策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
Strategy(抽象策略类):
定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,Context使用这个接口调用不同的算法,一般使用接口或抽象类实现。
ConcreteStrategy(具体策略类):
实现了Strategy定义的接口,提供具体的算法实现。
应用场景:
1) 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要
执行的行为
2) 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实
现
3) 对客户(Duck)隐藏具体策略(算法)的实现细节,彼此完全独立。
如果我们想要删除一个文件夹,只需要如下定义一个新类:
class folder_deleter : public folder_handler{public:folder_deleter(const string& parent_folder) : folder_handler(parent_folder) {}virtual bool folder_first(){ return false; }virtual bool handle_folder(const string& file_path, const WIN32_FIND_DATA&) {if (!::RemoveDirectory(file_path.c_str())) {cout << _T("Cann't delete file _T(") << file_path << endl;return false;}return true;}virtual bool handle_file(const string& file_path, const WIN32_FIND_DATA&) {if (!::DeleteFile(file_path.c_str())) {cout << _T("Cann't delete file _T(") << file_path << endl;return false;}return true;}};
并在相应的地方调用下面代码即可:
scan_folder(folder_path,&folder_deleter(folder_path)) ;
现在添加新的文件/文件夹处理方式非常的容易,而且根本不需要修改scan_folder函数,完全符合开放封闭原则。而且由于是一个统一的文件夹扫描函数,改成多线程的也非常方便。另附上完整代码:
#include "stdafx.h"#include "windows.h"#include <iostream>#include <fstream>#include <string>#include <vector>using namespace std;class folder_handler{public:folder_handler(const string& parent_folder) : m_parentfolder(parent_folder) {}virtual bool handle_folder(const string&, const WIN32_FIND_DATA&){ return true; }virtual bool handle_file(const string&, const WIN32_FIND_DATA&){ return true; }virtual bool folder_first(){ return true; }private:string m_parentfolder;};class folder_scanner {struct threadproc_param {threadproc_param(const string& folder_path, folder_scanner* pscanfolder, folder_handler* phandler): m_folderpath(folder_path), m_pscanfolder(pscanfolder), m_pfolderhandler(phandler){}const string& m_folderpath;folder_scanner* m_pscanfolder;folder_handler* m_pfolderhandler;};public:folder_scanner() : m_bstop(false), m_numfiles(0), m_foldersize(0){}void run_scan(const string& folder_path, folder_handler* phandler) {threadproc_param param(folder_path, this, phandler);DWORD dwId = 0;HANDLE hThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)scan_thread, ¶m, 0, &dwId);cout << _T("Scanning folder thread start, thread id:") << dwId << endl;::WaitForSingleObject(hThread, INFINITE);hThread = NULL;cout << _T("Scanning folder thread end") << endl;}void stop(){ m_bstop = true; }DWORD numfiles(){ return m_numfiles; }LONGLONG foldersize(){ return m_foldersize; }void zero_data() { m_numfiles = 0; m_foldersize = 0;}private:static void scan_thread(threadproc_param* p_param) {if (!p_param || !p_param->m_pscanfolder) {cout << _T("Invalid thread proc param.") << endl;return;}p_param->m_pscanfolder->scan_folder(p_param->m_folderpath, p_param->m_pfolderhandler);}void scan_folder(const string& folder_path, folder_handler* phandler) {string find_name = folder_path + "\\*.*";WIN32_FIND_DATA fd = {};HANDLE hFindFile = FindFirstFile(find_name.c_str(), &fd);if (INVALID_HANDLE_VALUE == hFindFile) {cout << "Cann't access folder " << folder_path << endl;return;}if (phandler && phandler->folder_first())phandler->handle_folder(folder_path, fd);string child_path("");do {if (m_bstop) return;if (_T('.') == fd.cFileName[0])continue;child_path = folder_path + _T("\\") + fd.cFileName;if (FILE_ATTRIBUTE_DIRECTORY & fd.dwFileAttributes) {scan_folder(child_path, phandler);} else {m_numfiles++;m_foldersize += ((LONGLONG)fd.nFileSizeHigh << 32) | fd.nFileSizeLow;if (phandler)phandler->handle_file(child_path, fd);}} while(FindNextFile(hFindFile, &fd));FindClose(hFindFile);hFindFile = NULL;if (phandler && !phandler->folder_first())phandler->handle_folder(folder_path, fd);}private:bool m_bstop;DWORD m_numfiles;LONGLONG m_foldersize;};class folder_deleter : public folder_handler{public:folder_deleter(const string& parent_folder) : folder_handler(parent_folder) {}virtual bool folder_first(){ return false; }virtual bool handle_folder(const string& file_path, const WIN32_FIND_DATA&) {if (!::RemoveDirectory(file_path.c_str())) {cout << _T("Cann't delete file _T(") << file_path << endl;return false;}return true;}virtual bool handle_file(const string& file_path, const WIN32_FIND_DATA&) {if (!::DeleteFile(file_path.c_str())) {cout << _T("Cann't delete file _T(") << file_path << endl;return false;}return true;}};class folder_details : public folder_handler{typedef struct {DWORD attrib;LONGLONG size;LONGLONG modified_time;string path;}file_info;typedef vector<file_info> file_infos;typedef file_infos::iterator fis_iter;typedef file_infos::const_iterator fis_citer;public:folder_details(const string& parent_folder) : folder_handler(parent_folder) {}virtual bool handle_file(const string& file_path, const WIN32_FIND_DATA& fd) {file_info fi = { fd.dwFileAttributes, ((LONGLONG)fd.nFileSizeHigh << 32) | fd.nFileSizeLow, ((LONGLONG)fd.ftLastWriteTime.dwHighDateTime << 32) | fd.ftLastWriteTime.dwLowDateTime, file_path};fis.push_back(fi);return true;}void writedata() {string file_path(_T("result.txt"));ofstream ifs(file_path.c_str());if (ifs){ifs << _T("FilePath\t\tSize\tAttrib") << endl;for (fis_iter iter = fis.begin(); iter != fis.end(); iter++)ifs << iter->path << _T("\t\t") << (DWORD)iter->size << _T("\t") << iter->attrib << endl;ShellExecute(NULL, "open", "result.txt", NULL, NULL, SW_SHOWNORMAL); }}file_infos fis;};int _tmain(int argc, _TCHAR* argv[]){int scan_type = 0;string folder_path(_T(""));folder_scanner scan;while (true) {cout << _T("Please input the folder path: ");cin >> folder_path;if (folder_path.empty())break;cout << _T("Please input the scan type: ") << endl << "1: 文件夹基本信息 2: 文件夹详情 3: 删除文件夹" << endl;cin >> scan_type;DWORD d1 = GetTickCount();scan.zero_data();if (1 == scan_type) {scan.run_scan(folder_path, NULL);cout << _T("The folder contains ") << scan.numfiles() << _T(" files.") << endl;cout << _T("The folder size about ") << DWORD(scan.foldersize() >> 20) << _T(" MB") << endl;} else if (2 == scan_type) {folder_details fdetails(folder_path);scan.run_scan(folder_path, &fdetails);cout << _T("Please check the scan result in result.txt.") << endl;fdetails.writedata();} else if (3 == scan_type) {string tmp("");cout << "Are you sure you want to delete the folder '" << folder_path << "'?" << endl << "Yes or No:";cin >> tmp;if (0 != tmp.compare("Yes"))continue;scan.run_scan(folder_path, &folder_deleter(folder_path));cout << _T("The folder ") << folder_path << " has been deleted." << endl;} elsecout << _T("Please input valid scan type.") << endl;cout << _T("Cost time about ") << (GetTickCount() - d1) / 1000 << _T(" second(s).") << endl;}return 0;}
- 从重构到模式-使用策略模式扫描文件夹
- 从重构到工厂模式
- 阅读者(二十二):从重构到模式
- 从重构的角度学习bridge设计模式
- 从重构的角度学习bridge设计模式
- Javascript组合模式-扫描文件夹
- 重构是提高可测试性的主要手段 《设计模式》《代码重构》《从重构到模式》 《反模式》 重构时机 编写测试时候 修改BUG时候
- Spring中使用到的设计模式 - 策略模式
- 策略模式的使用
- 策略模式使用场景
- 策略模式的使用
- 从简单模式到策略模式
- spring AOP策略模式使用
- 策略模式(使用Java实现)
- android 策略设计模式使用
- 策略模式的演化使用
- 策略模式的简单使用
- 策略模式(从放弃到入门)
- 认识程序集
- CWnd::WindowProc的理解
- UIWEBVIEW 与JS 如何回调交互
- test
- HTML5 Canvas 逐帧动画的实现
- 从重构到模式-使用策略模式扫描文件夹
- php class中public,private,protected的区别,以及实例
- sharepoint 部署的solution状态一直是False
- php簡繁體轉換
- jspsmart 上传图片
- VS内置宏和简写符
- 软考过后
- uboot去除启动时网络检测
- iframe显示高度自适应兼容多浏览器