跨平台專案 (Windows/macOS/Linux) 成功關鍵在於:將平台無關邏輯集中在核心層,平台相關實現隔離在具體模塊,通過**統一介面 (Interface)** 連接。工廠模式是實現此架構的經典選擇。
class ProcessList {
public:
#if defined(WIN32) int ScanWindows(); #endif
#if defined(DARWIN) int ScanMacOSX(); #endif
#if defined(LINUX) int ScanLinux(); #endif
int GetCount(); const char* GetName(int i); int GetPID(int i);
};
缺點:平台代碼滲透介面,維護困難,新增平台需修改頭文件。
int ProcessList::Scan() {
#if defined(WIN32) return ScanWindows();
#elif defined(DARWIN) return ScanMacOSX();
#elif defined(LINUX) return ScanLinux();
#endif
return -1;
}
改進:介面純粹,但 #ifdef 隨功能增長散落在各 .cpp,難以統一管理。
核心組件:
ProcessesImpl 定義純虛函數 Scan()、通用 GetCount() 等。WindowsProcessesImpl, LinuxProcessesImpl, CocoaProcessesImpl 繼承並實現 Scan()。ProcessesFactory 單例,提供 MakeProcesses() 返回 ProcessesImpl*。WindowsFactory 等實現工廠介面,編譯進平台專用庫。ProcessList 持有 ProcessesImpl*,轉發調用。
// Processes.h
class ProcessesImpl {
public:
virtual ~ProcessesImpl() = default;
virtual int Scan() = 0; // 平台相關
int GetCount() const { return procs.size(); } // 通用邏輯
const char* GetName(int i) const { return procs[i].name.c_str(); }
int GetPID(int i) const { return procs[i].pid; }
protected:
struct Proc { int pid; string name; };
vector procs; // 共享數據結構
};
// ProcessesFactory.h
class ProcessesFactory {
public:
static ProcessesFactory* GetFactory(); // 單例入口
virtual ~ProcessesFactory() = default;
virtual ProcessesImpl* MakeProcesses() = 0;
};
// WindowsProcessesImpl.cpp
#include
#include
#include "Processes.h"
class WindowsProcessesImpl : public ProcessesImpl {
int Scan() override {
procs.clear();
DWORD pids[1024], cbNeeded;
EnumProcesses(pids, sizeof(pids), &cbNeeded);
for (unsigned i = 0; i < cbNeeded/sizeof(DWORD); ++i) {
HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, FALSE, pids[i]);
if (h) {
char name[MAX_PATH] = "";
GetModuleBaseNameA(h, NULL, name, MAX_PATH);
procs.push_back({(int)pids[i], name});
CloseHandle(h);
}
}
return procs.size();
}
};
// WindowsFactory.cpp
class WindowsFactory : public ProcessesFactory {
public:
static WindowsFactory* GetFactoryInstance() {
static WindowsFactory inst; return &inst;
}
ProcessesImpl* MakeProcesses() override { return new WindowsProcessesImpl; }
};
// 僅在 ProcessesFactory::GetFactory() 中出現 #ifdef
ProcessesFactory* ProcessesFactory::GetFactory() {
static ProcessesFactory* factory = nullptr;
if (!factory) {
#if defined(HAVE_WIN32)
factory = WindowsFactory::GetFactoryInstance();
#elif defined(HAVE_LINUX)
factory = LinuxFactory::GetFactoryInstance();
#elif defined(HAVE_MACOS)
factory = CocoaFactory::GetFactoryInstance();
#endif
}
return factory;
}
/proc/[pid]/cmdline 或 /proc/[pid]/status 獲取進程名。opendir/readdir/stat 遍歷,純 POSIX API。sysctl(CTL_KERN, KERN_PROC, KERN_PROC_ALL, ...) 獲取 kinfo_proc 陣列。kinfo_proc 結構體版本差異,建議封裝 getBSDProcessList 輔助函數。
int main() {
ProcessList pl; // 構造時自動獲取工廠創建平台實現
pl.Scan();
for (int i = 0; i < pl.GetCount(); ++i)
cout << pl.GetPID(i) << ": " << pl.GetName(i) << endl;
}
FilesImpl + FilesFactory。FilesFactory::GetFactory()。CMake/Makefile 定義平台宏:
# CMakeLists.txt
if(WIN32) add_definitions(-DHAVE_WIN32)
elseif(APPLE) add_definitions(-DHAVE_MACOS)
elseif(UNIX) add_definitions(-DHAVE_LINUX)
endif()
# 各平台源文件編譯進對應靜態庫,主程序鏈接公共庫 + 當前平台庫
工廠模式將 #ifdef **集中限制在工廠單例獲取函數** 中,實現「修改關閉,擴展開放」。新增平台只需提供新具體實現與工廠,無需動核心代碼。這是大型跨平台 C++ 專案 (如 Chrome, Qt, Unreal Engine) 的標準架構基石。