利用Qt元对象技术防止工厂模式下代码臃肿问题,QT 动态创建对象(第2种方法)
問題的提出:
? ?近來要編寫一個仿真液壓、電力、機械的軟件,如下為液壓的:
?可以看到液壓圖中很多液壓元器件,這些元器件的id、名稱等都是從json配置文件讀取的,配置文件格式如下:
{"ClassName":"CComponentLineWidget","Object": {"displayname": "直線","drawoutline": false,"id": "66c7673c-2826-339e-4b38-a67f840680bd","linetype": 1,"pencolor": "#000000","penwidth": 4,"treeicon": "直線.svg","viewicon": "直線.svg","growstep": 1,"points": ""}}其中ClassName是實現該元器件的類?,如上面json中的CComponentLineWidget就是實現“直線”的類,"Object"節點是直線的屬性。
最初的設想是:
定義一個CComponentWidget的基類,其它如上圖液壓業務中的各種油缸、電磁閥、壓力表等都從?基類派生。然后定義一個類似如下的工廠模式,new出各種控件,如下:
CComponent* CComponent::createComponentFactory(const QString& strComponentId) {if (!strComponentId.compare(g_pszComposeComponentId, Qt::CaseInsensitive)) {pComponent = new CComposeComponent(strComponentId);}else if (!strComponentId.compare(g_pszValveComponentId, Qt::CaseInsensitive)) {pComponent = new CSingleValve(strComponentId);}else if (!strComponentId.compare(g_pszControlValveComponentId, Qt::CaseInsensitive)) {pComponent = new CControlValve(strComponentId);}else if (!strComponentId.compare(g_pszHighPressureBallValveComponentId, Qt::CaseInsensitive)) {pComponent = new CHighPressureBallValve(strComponentId);}........ // 這里還有好多各種元器件的構建代碼 }其中CComponentWidget為元器件的基類,CComposeComponent、CSingleValve、CControlValve等都是表示上圖的元器件實現類,是派生自CComponent的子類。
? ? ? 現實的情況是:單單就液壓業務,各種元器件都有60多個,再加上后期的電力、機械等,各種元器件不下200種,那么這個工廠模式下的if、else將會無比巨大,且更要命的是:每種元器件的屬性要彈出對話框,屬性對話框中的屬性就是讀自上面json配置文件中的"Object"節點,以便實現讓用戶設置元器件屬性,用戶選擇好后,這些屬性要保存到本地,即更新上面json配置文件中的"Object"節點,后期加載時,這些用戶設置好的屬性要附加到元器件上來,這么下來,200多個元器件的屬性設置、保存、加載也是巨大的工作量,為了解決這個問題,我們采用Qt的元對象技術,只要十幾行代碼就可以搞定工廠模式下的數百個if、else才搞定的工作。下面詳細說明:
1)在基類CComponentWidget中定義一個構建各種子類對象的靜態方法,如下:
// 加載 CComponentWidget* CComponentWidget::load(const QJsonObject& json) {QString className = json["ClassName"].toString(); // (1)if (className.isEmpty())return nullptr;int type = QMetaType::type((className + "*").toLatin1()); // (2)if (type == 0){assert(0);return nullptr;}const QMetaObject* metaObj = QMetaType::metaObjectForType(type);// (3)if (metaObj == nullptr){assert(0);return nullptr;}QObject* obj = metaObj->newInstance(); // (4)if (obj == nullptr){assert(0);return nullptr;}auto objHash = json["Object"].toObject().toVariantHash();for (auto iter = objHash.constBegin(); iter != objHash.constEnd(); ++iter) // (5){obj->setProperty(iter.key().toStdString().c_str(), iter.value());}return dynamic_cast<CComponentWidget*>(obj); }該段代碼說明如下:
(1)句代碼表示從json中讀取ClassName類的名稱,也就是元器件的實現類,這里是直線的實現類? ? ? ? CComponentLineWidget。
(2)句代碼是利用Qt的元類型類取出字符串:“CComponentLineWidget*”即指向?
? ? ?CComponentLineWidget類指針的句柄,說白了也就是返回一個
? ? ?CComponentLineWidget類的指針。
? ? 請注意:要使該句代碼作用,也就是type不返回QMetaType::UnknownType即0,必須在該load函數調用之前,利用qRegisterMetaType函數對要創建的類進行注冊,否則type因為沒有注冊該類,找不到該類,從而返回返回QMetaType::UnknownType導致后續流程都不能執行。以直線元件為例,調用如下:
// 本句必須保證在load函數之前調用 qRegisterMetaType<CComponentLineWidget*>();(3):根據返回的指向某個類的句柄創建?Qt的元對象metaObj。
(4):再調用Qt的元對象metaObj的newInstance()方法,此時會調用元器件類的構造函數。請注意:元器件的構造函數必須標識Q_INVOKABLE,否則不會調用,如下為Qt官方對newInstance()方法說明:
Note that only constructors that are declared with the Q_INVOKABLE modifier are made available through the meta-object system.本例CComponentLineWidget構造函數聲明如下:
Q_INVOKABLE CComponentLineWidget(QGraphicsItem *parent = nullptr)(5):讀取json中該元器件的各種屬性到Qt元對象屬性中,請注意:要使Qt元對象屬性起作用,必須用Q_PROPERTY在類的聲明文件的private區將屬性標識出來,且要提供相應的set、get函數,具體請參考Qt幫助手冊中Q_PROPERTY使用說明。在(5)這個for循環中會自動調用你寫好的set函數,從而實現將json中的屬性設置到元器件上。
以上是元器件的創建及屬性加載,再來看元器件的屬性保存,如下:
// 保存 void CComponentWidget::save(QJsonObject& json) {QJsonObject obj;auto metaObject = this->metaObject();(1)for (int index = 0; index < metaObject->propertyCount(); ++index)(2){auto metaProperty = metaObject->property(index);QString name = metaProperty.name();QVariant var = metaProperty.read(this);obj[name] = QJsonValue::fromVariant(var);}json["Object"] = obj;json["ClassName"] = metaObject->className(); }這個就很簡單了,(1)句代碼就去取出元器件實現類的元對象,然后變量對元對象的所有屬性遍歷、更新相應的json屬性節點。然后在元器件基類即CComponentWidget中相應雙擊事件,彈出屬性對話框,如下:
,QT 動態創建對象(第1種方法)參見:QT 動態創建對象(第一種方法)_danshiming的博客-CSDN博客在我繼續一系列的Qt數據序列化文章之前,有一個相對重要的需要提及的話題,那就是:基于類名動態創建類對象的能力。 假定現在我們要創建一系列的形狀,形狀是一個抽象類,實際類是存儲在一個列表中的各種各樣的派生類:矩形、圓等等。在序列化期間,我們可以保存每一項的類名和對象數據,在反序列化(即加載數據)時,我們需要能夠創建合適類實例的能力,這就是要用到一個對象工廠的地方。在支持反射的語言中,例如C#、Java,僅需要幾行代碼就可以從一個跟定的類名字符串獲得一個類實例。但是在c++中沒有這樣的機制。...https://blog.csdn.net/danshiming/article/details/119923840
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的利用Qt元对象技术防止工厂模式下代码臃肿问题,QT 动态创建对象(第2种方法)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vscode如何变为中文(VScode)
- 下一篇: 凝胶电池的特性是什么