首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ 跨平台開發實戰:工廠模式封裝平台差異 (一)

     編輯:關於C++

核心原則:最大化共享代碼,最小化平台專用代碼

跨平台專案 (Windows/macOS/Linux) 成功關鍵在於:將平台無關邏輯集中在核心層,平台相關實現隔離在具體模塊,通過**統一介面 (Interface)** 連接。工廠模式是實現此架構的經典選擇。

演進過程:從 #ifdef 混亂到工廠模式清晰架構

階段 1:介面類直接內嵌 #ifdef (反模式)


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);
};

缺點:平台代碼滲透介面,維護困難,新增平台需修改頭文件。

階段 2:介面類統一 Scan(),內部派發 #ifdef


int ProcessList::Scan() {
#if defined(WIN32) return ScanWindows();
#elif defined(DARWIN) return ScanMacOSX();
#elif defined(LINUX) return ScanLinux();
#endif
    return -1;
}

改進:介面純粹,但 #ifdef 隨功能增長散落在各 .cpp,難以統一管理。

階段 3:工廠模式 + 策略模式 (推薦架構)

核心組件:

  1. 抽象介面:ProcessesImpl 定義純虛函數 Scan()、通用 GetCount() 等。
  2. 平台具體實現:WindowsProcessesImpl, LinuxProcessesImpl, CocoaProcessesImpl 繼承並實現 Scan()。
  3. 抽象工廠:ProcessesFactory 單例,提供 MakeProcesses() 返回 ProcessesImpl*。
  4. 平台工廠:WindowsFactory 等實現工廠介面,編譯進平台專用庫。
  5. 客戶端代理: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;
};

Windows 實現 (編譯進 Windows 库)


// 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;
}

Linux 實現要點 (/proc 文件系統)

  • 掃描 /proc/[pid]/cmdline 或 /proc/[pid]/status 獲取進程名。
  • 目錄名純數字即為 PID。
  • 用 opendir/readdir/stat 遍歷,純 POSIX API。

macOS 實現要點 (sysctl 系統調用)

  • 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;
}

擴展新功能 (如文件操作) 的標準流程

  1. 新增抽象介面 FilesImpl + FilesFactory。
  2. 各平台工廠類繼承新介面 (多重繼承或組合)。
  3. 客戶端代理類添加轉發函數。
  4. 唯一修改 #ifdef 的地方: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) 的標準架構基石。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved