MVC模型简单解读

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
测试

如下为测试结果:
这里写图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值