我确实有想过 "新增类也不用重新编译主程序"。我理解说,这相当于进一步的解耦合了。
是的,这会进一步把主程序和具体业务界面解耦。
当前的关系是:
主程序 ├── 容器与窗口工厂 └── C、D、E 的实现主程序虽然通过名称选择内容,但仍包含这些类的实现,所以新增类需要重新编译主程序。
进一步可以变成:
主程序:加载 UI、提供容器、管理插件 统一接口:约定插件如何创建 QWidget 插件 A.dll:提供 C、D 插件 B.dll:提供 E 插件 F.dll:提供新界面 F这样新增F的流程就是:
- 编写并单独编译
F.dll。 - 放入程序的
plugins目录。 - 在
main.ui中把容器的contentClass设置为插件提供的名称。 - 重启程序,加载新界面。
主程序不用重新编译,新增的插件本身仍然需要编译。
我们已有插件加载器和窗口工厂,下一步可以把它们接起来:让插件通过统一接口提供“可创建的界面名称”和“创建 QWidget 的方法”,工厂再按名称调用对应插件。
还需要处理好一个生命周期约束:插件创建的窗口必须先销毁,才能卸载对应 DLL。同时保持 Qt、编译器和接口版本兼容,这套机制就能稳定扩展。
已实现:插件可以通过WidgetProvider接口声明并创建 QWidget,主程序自动注册这些类型。
示例ContentPlugin.dll提供:
plugin.f:计数面板plugin.g:文本输入面板
容器的contentClass填对应名称即可加载。以后新增类只需编译插件 DLL,主程序不用重新编译。
修改左侧WidgetContainer:
重启软件:
我在思考一些事情:
这意味着我可以在ContentPlugin中创建100个子类QWidget,然后,
修改QWidget *ContentPlugin::createWidget(const QString &name, QWidget *parent),
就可以实现自由地加载子类了吗
对。你可以在ContentPlugin中实现 100 个QWidget子类,然后维护两处对应关系:
widgetClasses():声明插件提供哪些名称。createWidget():根据名称创建对应的类。
例如:
QStringList ContentPlugin::widgetClasses() const { return {"panel.chart", "panel.device", "panel.settings"}; } QWidget *ContentPlugin::createWidget( const QString &name, QWidget *parent) { if (name == "panel.chart") return new ChartWidget(parent); if (name == "panel.device") return new DeviceWidget(parent); if (name == "panel.settings") return new SettingsWidget(parent); return nullptr; }程序就会把ChartWidget装进去。
然后,在main.ui的容器中配置。
新增类后,只需重新编译并替换ContentPlugin.dll,再重启程序;主程序不用重新编译。已有类之间的选择则只需修改 UI。
Release v1.0.4:支持插件提供 QWidget,新增界面无需重编译主程序 · lianpeiliang/LearnFramework