responsabilità:MVC e Oggetto Observer modello in C++ e QT
Mentre le prime risposte hanno preso atto, utilizzando MVC nel caso esempio attuale è eccessivo. L'obiettivo della domanda è comprendere i concetti sottostanti, con un semplice esempio, per essere in grado di usarli in un programma più grande in cui vengono modificati i dati più complessi (matrici, oggetti).
Sto cercando di implementare il pattern MVC in C++ & QT, simile alla domanda qui:
Il programma ha 2 linee edita:
- mHexLineEdit
- mDecLineEdit
3 pulsanti
- mConvertToHexButton
- mConvertoDecButton
- mClearButton
e modifica solo le stringhe.
La differenza con l'altra domanda è che sto cercando di implementare il modello Soggetto/Observer per aggiornare la vista una volta che il modello è cambiato.
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.he
#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();
}
Posso postare i file Subject/Observer in un secondo momento se diventano rilevanti.
Asides da osservazioni di carattere generale, qualcuno può rispondere a queste domande:
1) sarebbe meglio collegare i segnali pulsanti per gli slot del controller direttamente (come nella parte commentata in View::setController
)? Il controller deve sapere quale vista è stata chiamata in modo che possa utilizzare le informazioni corrette dalla vista, no? Questo significherebbe uno:
a) Reimplement a QSignalMapper o
b) Upgrade to Qt5 and VS2012 in order to connect directly with lambdas (C++11);
2) Qual è il modo ottimale per sapere cosa è cambiato quando viene chiamato l'aggiornamento dal modello? È un passaggio/looping attraverso tutte le possibilità, una mappa predefinita ...?
3) Inoltre, devo passare le informazioni necessarie attraverso la funzione di aggiornamento, o lasciare che View verifichi i valori richiesti del Modello una volta che viene notificato?
Nel secondo caso la vista ha bisogno di accedere ai dati del modello ...
EDIT:
In particolare nel caso in cui v'è un sacco dei dati modificati. Ad esempio, c'è un pulsante di caricamento e un intero oggetto/array viene modificato. Passare una copia alla vista attraverso il meccanismo segnale/slot richiederebbe molto tempo.
Dalla risposta di ddriver
Ora, sarebbe una questione diversa se si dispone di un "elenco di elementi" tradizionale modello e la vostra vista è una lista/albero/tavolo, ma il tuo caso è uno di una forma singola
4) Qualora il View devono avere un riferimento al modello? dal momento che agisce solo con il controller? (Visualizza :: setModel())
In caso negativo, come si registra come osservatore del modello?
L'ultimo paragrafo - si confondono MVC con MVD di Qt - il secondo riguarda i modelli che sono elenchi di oggetti e lista/tabella/albero vi ews, con MVC il caso è un singolo modulo. – dtech