免责声明:
正如前面的答案所指出的那样,在当前示例中使用MVC是过度的。问题的目标是理解基本概念,并通过简单的示例来使用它们,以便在修改更复杂的数据(数组、对象)的更大程序中使用它们。
我正在尝试在C++和QT中实现MVC模式,类似于此处的问题: 该程序有两个行编辑框:
- mHexLineEdit
- mDecLineEdit
- mConvertToHexButton
- mConvertoDecButton
- mClearButton
与其他问题的不同之处在于,我正在尝试实现主题/观察者模式,在模型更改后更新视图。
Model.h
#ifndef MODEL_H
#define MODEL_H
#include <QString>
#include <Subject>
class Model : virtual public Subject
{
public:
Model();
~Model();
void convertDecToHex(QString iDec);
void convertHexToDec(QString iHex);
void clear();
QString getDecValue() {return mDecValue;}
QString getHexValue() {return mHexValue;}
private:
QString mDecValue;
QString mHexValue;
};
#endif // MODEL_H
Model.cpp
#include "Model.h"
Model::Model():mDecValue(""),mHexValue(""){}
Model::~Model(){}
void Model::convertDecToHex(QString iDec)
{
mHexValue = iDec + "Hex";
notify("HexValue");
}
void Model::convertHexToDec(QString iHex)
{
mDecValue = iHex + "Dec";
notify("DecValue");
}
void Model::clear()
{
mHexValue = "";
mDecValue = "";
notify("AllValues");
}
View.h
#ifndef VIEW_H
#define VIEW_H
#include <QtGui/QMainWindow>
#include "ui_View.h"
#include <Observer>
class Controller;
class Model;
class View : public QMainWindow, public Observer
{
Q_OBJECT
public:
View(QWidget *parent = 0, Qt::WFlags flags = 0);
~View();
void setController(VController* iController);
void setModel(VModel* iModel);
QString getDecValue();
QString getHexValue();
public slots:
void ConvertToDecButtonClicked();
void ConvertToHexButtonClicked();
void ClearButtonClicked();
private:
virtual void update(Subject* iChangedSubject, std::string iNotification);
Ui::ViewClass ui;
Controller* mController;
Model* mModel;
};
#endif // VIEW_H
View.cpp
#include "View.h"
#include "Model.h"
#include "Controller.h"
#include <QSignalMapper>
VWorld::VWorld(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
ui.setupUi(this);
connect(ui.mConvertToHexButton,SIGNAL(clicked(bool)),this,SLOT(ConvertToHexButtonClicked()));
connect(ui.mConvertToDecButton,SIGNAL(clicked(bool)),this,SLOT(ConvertToDecButtonClicked()));
connect(ui.mClearButton,SIGNAL(clicked(bool)),this,SLOT(ClearButtonClicked()));
}
View::~View(){}
void View::setController(Controller* iController)
{
mController = iController;
//connect(ui.mConvertToHexButton,SIGNAL(clicked(bool)),this,SLOT(mController->OnConvertToHexButtonClicked(this)));
//connect(ui.mConvertToDecButton,SIGNAL(clicked(bool)),this,SLOT(mController->OnConvertToDecButtonClicked(this)));
//connect(ui.mClearButton,SIGNAL(clicked(bool)),this,SLOT(mController->OnClearButtonClicked(this)));
}
void View::setModel(Model* iModel)
{
mModel = iModel;
mModel->attach(this);
}
QString View::getDecValue()
{
return ui.mDecLineEdit->text();
}
QString View::getHexValue()
{
return ui.mHexLineEdit->text();
}
void View::ConvertToHexButtonClicked()
{
mController->OnConvertToHexButtonClicked(this);
}
void View::ConvertToDecButtonClicked()
{
mController->OnConvertToDecButtonClicked(this);
}
void VWorld::ClearButtonClicked()
{
mController->OnClearButtonClicked(this);
}
void View::update(Subject* iChangedSubject, std::string iNotification)
{
if(iNotification.compare("DecValue") == 0)
{
ui.mDecLineEdit->setText(mModel->getDecValue());
}
else if(iNotification.compare("HexValue") == 0)
{
ui.mHexLineEdit->setText(mModel->getHexValue());
}
else if(iNotification.compare("AllValues") == 0)
{
ui.mDecLineEdit->setText(mModel->getDecValue());
ui.mHexLineEdit->setText(mModel->getHexValue());
}
else
{
//Unknown notification;
}
}
Controller.h
#ifndef CONTROLLER_H
#define CONTROLLER_H
//Forward Declaration
class Model;
class View;
class Controller
{
public:
Controller(Model* iModel);
virtual ~Controller();
void OnConvertToDecButtonClicked(View* iView);
void OnConvertToHexButtonClicked(View* iView);
void OnClearButtonClicked(View* iView);
private:
Model* mModel;
};
#endif // CONTROLLER_H
Controller.cpp
#include "Controller.h"
#include "Model.h"
#include "View.h"
Controller::Controller(Model* iModel):mModel(iModel){}
Controller::~Controller(){}
void Controller::OnConvertToDecButtonClicked(View* iView)
{
QString wHexValue = iView->getHexValue();
mModel->convertHexToDec(wHexValue);
}
void Controller::OnConvertToHexButtonClicked(View* iView)
{
QString wDecValue = iView->getDecValue();
mModel->convertDecToHex(wDecValue);
}
void Controller::OnClearButtonClicked(View* iView)
{
mModel->clear();
}
main.cpp
#include "View.h"
#include "Model.h"
#include "Controller.h"
#include <QtGui/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Model wModel;
View wView;
Controller wCtrl(&wModel);
wView.setController(&wCtrl);
wView.setModel(&wModel);
wView.show();
return a.exec();
}
如果它们变得相关,我可以稍后发布Subject/Observer文件。
除了一般性的评论外,有人能回答以下问题吗:
1)将按钮信号直接连接到控制器插槽是否更好(如在View::setController
中注释掉的部分)?控制器需要知道哪个视图被调用,以便可以使用视图的正确信息,不是吗?这意味着要么:
a) 重新实现QSignalMapper 或者
b) 升级到Qt5和VS2012,以便使用lambda(C++11)直接连接;
2)更新时知道发生了什么变化的最佳方法是什么?是开关/循环遍历所有可能性,预定义映射……?
3)此外,我应该通过更新函数传递必要的信息,还是让View在收到通知后检查Model所需的值?
在第二种情况下,视图需要访问模型数据……
编辑:
特别是在数据修改非常频繁的情况下。例如,有一个加载按钮和一个整个对象/数组被修改了。通过信号/槽机制将副本传递给视图会耗费时间。
来自ddriver的回答
现在,如果您拥有传统的“项目列表”模型,并且您的视图是列表/树/表格,则情况将不同,但您的情况是单个表单之一。
4) 视图是否需要引用模型?因为它只与一个控制器交互?(View::setModel())
如果不需要,它如何将自己注册为模型的观察者?