MVC简介
MVC全称为Model View Controller(模型-视图-控制器)。M是指业务模型,V是指用户界面,C则是控制器。MVC主要用于UI的相关设计中,它用业务逻辑、数据、界面显示分离的方法组织代码。使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。比如一批统计数据可以分别用柱状图、饼图来表示。C存在的目的则是确保M和V的同步,一旦M改变,V应该同步更新。
要点
1.MVC是有数个设计模式结合起来的。
2.MVC结合了观察者模式、策略模式和组合模式。
3.模型使用观察者模式,以便观察者更新,同时保持两者之间解耦。
4.视图为模型的观察者,同一个模式可以使用不同的视图,甚至可以同时使用多个视图。
5.控制器是视图的策略,视图可以使用不同的控制器实现,得到不同的行为。
6.控制器收到视图的数据后,会经过“解读”后再发送给模型。
7.视图一般使用组合模式实现用户界面。
示意图
① 视图用于和用户交互
视图是模型的窗口,当用户对视图操作后,视图就告诉控制器用户的操作,控制器会负责处理用户的请求。
② 控制器要求模型改变状态
控制器解读用户的请求之后,便告知模型做出相应的动作。
③ 控制器也可能要求视图做改变
当控制器收到视图的某一动作后,可能需要告诉视图做出相应的改变,比如界面上的某一个按钮变得无效。
④ 当模型状态改变时,模型会通知视图
当模型的状态改变时(可能是因为用户的操作,也可能是模型自己内部状态的改变),会通知视图它的状态已经改变。
⑤ 视图向模型询问状态
视图可以直接从模型取得它的显示状态。视图做这个动作一般有两个因素:一是在模型通知视图它的状态改变;二是控制器请求视图改变。
示例
如上图所示,两个对话框是由View创建出来的。Control View 可以控制Display View中显示的内容,点击“Start”按钮后可以在Control View中输入数值,点击“Set”按钮便可以设置到Display View中。若点击“Stop”按钮后,无法再设置值到Display View中。“Increase”按钮可以让Display View中的数值加一,“Decrease”可以让Display View中的数值减一。类图如下所示:
Model:实现了可观察者接口,下面的方法允许观察者注册或者删除自己。
void registerObserver(IObserver* observer);
void removeObserver(IObserver* observer);
另开放下面的接口供Controller可以改变Model的状态:
void initialize();
void on();
void off();
void setValue(int value);
开放下面的接口供View可以获取Model的状态:
int getValue();
View:为了强调包含模型的视图的界面和包含其他用户控制的界面的差异,将界面设计成两个窗口(其实是一个类)。
View同时包含了IModel和IController的指针,但两者的作用完全不同,如下所示:
IModel* m_model; // 包含模型的指针作用:1.向模型注册观察者 2.从模型获取数据
IController* m_controller; // 控制器为视图的策略(所有用户操作交给控制器处理)
View同样开放一些接口,供Controller来改变View的状态:
void enableStartBtn();
void disableStartBtn();
void enableStopBtn();
void disableStopBtn();
View为模型的观察者,必须实现观察者接口,以便在Model状态改变时通知View:
void updateValue();
View在用户操作界面是会调用Controller如下所示的接口:
void start();
void stop();
void increaseValue();
void decreaseValue();
void setValue(int value);
控制器:Controller是策略,把它插进View中,让View变得聪明。控制器是连接View和Model的中间部件,View将用户的操作传给Controller,Controller ”解读“之后再传给Model。
Controller是通过View的构造函数传入View的。为了Controller能够改变View的状态,在Controller注册为View的策略的时候,View必须将自己的指针设置给Controller,如下所示:
View::View(IController* controller, IModel* model, QWidget *parent) : QWidget(parent)
{
this->m_controller = controller; // 控制器作为视图的策略,让视图变得聪明
m_controller->setView(this); // 把View的指针设置到控制器,用于控制器改变视图的状态
// 其他操作
}
下面列出全部代码(本示例是基于Qt5.6开发的):
IObserver.h
#ifndef IOSERVER_H
#define IOSERVER_H
// 观察者接口
class IObserver
{
public:
IObserver() {}
virtual ~IObserver() {}
virtual void updateValue() = 0;
};
#endif
Model.h
#ifndef MODEL_H
#define MODEL_H
#include "IObserver.h"
#include <QList>
// 模型接口
class IModel
{
public:
IModel() {}
virtual ~IModel() {}
virtual void initialize() = 0;
virtual void on() = 0; // 开始显示
virtual void off() = 0; // 停止显示
virtual void setValue(int value) = 0;
virtual int getValue() = 0;
virtual void registerObserver(IObserver* observer) = 0; // 注册观察者
virtual void removeObserver(IObserver* observer) = 0; // 删除观察者
virtual void notify() = 0; // 通知观察者
};
class Model : public IModel
{
private:
QList<IObserver*> m_observers;
int m_value;
public:
Model();
void initialize();
void on();
void off();
void setValue(int value);
int getValue();
void registerObserver(IObserver* observer);
void removeObserver(IObserver* observer);
void notify();
};
#endif // MODEL_H
Model.cpp
#include "Model.h"
#include "View.h"
Model::Model()
{
m_value = 0;
}
void Model::initialize()
{}
void Model::on()
{
setValue(90);
}
void Model::off()
{
setValue(0);
}
void Model::setValue(int value)
{
this->m_value = value;
notify();
}
int Model::getValue()
{
return m_value;
}
void Model::registerObserver(IObserver* observer)
{
m_observers.append(observer);
}
void Model::removeObserver(IObserver* observer)
{
m_observers.removeOne(observer);
}
void Model::notify()
{
for (int i=0; i<m_observers.size(); i++) {
m_observers[i]->updateValue();
}
}
View.h
#ifndef VIEW_H
#define VIEW_H
#include "IObserver.h"
#include <QWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QDialog>
#include <QLabel>
class IModel;
class IController;
class View : public QWidget, public IObserver
{
Q_OBJECT
private:
IModel* m_model; // 包含模型的指针作用:1.向模型注册观察者 2.从模型获取数据
IController* m_controller; // 控制器为视图的策略(所有用户操作交给控制器处理)
private:
QLineEdit* inputEdit;
QPushButton* startBtn;
QPushButton* stopBtn;
QDialog* displayDialog;
QLabel* displayLabel;
void constructUI();
protected:
void closeEvent(QCloseEvent *event);
public:
View(IController* controller, IModel* model, QWidget *parent = 0);
~View();
void enableStartBtn();
void disableStartBtn();
void enableStopBtn();
void disableStopBtn();
void updateValue();
protected slots:
void onSetBtn();
void onStartBtn();
void onStopBtn();
void onIncreaseBtn();
void onDecreaseBtn();
};
#endif // VIEW_H
View.cpp
#include "View.h"
#include "Model.h"
#include "Controller.h"
#include <QGridLayout>
View::View(IController* controller, IModel* model, QWidget *parent)
: QWidget(parent)
{
this->m_controller = controller; // 控制器作为视图的策略,让视图变得聪明
this->m_model = model;
m_model->registerObserver(this); // 向模型注册自己为观察者
m_controller->setView(this); // 把View的指针设置到控制器,用于控制器改变视图的状态
constructUI();
disableStopBtn();
enableStartBtn();
}
View::~View()
{
}
void View::updateValue()
{
int value = m_model->getValue();
if (0 == value) {
displayLabel->setText("Offline");
} else {
displayLabel->setText("Current value: " + QString::number(value));
}
}
void View::constructUI()
{
this->resize(300, 200);
inputEdit = new QLineEdit();
QPushButton* setBtn = new QPushButton();
setBtn->setText("Set");
connect(setBtn, SIGNAL(clicked()), this, SLOT(onSetBtn()));
startBtn = new QPushButton();
startBtn->setText("Start");
connect(startBtn, SIGNAL(clicked()), this, SLOT(onStartBtn()));
stopBtn = new QPushButton();
stopBtn->setText("Stop");
connect(stopBtn, SIGNAL(clicked()), this, SLOT(onStopBtn()));
QPushButton* increaseBtn = new QPushButton();
increaseBtn->setText("Increase");
connect(increaseBtn, SIGNAL(clicked()), this, SLOT(onIncreaseBtn()));
QPushButton* decreaseBtn = new QPushButton();
decreaseBtn->setText("Decrease");
connect(decreaseBtn, SIGNAL(clicked()), this, SLOT(onDecreaseBtn()));
QGridLayout* mainLayout = new QGridLayout(this);
mainLayout->addWidget(inputEdit, 0, 0, 1, 3);
mainLayout->addWidget(setBtn, 0, 3);
mainLayout->addWidget(startBtn, 1, 0);
mainLayout->addWidget(stopBtn, 1, 1);
mainLayout->addWidget(increaseBtn, 1, 2);
mainLayout->addWidget(decreaseBtn, 1, 3);
this->setWindowTitle("Control View");
this->resize(350, 70);
this->show();
displayDialog = new QDialog();
displayLabel = new QLabel(displayDialog);
displayDialog->setWindowTitle("Display View");
displayDialog->resize(200, 50);
displayLabel->resize(150, 20);
displayLabel->move(10, 10);
displayDialog->show();
}
void View::enableStartBtn()
{
startBtn->setEnabled(true);
}
void View::disableStartBtn()
{
startBtn->setEnabled(false);
}
void View::enableStopBtn()
{
stopBtn->setEnabled(true);
}
void View::disableStopBtn()
{
stopBtn->setEnabled(false);
}
void View::onSetBtn()
{
QString string = inputEdit->text();
if (string.isEmpty()) {
return;
}
int value = string.toInt();
m_controller->setValue(value);
}
void View::onStartBtn()
{
m_controller->start();
}
void View::onStopBtn()
{
m_controller->stop();
}
void View::onIncreaseBtn()
{
m_controller->increaseValue();
}
void View::onDecreaseBtn()
{
m_controller->decreaseValue();
}
void View::closeEvent(QCloseEvent *)
{
displayDialog->close();
delete displayDialog;
}
Controller.h
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include "Model.h"
#include "View.h"
class IController
{
public:
IController() {}
virtual ~IController() {}
virtual void start() = 0;
virtual void stop() = 0;
virtual void increaseValue() = 0;
virtual void decreaseValue() = 0;
virtual void setValue(int value) = 0;
virtual void setView(View* view) = 0;
};
class Controller : public IController
{
private:
IModel* m_model;
View* m_view;
public:
Controller(IModel* model);
void start();
void stop();
void increaseValue();
void decreaseValue();
void setValue(int value);
void setView(View* view);
};
#endif // CONTROLLER_H
Controller.cpp
#include "Controller.h"
Controller::Controller(IModel *model)
{
this->m_model = model;
}
void Controller::start()
{
m_model->on();
m_view->disableStartBtn();
m_view->enableStopBtn();
}
void Controller::stop()
{
m_model->off();
m_view->enableStartBtn();
m_view->disableStopBtn();
}
void Controller::increaseValue()
{
int value = m_model->getValue();
m_model->setValue(value+1);
}
void Controller::decreaseValue()
{
int value = m_model->getValue();
m_model->setValue(value-1);
}
void Controller::setValue(int value)
{
m_model->setValue(value);
}
void Controller::setView(View* view)
{
m_view = view;
}
main.cpp
#include "View.h"
#include "Model.h"
#include "Controller.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
IModel* model = new Model();
IController* controller = new Controller(model);
View* view = new View(controller, model);
return a.exec();
}
MVC.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = MVC
TEMPLATE = app
SOURCES += main.cpp\
View.cpp \
Controller.cpp \
Model.cpp
HEADERS += View.h \
Controller.h \
Model.h \
IObserver.h
测试
如下为测试结果: