3.文档视图:从gui分割状态
为了解决一个类实现所有功能的缺陷,我们把application分为2个部分。一个部分业务逻辑,一个部分视觉渲染和交互。这2个类在学术上被称为document view 或者 model
delegate。
Document类用来处理业务逻辑,和视觉渲染、GUI事件没有一点关系。它简单的存储应用程序的状态,提供接口获得状态并根据应用程序规则改变状态。另外,提供机制告知相关对象,应用程序状态改变。
View类处理事件和图像渲染。提供对 Document类的操作,界面改变和 Document类中状态改变同步。
Document View设计模式实现状态和图像显示分离,它们能单独的改变。Document类成为 non-GUI单元,能单独的工作和检测。任何View通过notification system保持和Document内容一致,并根据Document内容 负责图像呈现和用户交互。
这种设计消除了一些对单个类实现的担忧,对状态和业务逻辑的测试变得更容易:通过调用Document类中的方法。此Document对象现在是独立的,如果需要的话,可以和不同的View工作。代价就是告知View,Document中状态改变(notification system)。
代码如下:
model.h
#include <QSet> #include <view.h> class View; class Model { public: Model(); int getClickTimes() const; void setClickTimes(int value); void incrementClickTimes(); void addListener(View *v); void removeListener(View *v); private: int m_clickTimes; QSet<View *> listeners; //set容器避免重复 void notifyListener(); }; #endif // MODEL_H
model.cpp
#include "model.h" Model::Model() { m_clickTimes = 0; notifyListener(); } int Model::getClickTimes() const { return m_clickTimes; } void Model::setClickTimes(int value) { if(m_clickTimes != value) m_clickTimes = value; notifyListener(); } void Model::incrementClickTimes() { m_clickTimes++; notifyListener(); } void Model::addListener(View *v) { listeners.insert(v); notifyListener(); } void Model::removeListener(View *v) { listeners.remove(v); } void Model::notifyListener() { foreach (View *v, listeners) { v->notify(); } }
view.h
#ifndef VIEW_H #define VIEW_H #include <QPushButton> #include <model.h> class Model; class View : public QPushButton { Q_OBJECT public: explicit View(QWidget *parent = 0); void notify(); protected: void mouseReleaseEvent(QMouseEvent *e); private: Model *model; }; #endif // VIEW_H
view.cpp
#include "view.h" View::View(QWidget *parent) : QPushButton(parent) { model = new Model(); model->addListener(this); } void View::mouseReleaseEvent(QMouseEvent *e) { model->incrementClickTimes(); } void View::notify() { setText(QString::number(model->getClickTimes())); }
main.cpp
#include <view.h> #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); View v; v.show(); return a.exec(); }
思考:
1.Model类只处理数据,View类处理事件和如何显示程序。
2.Model类中有一个容器 listeners,存放需要显示数据的类,每当应用程序状态改变时(m_clickTimes改变),告诉那些类,应用程序状态改变啦!
3.View类 mouseRelease函数处理事件,notify函数显示数据。