青海做网站需要多少钱,内蒙古建设协会网站,app ui设计欣赏 网站,wordpress商城开源设计模式是代码重构的最终目标#xff0c;在程序设计中有效的运用这项技术#xff0c;可以大大提高代码的可读性和可维护性。使整个程序设计结构趋向精致完美。在我维护的FileBrowser模块中可以针对以下方面
应用相应的模式。
1. 使用策略模式来处理文件夹扫描操作
作为网…设计模式是代码重构的最终目标在程序设计中有效的运用这项技术可以大大提高代码的可读性和可维护性。使整个程序设计结构趋向精致完美。在我维护的FileBrowser模块中可以针对以下方面
应用相应的模式。
1. 使用策略模式来处理文件夹扫描操作
作为网络文件浏览器FileBrowser中自然有很多对文件夹的操作。包括计算文件夹信息删除文件夹等这些操作采用的都是标准的文件夹遍历的代码只是对于各文件/文件夹进行不同的处理而已基本代码如下
HANDLE fp FindFirstFile(szLocalFName, fd);
while(TRUE)
{
if(fd.dwFileAttributesFILE_ATTRIBUTE_DIRECTORY) {
Handle the folder… }
Else
{
Handle the file…
} if(!FindNextFile(fp, Fd))
break;
}
显而易见这里很适合使用策略模式定义一个通用的扫描函数定义策略类架构使每个策略对象对应一个文件处理方法。这样可以避免模块中大量的相似的文件夹遍历的代码使之简洁明了而且更加容易扩充。
我们看一下策略模式的定义
结构图: 基于这个思想我设计了以下文件夹扫描结构 class folder_handler
{
public:
folder_handler(const tstring root) : m_root(root){} virtual ~folder_handler(){} virtual bool handle_folder
(const tstring, const WIN32_FIND_DATA){ return true; } virtual bool handle_file(const tstring, const WIN32_FIND_DATA)
{ return true; } virtual bool folder_first(){ return true; } protected: tstring m_root;
}; void scan_folder(const tstring folder_path, folder_handler h)
{ tstring child_path(_T()); string find_name folder_path _T(“//*.*”); WIN32_FIND_DATA fd {0}; HANDLE hFindFile FindFirstFile(find_name.c_str(), fd); if (INVALID_HANDLE_VALUE hFindFile) { cout Cannt access folder folder_path endl; return; } // 先处理父文件夹
if (h.folder_first())
{
h.handle_folder(folder_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, h); } else { m_numfiles;
m_foldersize
MAKELONGLONG(fd.nFileSizeLow, fd.nFileSizeHigh);
h.handle_file(child_path, fd); } } while(FindNextFile(hFindFile, fd)); FindClose(hFindFile); // 后处理父文件夹
if (!h.folder_first())
{
h.handle_folder(folder_path); } }
这样我们想对文件夹做某种特定操作的时候只需要定义一个新类指定对子文件/子文件夹的特定操作即可而不需要关注其遍历过程的细节耦合度非常的低。例如我们想删除文件夹和扫描文件夹同步信息的话那么就可以定义以下两个类。
class folder_deleter : public folder_handler
{
public:
folder_deleter(const tstring parent_folder)
: folder_handler(parent_folder)
{
} virtual bool handle_folder
(const tstring folder_path, const WIN32_FIND_DATA)
{ if (!::RemoveDirectory(folder_path.c_str())) { _tcout _T(Fail to remove the folder ) folder_path endl; _tcout _T(The error code ) ::GetLastError() endl; return false; } return true;
}
virtual bool handle_file
(const tstring file_path, const WIN32_FIND_DATA)
{ if (!::DeleteFile(file_path.c_str())) { _tcout _T(Fail to delete the file ) file_path endl; _tcout _T(The error code ) ::GetLastError() endl; return false; } return true;
} virtual bool folder_first(){ return false; }
}; class folder_inforecorder : public folder_handler
{ typedef struct { DWORD attrib; LONGLONG size; LONGLONG modified_time; tstring path; }file_info; typedef vectorfile_info file_infos; typedef file_infos::iterator fis_iter; typedef file_infos::const_iterator fis_citer; public: folder_inforecorder(const tstring parent_folder) : folder_handler(parent_folder) {} ~folder_inforecorder(){} virtual bool handle_file
(const tstring folder_name, const WIN32_FIND_DATA fd) { file_info fi; fi.attrib fd.dwFileAttributes; fi.size MAKELONGLONG(fd.nFileSizeLow, fd.nFileSizeHigh); fi.modified_time MAKELONGLONG(fd.ftLastWriteTime.dwLowDateTime,
fd.ftLastWriteTime.dwHighDateTime); tstring file_path folder_name _T(//) fd.cFileName; const tstring parentfolder m_root _T(//); tstring::size_type pos file_path.find(parentfolder); if (tstring::npos pos) fi.path file_path.c_str(); else fi.path file_path.substr(pos parentfolder.size(),
tstring::npos).c_str(); m_FileInfos.push_back(fi); return true;
} public: file_infos m_FileInfos;
};
使用的时候调用如下代码即可
scanfolder scan
// 删除路径
scan.run_scan(folder_path, folder_deleter());
folder_inforecorder fir;
// 扫描路径文件信息
scan.run_scan(folder_path, fir); 2. 使用Command命令模式实现文件操作的Undo/Redo
FileBrowser对于文件操作的目前不支持Undo/Redo功能用户所有的操作都是不可逆的所以对于用户来说存在一定的操作风险以及不便。我们可以利用Command命令模式来实现这个功能:
将用户的命令封装成不同的对象支持统一的接口Execute /UnExecute;定义一个命令管理类支持Undo/Redo接口维护一个执行命令对象链;每当用户完成一个操作之后便生成相应的命令对象将其加入命令对象链尾部并将当前命令对象迭代器指向它。执行Undo/Redo功能时根据当前命令对象迭代器调用当前命令对象Execute /UnExecute接口并将当前命令对象迭代器在命令对象链中做相应的移动。
命令对象链操作示意图 当前命令对象迭代器 命令对象链 Figrure 31 命令对象类体系图 大致代码如下
class CCommand
{
public:
CCommand(){}
virtual ~CCommand(){} virtual void Execute() 0;
virtual void UnExecute() 0;
}; class CCommandManager
{
public:
CCommandManager() : m_CurPos(m_cCommandList.end()){}
virtual ~CCommandManager(){ Clear(); } void AddCommand(CCommand* pcCommand)
{ ASSERT(pcCommand); m_cCommandList.erase(m_cCommandList.begin(), m_CurPos); m_cCommandList.insert(m_CurPos, pcCommand); m_CurPos--;
}
BOOL CanUndo()
{
return (m_cCommandList.size() 0
m_CurPos ! m_cCommandList.end());
}
BOOL CanRedo();
{ return (m_cCommandList.size() 0 m_CurPos ! m_cCommandList.begin());
}
CCommand* Undo()
{ CCommand* pcCommand NULL; if (CanUndo()) { pcCommand *m_CurPos; ASSERT(pcCommand); pcCommand-UnExecute(); m_CurPos; } return pcCommand;
} CCommand* Redo()
{ CCommand* pcCommand NULL; if (CanRedo()) { m_CurPos--; pcCommand *m_CurPos; ASSERT(pcCommand); pcCommand-Execute(); } return pcCommand;
}
void Clear(); private: typedef listCCommand* commands; typedef commands::iterator commands_iter; commands m_cCommandList;// 命令对象指针容器 commands_iter m_CurPos;// 当前命令对象迭代器
};
比如我们想支持文件名命名操作Undo/Redo的话,定义以下类即可
class CCommand_Rename
{
public: CCommand_Rename (){} virtual ~CCommand_Rename (){} virtual void Execute()
{
_trename(m_newfilename, m_oldfilename) } virtual void UnExecute()
{
_trename(m_oldfilename, m_newfilename); } private: tstring m_oldfilename, m_newfilename;
}; 3. 使用智能指针来管理内存(代理模式).
在程序中维护大量的指针是非常头疼的问题。我们经常会忘记释放某些已分配内存的指针特别是在一些复杂逻辑和很多出口的函数中从而造成一些内存漏洞。而且有的地方我们为了保证释放内存不得不写很多冗余的代码或者借助于一些不良的语法。
我们来看如下逻辑
1) 先对指针A分配内存A分配内存如果成功那么对指针B分配内存,如果失败返回.
2) 如果B分配失败那么释放指针A返回如果成功。那么对指针C分配内存.
3) 如果C分配失败那么释放指针AB返回。如果成功对指针D分配内存.
4) 如果D分配失败那么释放指针ABC返回
…
一般的代码可能是这样
CA* A new CA;
If (!A) return;
A-f1()
CB* B new CB;
If (!B)
{
delete A;
return 0;
}
B- f2();
CC* C new CC;
If (!C)
{
delete A;
delete B;
return 0;
}
C- f3();
CD* D new CD;
If (!D)
{
delete A;
delete B;
delete C;
return 0;
}
…
这样的代码看上去非常的罗嗦增加了代码阅读的困难而且很容易遗漏一些内存释放的语句。
而有的程序员为了避免啰嗦不得不借助于“goto”语句。
CA* A NULL; CB* B NULL; CC* C NULL; CD* D NULL; A new CA; if (!A) return 0; A- f1(); B new CB; if (!B) { goto CLEANUP; } B- f2(); C new CC; if (!C) { goto CLEANUP; } C- f3(); D new CD; if (!D) { goto CLEANUP; } …
CLEANUP: if(A) delete A; if(B) delete B;
if(C) delete C; return 0;
但是goto语句早就已经是臭名昭著其危害性很多文章里都有详细的描述因此应该尽量避免我们这里可以利用智能指针来避免这种尴尬。
auto_ptrCA A(new CA);
if (!A) return 0;
A-f1();
auto_ptrCB B(new CB);
if (!B) return 0; B-f2();
auto_ptrCC C(new CC); if (!C) return 0; C-f3(); auto_ptrCD D(new CD); if (!D) return 0; …
这样代码简洁得多而且由于它是在函数退出时弹栈调用析构函数对所代理的指针进行删除所以不会存在任何内存漏洞。 单件模式结构图: 4. 使用单件模式来管理资源
作为一个GUI程序FileBrowser自然会和很多资源打交道
目前这部分逻辑实现的不是很理想。有的地方通过全局变量有的是通过全局函数来访问资源.感觉上程序风格不是很统一我们应该尽量避免使用全局变量。另外调用全局函数则每次都要加载释放资源动态库比较的冗余低效。因为资源这块对于模块来讲只有唯一一个实例而且可以被整个系统调用。所以可以使用单件模式来管理它。
定义一个资源管理管理类保证其只有一个实例模块中所有资源调用都是通过调用它的接口结构图如下 大致代码如下
class CResManager
{
protected: CResManager (){ LoadResouce(); } ~ CResManager (){FreeResouce (); } public: static CResManager Instance()
{
static CResManager instance;
return instance;
} void FsLoadString(UINT idString, LPTSTR lptsBuffer, DWORD dwLen);
void FsLoadBitMap(…);
void FsLoadMenu(HMENU* phMenu, UINT idMenu);
… private: void LoadResouce(){…} void FreeResouce(){…} private:
HINSTANCE m_hRes; }
这样用户想读取一个字符串的话调用以下语句即可
CResManager::Instance().FsLoadString(…);
整个模块的资源调用就显得统一简洁高效。 5. 使用备忘录模式来实现本地/远程目录的“前进/后退”功能
Windows自带的浏览器支持前进/后退的功能用户可以来回浏览刚才访问过的路径而不需要记住并敲入路径名很是方便。其实利用备忘录模式可以很容易的在FileBrowser中加入这一功能。
备忘录模式的机制在于定义一个对象状态的结构当对对象进行操作之前保存对象状态撤销操作时根据保存的对象状态恢复此对象。这个描述看上去很像命令模式不过命令模式保存的是对对象的操作而不是对象状态。
由于我们只要保存的文件夹路径这一单一信息所以直接可以设置一个字符串链表即可每次用户浏览新的路径将当前路径名字符串保存到路径链表并设置当前路径迭代器指向此字符串。用户前进/后退时设置浏览器当前路径为路径迭代器所指向路径并对路径迭代器做相应的调整。
路径链表操作示意图 当前路径迭代器 路径字符串链表 大致代码如下:
class CPathControl
{
public: CPathControl(void) : m_bInitialize(false) {} ~CPathControl(void){} void CPathControl::Initialize(const tstring path)
{ ASSERT(m_cPathList.empty()); m_cPathList.push_back(path); m_CurPos m_ PathList.begin(); m_bInitialize true;
}
void AddNewPath(const tstring path)
{
ASSERT(m_bInitialize);
m_CurPos m_ cPathList.insert(m_CurPos, path);
}
void GetCurPath(tstring path)
{ ASSERT(m_bInitialize);
if (m_cPathList.size() 0 m_CurPos ! m_cPathList.end()) (*m_CurPos).swap(path);.
} BOOL CanMoveLast(void)
{
return (m_cPathList.size() 0 m_CurPos ! m_cPathList.begin());
} BOOL CanMoveNext(void)
{
listst_viewinfo::iterator iter m_CurPos; return (m_cPathList.size() 0 iter ! m_cPathList.end());
} void LastView(void)
{
if (!CanMoveLast())
m_CurPos--;
} void NextView(void) { if (CanMoveNext()) m_CurPos;
} public:
listtstring m_cPathList; listtstring::iterator m_CurPos;
private: bool m_bInitialize;
}; class CFsSpDoc
{
…
Public:
void SetNewPath(const tstring path)
{ m_cPathControl. AddNewPath(path); BrowsePath(path);//浏览该路径
}
void Forward()
{
m_cPathControl.NextView(); tstring path(_T(“”)); m_cPathControl.GetPath(path); BrowsePath(path); //浏览该路径
}
void Backward()
{
m_cPathControl.LastView(); tstring path(_T(“”)); m_cPathControl.GetPath(path); BrowsePath(path);
}
…
private: CPathControl m_cPathControl;
}