13 KiB
13 KiB
单例模式
**本文引用的文件** - [GeoMative.cpp](file://cpp/Main/GeoMative.cpp) - [GeoMative.h](file://h/GeoMative.h) - [Global.cpp](file://cpp/Main/Global.cpp) - [DevManager.h](file://h/DevManager.h) - [ProManager.h](file://h/ProManager.h) - [TdManager.h](file://h/TdManager.h)目录
引言
本文件聚焦于CGeoMativeApp类在MFC框架下的单例模式实现与应用。CGeoMativeApp继承自CWinApp,作为全局应用程序对象,负责应用生命周期管理(InitInstance/ExitInstance)、全局资源初始化与释放、以及通过成员指针(如m_pDevManager、m_pProManager等)向全应用提供统一的核心管理器访问入口。本文将从代码结构、控制流、资源管理一致性与全局状态统一性等方面,系统解析该单例模式的设计与实践,并给出典型应用场景与潜在风险提示。
项目结构
- 应用程序入口与主框架位于Main目录,包含CGeoMativeApp类及其声明。
- 全局单例实例theApp在Global.cpp中定义,确保全局唯一性。
- 核心管理器(设备、项目、测试数据等)在各自的头文件中声明,CGeoMativeApp在InitInstance中创建并持有它们的指针,形成全局可访问的资源中心。
graph TB
subgraph "主程序层"
A["CGeoMativeApp<br/>全局应用对象"]
B["MainFrm<br/>主MDI框架"]
end
subgraph "管理器层"
C["CDevManager<br/>设备管理"]
D["CProManager<br/>项目管理"]
E["CTdManager<br/>测试数据管理"]
F["CExecManager<br/>执行管理"]
G["CSptManager<br/>脚本管理"]
H["CIOManager<br/>IO管理"]
end
subgraph "外部资源"
I["数据库连接<br/>_ConnectionPtr"]
J["网络操作<br/>CNetWorkOper"]
end
A --> B
A --> C
A --> D
A --> E
A --> F
A --> G
A --> H
A --> I
A --> J
图表来源
章节来源
核心组件
- CGeoMativeApp:从CWinApp派生,重写InitInstance/ExitInstance,承担应用启动与退出阶段的初始化与清理工作;同时维护全局管理器指针,提供跨模块访问。
- theApp:全局唯一实例,由Global.cpp定义,保证单例语义。
- 管理器集合:设备管理、项目管理、测试数据管理、执行管理、脚本管理、IO管理等,均由CGeoMativeApp在InitInstance中创建并持有。
章节来源
架构总览
CGeoMativeApp作为全局应用对象,贯穿应用生命周期:
- 启动阶段:InitInstance负责初始化MFC、主窗口、数据库连接、网络、线程、配置读取、管理器创建等。
- 运行阶段:各模块通过CGeoMativeApp提供的全局指针访问核心资源。
- 退出阶段:ExitInstance负责反向销毁顺序、关闭数据库连接、释放资源、清理线程与临界区。
sequenceDiagram
participant App as "CGeoMativeApp"
participant Win as "CWinApp基类"
participant UI as "主窗口(MainFrm)"
participant DB as "数据库连接"
participant Net as "网络操作"
participant Mgr as "管理器集合"
App->>Win : 调用基类构造
App->>App : 构造函数初始化成员指针为空
App->>Win : InitInstance()
Win-->>App : 返回TRUE/FALSE
App->>UI : 创建并显示主窗口
App->>DB : 建立数据库连接
App->>Mgr : 创建管理器实例并赋值给m_p*指针
App->>Net : 初始化网络与线程
App-->>UI : 设置状态栏/菜单/语言
App->>Win : 进入消息循环
App->>Win : ExitInstance()
Win-->>App : 返回退出码
App->>Mgr : 逐个delete管理器
App->>DB : 关闭连接并释放
App->>Net : 清理网络与线程
App->>UI : 删除主窗口
图表来源
详细组件分析
CGeoMativeApp单例实现与生命周期
- 单例语义:theApp在Global.cpp中定义为全局变量,确保应用范围内仅存在一个CGeoMativeApp实例,从而天然满足单例要求。
- 生命周期管理:
- 构造函数:初始化成员指针为空,设置默认运行参数。
- InitInstance:完成MFC初始化、主窗口创建、数据库连接、网络初始化、线程创建、配置读取、管理器创建等。
- ExitInstance:逆序释放管理器、关闭数据库连接、释放网络资源、销毁主窗口、清理线程与临界区。
classDiagram
class CWinApp
class CGeoMativeApp {
+CGeoMativeApp()
+~CGeoMativeApp()
+InitInstance() bool
+ExitInstance() int
+PreTranslateMessage(pMsg) bool
+OnFileExit() void
+OnHelpAbout() void
+m_pDevManager : CDevManager*
+m_pProManager : CProManager*
+m_pTdManager : CTdManager*
+m_pExecManager : CExecManager*
+m_pSptManager : CSptManager*
+m_pIOManager : CIOManager*
+m_pConnection : _ConnectionPtr
+m_NetWorkOper : CNetWorkOper
}
CGeoMativeApp --|> CWinApp
图表来源
章节来源
启动流程与资源初始化(InitInstance)
- MFC与OLE初始化:启用MFC、OLE、控件容器等。
- 主窗口创建:加载并显示MDI主框架。
- 数据库连接:创建ADO连接,打开数据库文件,设置锁策略。
- 网络与线程:初始化网络操作、启动系统时间线程、扫描线程等。
- 配置读取:从配置文件读取传输方式、用户信息、语言、时区等。
- 管理器创建:按顺序创建设备、项目、测试数据、执行、脚本、IO等管理器实例,并保存到m_p*指针,供后续模块使用。
- 登录与网络:根据传输方式决定在线/离线登录流程,启动心跳包或广播等。
- 状态与日志:初始化日志、清理旧日志、设置状态栏文本。
flowchart TD
Start(["进入InitInstance"]) --> OLE["初始化MFC/OLE/控件容器"]
OLE --> Win["创建并显示主窗口"]
Win --> DB["建立数据库连接"]
DB --> Net["初始化网络与线程"]
Net --> Cfg["读取配置信息"]
Cfg --> Mgrs["创建管理器实例并赋值到m_p*"]
Mgrs --> Login["在线/离线登录处理"]
Login --> Threads["启动定时器/心跳/扫描线程"]
Threads --> Ready(["应用就绪"])
图表来源
章节来源
退出流程与资源回收(ExitInstance)
- 逆序释放:先释放主窗口,再逐个delete管理器,最后关闭数据库连接。
- 网络与线程:停止网络操作、等待扫描线程安全退出、释放线程句柄。
- COM与临界区:关闭COM、删除扫描临界区。
- 错误处理:捕获异常并调用基类ExitInstance,保证退出路径稳定。
flowchart TD
ExitStart(["进入ExitInstance"]) --> CloseWin["删除主窗口"]
CloseWin --> DelMgrs["逐个delete管理器"]
DelMgrs --> CloseDB["关闭数据库连接并释放"]
CloseDB --> NetClean["停止网络操作/线程清理"]
NetClean --> COM["CoUninitialize/删除临界区"]
COM --> BaseExit["调用基类ExitInstance"]
BaseExit --> ExitEnd(["退出完成"])
图表来源
章节来源
全局管理器的创建与使用
- 设备管理器:负责设备发现、注册、远程设备管理等。
- 项目管理器:负责工程、测区、DMS树等结构的创建与同步。
- 测试数据管理器:负责各类测试数据的导入、上传、转换等。
- 执行、脚本、IO管理器:分别负责任务执行、脚本管理、IO交互等。
classDiagram
class CGeoMativeApp {
+m_pDevManager : CDevManager*
+m_pProManager : CProManager*
+m_pTdManager : CTdManager*
+m_pExecManager : CExecManager*
+m_pSptManager : CSptManager*
+m_pIOManager : CIOManager*
}
class CDevManager
class CProManager
class CTdManager
class CExecManager
class CSptManager
class CIOManager
CGeoMativeApp --> CDevManager : "创建并持有"
CGeoMativeApp --> CProManager : "创建并持有"
CGeoMativeApp --> CTdManager : "创建并持有"
CGeoMativeApp --> CExecManager : "创建并持有"
CGeoMativeApp --> CSptManager : "创建并持有"
CGeoMativeApp --> CIOManager : "创建并持有"
图表来源
章节来源
单例模式在MFC中的典型应用场景
- 全局状态集中管理:通过CGeoMativeApp集中管理数据库连接、网络、线程、配置等,避免分散在各模块导致状态漂移。
- 资源生命周期统一:InitInstance集中初始化,ExitInstance集中释放,确保资源分配与回收成对出现,降低泄漏风险。
- 模块间解耦:各模块通过m_p*指针访问核心资源,无需自行创建或管理,降低耦合度。
- 全局事件与消息:通过OnFileExit/OnHelpAbout等消息处理,统一响应用户操作。
章节来源
单例模式的潜在风险与建议
- 线程安全:CGeoMativeApp作为全局单例,若多线程并发访问其成员(尤其是共享资源如数据库连接、网络句柄),需考虑加锁或使用线程安全接口。
- 资源竞争:多个管理器共享同一数据库连接,应避免在同一事务中长时间占用连接,防止阻塞。
- 依赖膨胀:全局对象可能成为“上帝对象”,建议通过接口抽象与依赖注入逐步拆分职责。
- 测试困难:全局状态使单元测试难以隔离,建议在测试环境中提供可替换的全局对象或工厂。
章节来源
依赖关系分析
- CGeoMativeApp依赖于多个管理器类(设备、项目、测试数据、执行、脚本、IO),并在InitInstance中创建实例。
- CGeoMativeApp还依赖数据库连接与网络操作,用于数据持久化与远程通信。
- 全局单例theApp确保CGeoMativeApp在整个进程内唯一,避免重复初始化与资源冲突。
graph LR
App["CGeoMativeApp"] --> DevMgr["CDevManager"]
App --> ProMgr["CProManager"]
App --> TdMgr["CTdManager"]
App --> ExecMgr["CExecManager"]
App --> SptMgr["CSptManager"]
App --> IoMgr["CIOManager"]
App --> Conn["_ConnectionPtr"]
App --> Net["CNetWorkOper"]
图表来源
章节来源
性能考量
- 启动阶段的I/O与网络初始化会阻塞主线程,建议在后台线程中进行非关键初始化,或采用延迟初始化策略。
- 数据库连接与事务:批量操作时合并事务,减少提交次数;避免长事务占用连接。
- 线程与临界区:扫描线程与系统时间线程需合理调度,避免CPU占用过高;临界区保护共享数据结构。
- 日志与磁盘:日志写入应缓冲并异步化,避免频繁磁盘I/O影响UI响应。
故障排查指南
- 数据库连接失败:检查数据库路径、权限与密码;确认InitInstance中连接逻辑与异常分支。
- 网络初始化失败:检查IP/端口配置、网络连通性与线程启动结果。
- 管理器创建异常:关注异常捕获与回滚逻辑,定位具体管理器初始化问题。
- 退出异常:ExitInstance中捕获异常并调用基类ExitInstance,确保进程稳定退出。
章节来源
结论
CGeoMativeApp通过theApp全局单例与CWinApp生命周期钩子,实现了应用级资源的集中管理与统一释放。InitInstance/ExitInstance的职责划分清晰,配合管理器集合的创建与持有,为全应用提供了稳定的全局访问入口。实践中应注意线程安全、资源竞争与依赖膨胀等问题,以确保系统的健壮性与可维护性。