从重构到模式-使用策略模式扫描文件夹

在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;	
		} else
			cout << _T("Please input valid scan type.") << endl;
		cout << _T("Cost time about ") << (GetTickCount() - d1) / 1000 << _T(" second(s).") << endl;
	}

	return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值