多视图与对象的配置选项处理优化方案咨询
问题背景
我开发的程序包含多个视图:部分视图内嵌不同对象,部分仅以表格形式展示数据。所有视图及部分视图内的对象都有各自独立的配置选项。
当前实现逻辑:用一个统一的对话框类,为每个对象/视图生成不同标签页来展示或修改配置;同时通过不同的Handler类作为中转指针,将配置选项传递到属性对话框。
我希望直接将视图/对象传入Handler类完成配置修改。之前考虑过用模板实现,但由于各视图/对象的配置选项差异大,配置的应用逻辑完全依赖目标对象本身,用is_same做类型判断又违背了模板的设计初衷,因此寻求更优解。
当前代码示例
创建Handler并传递至对话框的代码
std::unique_ptr<PropertiesHandler> propertiesPtr; #if __cplusplus > 201300 auto ptr = std::make_unique<Handler>( m_conf->m_dbOptions, m_canvas ); #else auto ptr = std::unique_ptr<Handler>( new Handler( m_conf->m_dbOptions, m_canvas ) ); #endif propertiesPtr = std::move( ptr ); propertiesPtr->SetType( m_type == View ? ViewProperties : ObjectProperties );
属性对话框内的类型判断代码
auto type = handler->GetType(); switch( type ) { case ViewProperties: Handler *propHandler = dynamic_cast<Handler*>( handler ); // 创建视图相关的配置标签页 break; // 其他对象类型的case分支 }
解决方案建议
1. 基于多态的抽象Handler重构
这是最直接且易维护的方案,通过抽象基类封装统一接口,每个视图/对象对应专属的Handler子类,彻底消除类型判断和强制转换。
步骤1:定义抽象基类
将PropertiesHandler改造为抽象基类,定义创建配置页、应用配置的纯虚方法:
class PropertiesHandler { public: virtual ~PropertiesHandler() = default; // 创建当前对象/视图对应的配置标签页 virtual void CreatePropertyPages(QDialog* parentDialog) = 0; // 将配置页的修改应用到目标对象/视图 virtual void ApplySettings() = 0; };
步骤2:实现具体Handler子类
每个子类直接持有目标视图/对象的引用,在内部封装对应配置页的创建和配置应用逻辑:
// 视图专属Handler class ViewPropertiesHandler : public PropertiesHandler { private: Canvas& m_targetCanvas; DBOptions& m_dbConfig; public: ViewPropertiesHandler(DBOptions& dbOpts, Canvas& canvas) : m_dbConfig(dbOpts), m_targetCanvas(canvas) {} void CreatePropertyPages(QDialog* parentDialog) override { // 直接基于视图的现有配置初始化标签页 auto generalPage = new ViewGeneralConfigPage(parentDialog, m_targetCanvas.GetViewSettings()); parentDialog->addTab(generalPage, "视图通用设置"); auto dbPage = new ViewDbConfigPage(parentDialog, m_dbConfig); parentDialog->addTab(dbPage, "数据源配置"); } void ApplySettings() override { // 从标签页读取配置,直接更新到视图对象 m_targetCanvas.UpdateViewSettings(...); m_dbConfig.Save(); } }; // 对象专属Handler示例 class ObjectPropertiesHandler : public PropertiesHandler { private: DataObject& m_targetObj; public: ObjectPropertiesHandler(DataObject& obj) : m_targetObj(obj) {} void CreatePropertyPages(QDialog* parentDialog) override { auto attrPage = new ObjectAttrConfigPage(parentDialog, m_targetObj.GetAttributes()); parentDialog->addTab(attrPage, "对象属性"); } void ApplySettings() override { m_targetObj.UpdateAttributes(...); } };
步骤3:简化调用逻辑
创建Handler时直接实例化对应子类,对话框无需任何类型判断,直接调用多态方法:
// 创建Handler的代码 std::unique_ptr<PropertiesHandler> propertiesPtr; if (m_type == View) { propertiesPtr = std::make_unique<ViewPropertiesHandler>(m_conf->m_dbOptions, m_canvas); } else { propertiesPtr = std::make_unique<ObjectPropertiesHandler>(m_selectedObject); } // 属性对话框内的代码 // 直接生成对应配置页 propertiesPtr->CreatePropertyPages(this); // 用户点击确定按钮时 propertiesPtr->ApplySettings();
方案优势
- 彻底移除了
SetType、GetType、switch和dynamic_cast,避免类型转换错误 - 每个Handler与目标对象/视图强绑定,配置逻辑封装在子类中,符合单一职责原则
- 新增视图/对象时只需扩展新的Handler子类,无需修改现有代码,符合开闭原则
2. 可选:访问者模式(适用于复杂继承层级)
如果你的视图和对象存在复杂的继承关系,且需要统一处理多类型配置逻辑,可以采用访问者模式:
- 定义
Configurable接口,包含Accept方法 - 实现
PropertiesVisitor抽象类,为每个可配置类型定义专属访问方法 - 每个视图/对象实现
Accept方法,调用访问者对应的方法 - 对话框通过访问者生成配置页并应用修改
不过对于大多数场景,第一种多态Handler方案已经足够简洁,且学习和维护成本更低。
内容的提问来源于stack exchange,提问作者Igor
相关产品推荐
相关产品推荐

